1. 背景

在构建精准用户画像时,面临着这样一个问题:日志采集不能成功地收集用户的所有ID,且每条业务线有各自定义的UID用来标识用户,从而造成了用户ID的零碎化。因此,为了做用户标签的整合,用户ID之间的强打通(亦称为ID-Mapping)成了迫切的需求。大概三年前,在知乎上有这样一个与之相类似的问题:如何用MR实现并查集以对海量数据pair做聚合;目前为止还无人解答。本文将提供一个可能的基于MR计算框架的解决方案,以实现大数据下的ID强打通。

首先,简要地介绍下Android设备常见的ID:

  • IMEI(International Mobile Equipment Identity),即通常所说的手机序列号、手机“串号”,用于在移动电话网络中识别每一部独立的手机等行动通讯装置;序列号共有15位数字,前6位(TAC)是型号核准号码,代表手机类型。接着2位(FAC)是最后装配号,代表产地。后6位(SNR)是串号,代表生产顺序号。最后1位(SP)一般为0,是检验码,备用。
  • MAC(Media Access Control)一般代指MAC位址,为网卡的标识,用来定义网络设备的位置。
  • IMSI(International Mobile SubscriberIdentification Number),储存在SIM卡中,可用于区别移动用户的有效信息;其总长度不超过15位,同样使用0~9的数字。其中MCC是移动用户所属国家代号,占3位数字,中国的MCC规定为460;MNC是移动网号码,最多由两位数字组成,用于识别移动用户所归属的移动通信网;MSIN是移动用户识别码,用以识别某一移动通信网中的移动用户。
  • Android ID是系统随机生成的设备ID 为一串64位的编码(十六进制的字符串),通过它可以知道设备的寿命(在设备恢复出厂设置或刷机后,该值可能会改变)。
  • IDFA (Identifier for Advertisers) 是苹果推出来的用于广告标识的设备ID,同一设备上的不同APP所获取的IDFA是一致的;但是用户可以自主更改IDFA,所以IDFA并不是和设备一一绑定的。

2. 设计

从图论的角度出发,ID强打通更像是将小连通图合并成一个大连通图;比如,在日志中出现如下三条记录,分别表示三个ID集合(小连通图):

  1. A B C
  2. C D
  3. D E

通过将三个小连通图合并,便可得到一个大连通图——完整的ID集合列表A B C D E淘宝明风介绍了如何用Spark GraphX通过outerJoinVertices等运算符来做大数据下的多图合并;针对ID强打通的场景,也可采用类似的思路:日志数据构建大的稀疏图,然后采用自join的方式做打通。但是,我并没有选用GraphX,理由如下:

  • GraphX只支持有向图,而不支持无向图,而ID之间的关联关系是一个无向连通图;
  • GraphX的join操作不完全可控,“不完全可控”是指在做图合并时我们需要做过滤山寨设备、一对多的ID等操作,而在GraphX封装好的join算子上实现过滤操作则成本过高。

因而,基于MR计算模型(Spark框架)我设计新的ID打通算法;算法流程如下:打通的map阶段将ID集合id_set中每一个Id做key然后进行打散(id_set.map(id -> id_set))),Reduce阶段按key做id_set的合并。通过观察发现:仅需要两步MR便可完成上述打通的操作。以上面的例子做说明,第一步MR完成后,打通ID集合为:A B C DC D E,第二步MR完成后便得到完整的ID集合列表A B C D E。但是,在两步MR过程中,所有的key都会对应一个聚合结果,而其中一些聚合结果只是中间结果。故而引入了key_set用于保存聚合时的key值,加入了第三步MR,通过比较key_setid_set来对中间聚合结果进行过滤。算法的伪代码如下:

  1. MR step1:
  2. Map:
  3. input: id_set
  4. process: flatMap id_set;
  5. output: id -> (id_set, 1)
  6. Rduce:
  7. process: reduceByKey
  8. output: id -> (id_set, empty key_set, int_value)
  9. MR step2:
  10. Map:
  11. input: id -> (id_set, empty key_set, int_value)
  12. process: flatMap id_set, if have id_aggregation, then add key to key_set
  13. output: id -> (id_set, key_set, int_value)
  14. Reduce:
  15. process: reduceByKey
  16. output: id -> (id_set, key_set, int_value)
  17. MR step3:
  18. Map:
  19. input: id -> (id_set, empty key_set, int_value)
  20. process: flatMap id_set, if have id_aggregation, then add key to key_set
  21. output: id -> (id_set, key_set, int_value)
  22. Reduce:
  23. process: reduceByKey
  24. output: id -> (id_set, key_set, int_value)
  25. Filters:
  26. process: if have id_aggregation, then add key to key_set
  27. filter: if no id_aggregation or key_set == id_set
  28. distinct

