1、项目描述
-
用户画像:也叫用户信息标签化。根据用户的信息和行为动作,用一些标签把用户描绘出来,描绘的标签就是用户画像。
-
这里我们通过手机app对用户的性别和年龄段进行推算,通过对app设置性别和年龄段的相应权重,然后结合用户每天使用app的时长,综合计算app中的性别和年龄段的比重,从而得出app在男女人群和年龄段中的受欢迎程度。
-
appID:用于标记APP,APP的唯一编号,判断终端安装的APP。
-
年龄段说明:
名称 说明 年龄段1 24岁以下 年龄段2 25-30岁 年龄段3 31-35岁 年龄段4 36-40岁 年龄段5 40岁以上
2、数据描述
这个有两个数据文件,一个是用户使用app的数据userdata文件,一个是每个app对年龄段和男女设置的权重信息appTabs文件。
- userdata文件:部分数据如下,数据以’|'为分隔符,其中,第1个字段如:‘NXxY3tn5XsuFcyzEw8qP8g==’表示加密过的手机号;第10个字段表示时间戳;第16个字段表示appID。
NXxY3tn5XsuFcyzEw8qP8g==|1|100.88.255.208|100.88.184.53|2152|2152|204409856|61650535|18320|81244939|CMNET.MNC002.MCC460.GPRS|1471017931598|23|1|33|330400|1|0|10.32.244.101||43785|0|111.20.242.48||80|724|3718|2|tb.himg.baidu.com|/sys/portraitn/item/e5047a6875313233343531322005||bdtb for Android 7.6.2|image/jpeg|||2912|0||||0|0|255|4006|40060044|500-1000|||2055|116.09607|43.93931|290||4||
5ghu6yckW5vTxRGAtMhTFg==|1|100.88.255.71|100.88.188.59|2152|2152|318639104|55579611|18322|102176409|CMNET.MNC002.MCC460.GPRS|1471017996630|153|1|02|020451|8|0|10.232.18.66||55282|0|111.56.32.73||80|1651|14092|2|p2.pstatp.com|/list/s360/9e70005465a6ee3a4e8.webp||okhttp/2.6.0|image/webp|||12384|0||||0|0|255|4002|40020157|3000-4000|||2457|116.08951|43.81833|471||1||
MpzAa6+icSF9HWozeAdLuQ==|1|100.88.255.210|100.88.191.101|2152|2152|131009536|37537304|18320|102115991|CMNET.MNC002.MCC460.GPRS|1471017994558|2225|1|||0|0|10.53.77.48||63518|0|111.56.28.120||8080|1014|614|2|111.56.28.120|/html/servicereq/listredenvelope?callback=angular.callbacks._4&csession=-2111826898&langId=zh&reqparam=%7B%22sresptime%22:%2220160813001330%22%7D&templateId=fullscreenbar||Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Mobile/11D257|application/json;charset=UTF-8|||0|0||||0|0|255|4001|40010003|3000-4000||||116.01994|43.99342|471||4||
- appTabs文件:各字段含义如下。
AppID App名称 男性权重 女性权重 年龄段1 年龄段2 年龄段3 年龄段4 年龄段5
10004|阿里旺旺|0.001|0.001|0|0.2|0.3|0.2|0.3
10005|微信|0.001|0.001|0|0.2|0.3|0.2|0.3
10006|陌陌|0.001|0.001|0|0.2|0.3|0.2|0.3
3、代码实现
代码流程如下:首先读取userdata文件中的数据,根据appTabs数据的规则,解析数据。这里我们运行了两个Job任务,job2必须等job1执行成功后,才可以执行;接着,我们将解析后的数据写入hbase中。
-
第一个阶段(job-1):map函数如下:
public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); String[] dataArray = line.split(config.Separator); String uiqkey = dataArray[Integer.parseInt(config.MDN)] + dataArray[Integer.parseInt(config.appID)]; // MDN + appID String[] val = new String[5]; String timenow = dataArray[Integer.parseInt(config.Date)]; SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); val[0] = sdf.format(Long.parseLong(timenow));//时间 val[1] = dataArray[Integer.parseInt(config.MDN)];// 手机号 val[2] = dataArray[Integer.parseInt(config.appID)];// appID val[3] = "1";// 计数 val[4] = dataArray[Integer.parseInt(config.ProcedureTime)];// 使用时长 k.set(uiqkey); context.write(k, new TextArrayWritable(val)); }
reduce函数如下:
public void reduce(Text key, Iterable<TextArrayWritable> values, Context context) throws IOException, InterruptedException { long sum = 0; int count = 0; String[] res = new String[5]; boolean flg = true; for (TextArrayWritable t : values) { String[] vals = t.toStrings(); if (flg) { res = vals; } if (vals[3] != null) { count = count + 1; } if (vals[4] != null) { sum += Long.valueOf(vals[4]); } } res[3] = String.valueOf(count); res[4] = String.valueOf(sum); StringBuffer sb = new StringBuffer(); sb.append(res[0]).append("|");// 时间 sb.append(res[1]).append("|");// 手机号 sb.append(res[2]).append("|");// appID sb.append(res[3]).append("|");// 计数 sb.append(res[4]);// 使用时长 v.set(sb.toString()); context.write(null, v); }
-
第二个阶段(job-2):map函数如下:
public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); String[] dataArray = line.split("\\|"); String newkey = dataArray[1] ; // MDN k.set(newkey); context.write(k, value); }
reduce函数如下:
Map<String, String[]> appMap = LoadHdfsTable.getAppMap(); Text v = new Text(); public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException { Map<String, UserDraw> userDrawMap = new HashMap<>(); Set<String> keySet = userDrawMap.keySet(); String keyMDN = null; for (Text t : values) { String[] dataArray = t.toString().split("\\|"); keyMDN = dataArray[1]; // 用户MDN String appID = dataArray[2]; // APPID // 根据appID获取对应的标签信息 if (appID.length() > 0) { // appID不能为空 if (appMap.get(appID) == null) { continue; } String favourite = appMap.get(appID)[2]; float male = Float.parseFloat(appMap.get(appID)[1]); float female = Float.parseFloat(appMap.get(appID)[2]); float age1 = Float.parseFloat(appMap.get(appID)[3]); float age2 = Float.parseFloat(appMap.get(appID)[4]); float age3 = Float.parseFloat(appMap.get(appID)[5]); float age4 = Float.parseFloat(appMap.get(appID)[6]); float age5 = Float.parseFloat(appMap.get(appID)[7]); long times = Long.parseLong(dataArray[4]); if (userDrawMap.containsKey(keyMDN) == true) { UserDraw userDraw = userDrawMap.get(keyMDN); // 性别权重 userDraw.protraitSex(male, female, times); // 年龄段权重 userDraw.protraitAge(age1, age2, age3, age4, age5, times); } else { userDrawMap.put(keyMDN, createDrawData(dataArray, favourite, male, female, age1, age2, age3, age4, age5, times)); } } } for (String keys : keySet) { v.set(userDrawMap.get(keys).toString()); context.write(null, v); } }
-
接着,我们将分析的数据写入hbase。
public static class UserDrawPutInHbaseMap2 extends Mapper<LongWritable, Text, Text, Text> { Text k2 = new Text(); Text v2 = new Text(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); String[] splited = line.split("\\|"); k2.set(splited[1]); v2.set(line); context.write(k2, v2); } } public static class UserDrawPutInHbaseReduce2 extends TableReducer<Text, Text, NullWritable> @Override protected void reduce(Text k2, Iterable<Text> val, Reducer<Text, Text, NullWritable, Mutation>.Context context) throws IOException, InterruptedException { for (Text v2 : val) { String[] splited = v2.toString().split("\\|"); //rowkey if (k2.toString().length() != 0) { Put put = new Put(Bytes.toBytes(k2.toString())); System.out.println("k2=" + k2.toString()); //跳过写入Hlog,提高写入速度 put.setDurability(Durability.SKIP_WAL); put.add(Bytes.toBytes("draw"), Bytes.toBytes("mdn"), Bytes.toBytes(splited[1])); put.add(Bytes.toBytes("draw"), Bytes.toBytes("male"), Bytes.toBytes(splited[2])); put.add(Bytes.toBytes("draw"), Bytes.toBytes("female"), Bytes.toBytes(splited[3])); put.add(Bytes.toBytes("draw"), Bytes.toBytes("age1"), Bytes.toBytes(splited[4])); put.add(Bytes.toBytes("draw"), Bytes.toBytes("age2"), Bytes.toBytes(splited[5])); put.add(Bytes.toBytes("draw"), Bytes.toBytes("age3"), Bytes.toBytes(splited[6])); put.add(Bytes.toBytes("draw"), Bytes.toBytes("age4"), Bytes.toBytes(splited[7])); put.add(Bytes.toBytes("draw"), Bytes.toBytes("age5"), Bytes.toBytes(splited[8])); context.write(NullWritable.get(), put); } } } }
-
源码和数据
https://github.com/fengqijie001/userPortrait希望可以帮到各位,不当之处,请多指教~?