vector_trans udf函数的开发及优化
一、背景
团体数据挖掘我的项目下,用户画像工程须要开发一些根底标签,如年龄、性别、婚姻状况、有车、有房、资产状况等。这些标签不能简略的应用团体下的各个数据作为最终评估后果,因为不同数据的置信度状况不一样。
因而咱们须要应用决策树+XGBoost等模型来综合预测用户标签。比方以下几种标签的决策树如图所示:
年龄:
年龄段:
性别:
婚姻状况:
在我本人曾经实现的决策树+算法模型预测用户标签的我的项目下,须要保障XGBoost模型输出的数据为一个不同特色数据转换而来的向量数组。下边贴一段scala代码:
//次要做的是将app_list特色数据转换成XGBoost须要的向量数组data.map(row => { val appList = row.getAs[String]("app_list") if(null == appList || appList.length == 0) { (row.getString(0), row.getString(1), row.getString(2), row.getString(3), row.getString(4), null) } else { val arr = appList.split("|") val map = new mutable.LinkedHashMap[String, Double]() ++= appMap arr.foreach(i => if (map.contains(i)) map(i) = 1d) (row.getString(0), row.getString(1), row.getString(2), row.getString(3), row.getString(4), map.values.toArray) }}).toDF("id", "finance_age", "xinan_age", "resume_age", "umc_age", "model_data")
为了缩小业务逻辑对形象工程的影响,我倡议把这段代码放到前一段ETL去做,而我这边的决策树我的项目自身只做数据集的分类计算的工作,这样能够让工程自身的扩展性大大提高。于是接下来便是怎么解决几百上千各特色在分布式环境下高效遍历的问题。我想到了自定义UDF。
二、实现
代码实现其实不难,下边举个粒子:
str1:"王者光荣|i漫游服务|qq音乐"str2:"王者光荣|爱奇艺|i漫游服务|qq音乐|seetong|天猫精灵|qq邮箱"
str1是咱们某个用户的app软件列表,str2是咱们模型须要的特色列表。
咱们想要失去的后果为:
out:Array[1.0,0.0,1.0,1.0,,0.0,0.0,0.0]
意思是在str2中某个地位如果存在,那这个地位的编码为1,否则为0。这样就把特色转换成了向量数组。
udf代码如下:
package com.bj58.fbuudf.trans;import org.apache.hadoop.hive.ql.exec.UDF;import javax.annotation.Nonnull;import java.util.ArrayList;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;/** * 遍历传入数组,将其转为指标向量数组(XGBoost模型) * 入参:【String, String, String】:待转换数组字符串,指标数组字符串,分隔符 * 例:select vector_trans("1,6", "1,2,3,4,5,6", ",") * 返回值:【Array<Double>】:指标向量数组 * 例:[1.0,0.0,0.0,0.0,0.0,1.0] * * @author yangfan * @since 2021/3/18 * @version 1.0.0 init */public class ModelVectorTransUdf extends UDF { private Map<String, Double> targetMap; public List<Double> evaluate(String str1, @Nonnull String str2, @Nonnull String regex) { if (null == str1 || str1.length() == 0) { return null; } if (null == targetMap) { targetMap = new LinkedHashMap<>(); for (String s : str2.split(regex)) { targetMap.put(s, 0d); } } Map<String, Double> map = new LinkedHashMap<String, Double>() {{ putAll(targetMap); }}; for (String s : str1.split(regex)) { if (map.containsKey(s)) { map.put(s, 1d); } } return new ArrayList<>(map.values()); } public static void main(String[] args) { List<Double> result = new ModelVectorTransUdf() .evaluate("1,6", "1,2,3,4,5,6", ","); System.out.println(result); }}
三、优化
其实优化曾经提当初udf代码中了,这里提一下。依据理论状况,参数str2其实是模型数组,同一个模型下,这个字符串是不变的。因而能够在第一次传入后将其转换成LinkedHashMap<String, Double>缓存下来,这样下次再计算时,就不必每次都去解析字符串了。至于为什么是LinkedHashMap,而不是HashMap、List构造,是因为模型向量数组中值的程序是不能扭转的,否则预测后果将不精确,而所以用LinkedHashMap而不是HashMap;前边说过咱们的特色有几百上千个,那就须要在遍历str1数组时,效率越高越好,而Map的查找时间复杂度为O(1),所以不必List构造。那就说这么多吧~