3. 实现

针对上述ID强打通算法,Spark实现代码如下:

  1. case class DvcId(id: String, value: String)
  2. val log: RDD[mutable.Set[DvcId]]
  3. // MR1
  4. val rdd1: RDD[(DvcId, (mutable.Set[DvcId], mutable.Set[DvcId], Int))] = log
  5. .flatMap { set =>
  6. set.map(t => (t, (set, 1)))
  7. }.reduceByKey { (t1, t2) =>
  8. t1._1 ++= t2._1
  9. val added = t1._2 + t2._2
  10. (t1._1, added)
  11. }.map { t =>
  12. (t._1, (t._2._1, mutable.Set.empty[DvcId], t._2._2))
  13. }
  14. // MR2
  15. val rdd2: RDD[(DvcId, (mutable.Set[DvcId], mutable.Set[DvcId], Int))] = rdd1
  16. .flatMap(flatIdSet).reduceByKey(tuple3Add)
  17. // MR3
  18. val rdd3: RDD[(DvcId, (mutable.Set[DvcId], mutable.Set[DvcId], Int))] = rdd2
  19. .flatMap(flatIdSet).reduceByKey(tuple3Add)
  20. // filter
  21. val rdd4 = rdd3.filter { t =>
  22. t._2._2 += t._1
  23. t._2._3 == 1 || (t._2._1 -- t._2._2).isEmpty
  24. }.map(_._2._1).distinct()
  25. // flat id_set
  26. def flatIdSet(row: (DvcId, (mutable.Set[DvcId], mutable.Set[DvcId], Int))) = {
  27. row._2._3 match {
  28. case 1 =>
  29. Array((row._1, (row._2._1, row._2._2, row._2._3)))
  30. case _ =>
  31. row._2._2 += row._1 // add key to keySet
  32. row._2._1.map(d => (d, (row._2._1, row._2._2, row._2._3))).toArray
  33. }
  34. }
  35. def tuple3Add(t1: (mutable.Set[DvcId], mutable.Set[DvcId], Int),
  36. t2: (mutable.Set[DvcId], mutable.Set[DvcId], Int)) = {
  37. t1._1 ++= t2._1
  38. t1._2 ++= t2._2
  39. val added = t1._3 + t2._3
  40. (t1._1, t1._2, added)
  41. }

其中,引入常量1是为了标记该条记录是否发生了ID聚合的情况。

ID强打通算法实现起来比较简单,但是在实际的应用时,日志数据往往是带噪声的:

  • 有山寨设备;
  • ID之间存在着一对多的情况,比如,各业务线的UID的靠谱程度不一,有的UID会对应到多个设备。

另外,ID强打通后是HDFS的离线数据,为了提供线上服务、保证ID之间的一一对应关系,应选择何种分布式数据库、表应如何设计、如何做到数据更新时而不影响线上服务等等,则是另一个需要思考的问题。

一点做用户画像的人生经验(一):ID强打通的更多相关文章

  1. 一点做用户画像的人生经验:ID强打通

    1. 背景 在构建精准用户画像时,面临着这样一个问题:日志采集不能成功地收集用户的所有ID,且每条业务线有各自定义的UID用来标识用户,从而造成了用户ID的零碎化.因此,为了做用户标签的整合,用户ID ...

  2. 【转】4w+1h 教你如何做用户画像

    记得14年开始做用户画像的时候,对于用户画像完全没有概念,以为是要画一幅幅图画,经过两年多的学习和理解,渐渐的总结出了一些方法和技巧,在这里就通过4个W英文字母开头和1个H英文字母开头的单词和大家分享 ...

  3. 【原】浅谈KL散度(相对熵)在用户画像中的应用

    最近做用户画像,用到了KL散度,发现效果还是不错的,现跟大家分享一下,为了文章的易读性,不具体讲公式的计算,主要讲应用,不过公式也不复杂,具体可以看链接. 首先先介绍一下KL散度是啥.KL散度全称Ku ...

  4. 大数据时代下的用户洞察:用户画像建立(ppt版)

    大数据是物理世界在网络世界的映射,是一场人类空前的网络画像运动.网络世界与物理世界不是孤立的,网络世界是物理世界层次的反映.数据是无缝连接网络世界与物理世界的DNA.发现数据DNA.重组数据DNA是人 ...

  5. 用Mirror,搞定用户画像

    Mirror产品概述 Mirror是专为金融行业设计的全面用户画像管理系统.该系统基于星环多年来为多个金融企业客户构建用户画像的经验,深入契合业务需求,实现对用户全方位全维度的刻画.Mirror内置银 ...

  6. doubleclick cookie、动态脚本、用户画像、用户行为分析和海量数据存取 推荐词 京东 电商 信息上传 黑洞 https://blackhole.m.jd.com/getinfo

    doubleclick cookie https://mp.weixin.qq.com/s/vZUj-Z9FGSSWXOodGqbYkA 揭密Google的网络广告技术:基于互联网大数据视角 原创:  ...

  7. 个推用户画像产品(个像)iOS集成实践

    最近业务方给我们部门提了新的需求,希望能构建精准用户画像.我们尝试使用的是个推(之前专门做消息推送的公司)旗下新推出的产品“个像·用户画像”.根据官方的说法,个像能够为APP开发者提供丰富的用户画像数 ...

  8. 用户画像,知乎Live总结

    ttps://www.zhihu.com/lives/889189116527403008/messages 用户画像两层含义:单个标签:用户的分布 标签体系要与时俱进,如果标签被下游强依赖,则不轻易 ...

  9. 用户画像 销量预测 微观 宏观 bi

    w 目前我们没有自己的平台 第三方平台又不会给任何我们想要的数据   没有用户的注册信息 全天候的行为信息   用户画像没法做    针对我们业务的bi做的思路是什么呢   数据中心怎么做销量预测呢 ...

随机推荐

  1. linux基础学习笔记

    我用的是centOS7.0版本的系统.linux的shell终端窗口类似于wind的command窗口 shell命令提示符格式:用户名@主机名:目录名 提示符 @前面的是已登录的用户名,@之后的为计 ...

  2. 02.SQLServer性能优化之---牛逼的OSQL----大数据导入

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 上一篇:01.SQLServer性能优化之----强大的文件组----分盘存储 http ...

  3. Tesseract-OCR字符识别简介

    OCR(Optical Character Recognition):光学字符识别,是指对图片文件中的文字进行分析识别,获取的过程.Tesseract:开源的OCR识别引擎,初期Tesseract引擎 ...

  4. c# 基础 object ,new操作符,类型转换

    参考页面: http://www.yuanjiaocheng.net/webapi/config-webapi.html http://www.yuanjiaocheng.net/webapi/web ...

  5. css样式之超出隐藏

    文本超出部分隐藏,总结两种方法. 1.单行隐藏 html代码 <div class="mi">当文字超过范围的时候,超出部分会隐藏起来.</div> css ...

  6. 转:ORA-15186: ASMLIB error function = [asm_open], error = [1], 2009-05-24 13:57:38

    转:ORA-15186: ASMLIB error function = [asm_open], error = [1], 2009-05-24 13:57:38http://space.itpub. ...

  7. Oracle:一个用户操作多个表空间中表的问题(转)

    原文地址:http://blog.csdn.net/shmiloy001/article/details/6287317 首先,授权给指定用户. 一个用户的默认表空间只能有一个,但是你可以试下用下面的 ...

  8. Linux设备管理(二)_从cdev_add说起

    我在Linux字符设备驱动框架一文中已经简单的介绍了字符设备驱动的基本的编程框架,这里我们来探讨一下Linux内核(以4.8.5内核为例)是怎么管理字符设备的,即当我们获得了设备号,分配了cdev结构 ...

  9. Debian下安装mono

    从mono的官网上查,debian的步骤写得太乱了.其实总结起来,就是这么几步: apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --rec ...

  10. 【腾讯Bugly干货分享】基于 Webpack & Vue & Vue-Router 的 SPA 初体验

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d13a57132ff21c38110186 导语 最近这几年的前端圈子,由于 ...