数据应用OneID:ID-Mapping Spark GraphX实现

前言

说明

以用户实体为例,ID 类型包含 user_id 和 device_id。当然还有其他类型id。不同id可以获取到的阶段、生命周期均不相同。

device_id 生命周期通常指的是一个设备从首次被识别到不再活跃的整个时间段。

user_id是用户登录之后系统分配的唯一标识,即使不同的设备只要user_id相同就会识别为一个用户,但 user_id 只能在登录后获取到,所以会损失用户登录前的行为数据。

单体应用单独使用user_id或者device_id都不能完整地表达一个用户,多应用多类id又有差异性。如果可以将不同 ID 进行关联映射,最终通过唯一的 ID 标识用户。所以需要一个解决方案来映射。

用户渠道

  • 手机、平板电脑
  • 安卓手机、ios手机
  • 有PC、APP和小程序

标识情况

(1)cookieid:PC站存在用户cookies中的ID,会被清理电脑时重生成。

(2)unionid:微信提供的唯一身份认证。

(3)mac:手机网卡物理地址。

(4)imei(入网许可证序号):安卓系统可取到。

(5)imsi(手机SIM卡序号):安卓系统可取到。

(6)androidid :安卓系统id。

(7)openid (app自己生成的序号) :卸载重装app就会变更。

(8)idfa(广告跟踪码):用户可重置。

(9)deviceid(app日志采集埋点开发人员自己定义一种逻辑id,可能取自 android,imei,openudid等):逻辑上的id。

还有其他不同应用设定标识用户的ID. . . . . .

设备与登录用户分析

1. device_id 作为唯一

场景

适用登录率比较低的应用。

缺点
  • 不同用户登录一个设备,会识别为一个用户。
  • 同一个用户使用不同设备,会识别为多个用户。

2. 一个device_id关联一个user_id

场景

同一个设备登陆前(device_id) 和登录后(user_id) 可以绑定。

缺点
  • 一个未被绑定的设备登录前的用户和登录后的用户不同,这个时候会被错误地识别为同一个用户。
  • 一个被绑定的设备后续被其他用户在未登录状态下使用,也会被错误地识别为之前被绑定的用户。
  • 一个被绑定了的用户使用其他设备时,未登录状态下的数据不会标识为该用户数据。

3. 多个device_id关联一个user_id

场景

只要登录后的 user_id 相同,其多个设备上登录前后的数据都可以连通起来。

缺点

一个 device_id只能绑定到一个用户,当其他用户使用同一个已被绑定的设备时,其登录前数据还是会被识别成已绑定到该设备的用户。

4. 多个应用间的不同ID进行关联

场景

当存在多个应用,实现应用间 ID 映射和数据相通时。比如,通过手机号,邮箱号,微信号等等可以统一为一个 ID。

缺点

复杂性高。

5. 行业内方案

网易ID-Mapping

网易产品线:网易云音乐,邮箱,新闻,严选等等,不同的应用有不同的ID,比如:phone,email,yanxuan_id,music_id 等等

思路与方案
  • 结合各种应用账号,各种设备型号之间的关系,以及设备使用规律,比如时间和频次。
  • 采用规则过滤 和 数据挖掘,判断账号是否属于同一个人。
存在问题和方案
  • 用户有多个设备信息:使用一定时间 和 频次才进行关联。
  • 设备以后从来不用:设定设备未使用衰减函数。

6. 其他

美团采用手机号、微信、微博、美团账号的登录方式;大众点评采用的手机号、微信、QQ、微博的登录方式;其交集为手机号、微信、微博。最终,对于注册用户账户体系,美团采用了手机号作为用户的唯一标识。

图计算

图计算的核心思想:将数据表达成“点”,点和点之间可以通过某种业务含义建立“边”。然后,我们就可以从点、边上找出各种类型的数据关系。

在GraphX中,图由顶点(Vertices)和边(Edges)组成:

  • 顶点(Vertices):图中的点,代表实体,例如人、商品或事件。
  • 边(Edges):连接两个顶点的线,代表实体之间的关系,例如朋友关系、购买行为或网络连接。
  • 边的属性(Edge Attributes):边的附加信息,可以是权重、成本或其他相关数据。
  • 顶点的属性(Vertex Attributes):顶点的附加信息,可以是标签、状态或其他相关数据。

首先通过一个案例先认识下图计算。

案例:朋友关系的连通性

首先,需要将这些数据转换为Vertex和Edge对象

假设有以下数据:user_id: A, friend_id: B
user_id: B, friend_id: C
user_id: C, friend_id: D
user_id: D, friend_id: E
user_id: E, friend_id: F
user_id: F, friend_id: G
user_id: G, friend_id: H
user_id: H, friend_id: I
user_id: I, friend_id: J
import org.apache.spark._
import org.apache.spark.graphx._val conf = new SparkConf().setAppName("Graph Example").setMaster("local[*]")
val sc = new SparkContext(conf)// 将原始数据转换为Vertex和Edge对象
val vertices: RDD[(VertexId, String)] = sc.parallelize(Seq((1L, "A"), (2L, "B"), (3L, "C"), (4L, "D"), (5L, "E"),(6L, "F"), (7L, "G"), (8L, "H"), (9L, "I"), (10L, "J"))
)val edges: RDD[Edge[String]] = sc.parallelize(Seq(Edge(1L, 2L,"friend"), Edge(2L, 3L,"friend"), Edge(3L, 4L,"friend"),Edge(4L, 5L,"friend"), Edge(5L, 6L,"friend"), Edge(6L, 7L,"friend"),Edge(7L, 8L,"friend"), Edge(8L, 9L,"friend"),Edge(9L, 10L,"friend"), Edge(10L, 1L,"friend")
))// 创建图
val graph: Graph[String,String] = Graph(vertices, edges)
// triplets同时存储了边属性和对应顶点信息
graph.triplets.foreach(println)((4,D),(5,E),friend)
((5,E),(6,F),friend)
((9,I),(10,J),friend)
((10,J),(1,A),friend)
......

// 连通性:可以将每个顶点都关联到连通图里的最小顶点
val value = graph.connectedComponents()
value.vertices.map(tp => (tp._2, tp._1)).groupByKey().collect().foreach(println)结果:(1,CompactBuffer(8, 1, 9, 10, 2, 3, 4, 5, 6, 7))如果修改:Edge(5L, 1L,"friend") Edge(10L, 5L,"friend")val edges: RDD[Edge[String]] = sc.parallelize(Seq(Edge(1L, 2L,"friend"), Edge(2L, 3L,"friend"), Edge(3L, 4L,"friend"),Edge(4L, 5L,"friend"), Edge(5L, 1L,"friend"), Edge(6L, 7L,"friend"),Edge(7L, 8L,"friend"), Edge(8L, 9L,"friend"),Edge(9L, 10L,"friend"), Edge(10L, 5L,"friend")
))结果:
(1,CompactBuffer(1, 2, 3, 4))
(5,CompactBuffer(8, 9, 10, 5, 6, 7))

ID-Mapping 简单实现

val conf = new SparkConf().setAppName("Graph Example").setMaster("local[*]")
val sc = new SparkContext(conf)
// 假设我们有三个数据集
val userMappingData = sc.parallelize(Seq((11L,111L), //  phone,device_id(22L,222L)
))val userInfoData = sc.parallelize(Seq((11L, 1111L), // phone,open_id,这里把phone当作user_id(22L, 2222L)
))val userLoginData = sc.parallelize(Seq((1111L, 11111L, 111111L), // open_id,idfa,idfy(2222L, 22222L, 222222L)
))// 为每个数据集创建顶点RDD
//    val userVertices = userMappingData.flatMap(item =>{
//      for (element <- item.productIterator)
//        yield (element,element)
//    })val phoneVertices = userMappingData.map { case (phone, _) => (phone, "phone") }
val deviceVertices = userMappingData.map { case (_, deviceId) => (deviceId, "deviceId") }val userPhoneVertices = userInfoData.map { case (phone,_) => (phone, "phone") }
val openidVertices = userInfoData.map { case (_, openId) => (openId, "openId") }val idfaVertices = userLoginData.flatMap { case (openId, idfa, _) => Seq((openId, "openid"), (idfa, "idfa")) }
val idfvVertices = userLoginData.flatMap { case (openId, _, idfv) => Seq((openId, "openid"), (idfv, "idfv")) }// 合并所有顶点RDD
val allVertices = phoneVertices.union(deviceVertices).union(userPhoneVertices).union(openidVertices).union(idfaVertices).union(idfvVertices)// 创建边RDD
val mappingEdges = userMappingData.map { case (phone, deviceId) => Edge(phone, deviceId, "maps_to") }
val infoEdges = userInfoData.map { case (phone, openid) => Edge(phone, openid, "linked_to") }
val loginEdges = userLoginData.flatMap { case (openid, idfa, idfv) =>Seq(Edge(openid, idfa, "logins_with"), Edge(openid, idfv, "logins_with"))
}// 合并所有边RDD
val allEdges = mappingEdges.union(infoEdges).union(loginEdges)val graph = Graph(allVertices, allEdges)

graph.triplets.map(item=> "点 and 边:"+item).foreach(println)点 and 边:((22,phone),(222,deviceId),maps_to)
点 and 边:((11,phone),(111,deviceId),maps_to)
点 and 边:((11,phone),(1111,openId),linked_to)
点 and 边:((22,phone),(2222,openId),linked_to)
点 and 边:((1111,openId),(11111,idfa),logins_with)
点 and 边:((1111,openId),(111111,idfv),logins_with)
点 and 边:((2222,openId),(22222,idfa),logins_with)
点 and 边:((2222,openId),(222222,idfv),logins_with)
val value = graph.connectedComponents()
value.vertices.map(tp => (tp._2, tp._1)).groupByKey().collect().foreach(println)(11,CompactBuffer(1111, 11, 111, 11111, 111111))
(22,CompactBuffer(2222, 22, 222, 222222, 22222))

说明

真实的数据可能不会都是Long型,需要你特殊处理计算,计算出结果再转换为明文。案例中,数据连通性后,可以生成统一ID。

上面只是简单案例,在最上面分析过会出现不同的情况,更复杂的需要更复杂的逻辑处理。

除了图计算,直接SQL JOIN也即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/308415.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式学习52-ARM1

知识零散&#xff1a; 1.flash&#xff1a; nor flash 可被寻地址 …

【智能优化算法】河马优化算法(Hippopotamus optimization algorithm,HO)

河马优化算法&#xff08;Hippopotamus optimization algorithm&#xff0c;HO&#xff09;是发表在中科院二区期刊“Scientific Reports”的文章“Hippopotamus Optimization Algorithm: a Novel Nature-Inspired Optimization Algorithm”上的算法。 01.引言 河马优化算法&a…

LeetCode 1 in Python. Two Sum (两数之和)

两数之和算法思想很简单&#xff0c;即找到nums[i]和nums[j]target-(nums[i])返回[I, j ]即可。问题在于&#xff0c;简单的两层遍历循环时间复杂度为O()&#xff0c;而通过构建一个hash表就可将时间复杂度降至O(n)。本文给出两种方法的代码实现。 示例&#xff1a; 图1 两数之…

【noVNC】使用noVNC实现浏览器网页访问vnc(基于web的远程桌面)

1.VNC本身提供的http连接方式&#xff0c;可传输文件&#xff0c;画面有卡顿&#xff0c;需要安装jre 2.noVNC访问方式&#xff0c;不可传输文件&#xff0c;画面较为流畅&#xff0c;不用安装插件运行环境 一、noVNC 是什么 Web 端的Vnc软件&#xff0c;通过noVNC&#xff0…

[C++]让C++的opencv库支持写出h264格式视频

当我们写下面测试代码时候&#xff1a; #include <opencv2/opencv.hpp>int main() {cv::VideoCapture cap("E:\\car.mp4"); // 打开默认摄像头if (!cap.isOpened()) {std::cout << "读取完毕!" << std::endl;return -1;}double fps ca…

基于FPGA的以太网相关文章导航

首先需要了解以太网的一些接口协议标准&#xff0c;常见的MII、GMII、RGMII时序&#xff0c;便于后续开发。 【必读】从MII到RGMII&#xff0c;一文了解以太网PHY芯片不同传输接口信号时序&#xff01; 介绍一款比较老的以太网PHY芯片88E1518&#xff0c;具有RGMII接口&#xf…

【鸿蒙开发】第二十章 Camera相机服务

1 简介 开发者通过调用Camera Kit(相机服务)提供的接口可以开发相机应用&#xff0c;应用通过访问和操作相机硬件&#xff0c;实现基础操作&#xff0c;如预览、拍照和录像&#xff1b;还可以通过接口组合完成更多操作&#xff0c;如控制闪光灯和曝光时间、对焦或调焦等。 2 …

【C语言基础】:编译和链接(计算机中的翻译官)

文章目录 一、翻译环境和运行环境1. 翻译环境1.1 编译1.1.1 预处理1.1.2 编译1.1.3 汇编 1.2 链接 2. 运行环境 一、翻译环境和运行环境 我们在Visual Studio上写的C语言代码其实都是一些文本信息&#xff0c;计算机是不能够直接执行他们的&#xff0c;计算机只能够执行二进制…

web自动化测试系列-selenium xpath定位方法详解(六)

1.xpath介绍 XPath 是一门在 XML 文档中查找信息的语言。XPath 用于在 XML 文档中通过元素和属性进行导航。而html中也应用了这种语言 &#xff0c;所以 &#xff0c;我们定位html页面元素时也会用到xpath这种方法 。 2.xpath定位方式 xpath主要通过以下四种方法定位 &#…

Windows系统本地部署Jupyter Notebook并实现公网访问编辑笔记

文章目录 1.前言2.Jupyter Notebook的安装2.1 Jupyter Notebook下载安装2.2 Jupyter Notebook的配置2.3 Cpolar下载安装 3.Cpolar端口设置3.1 Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 在数据分析工作中&#xff0c;使用最多的无疑就是各种函数、图表、…

基于深度学习的智能停车场车牌识别计费系统(完整程序+训练数据集+开题报告+论文))

摘要 本篇论文研究的是基于车牌识别技术的智能停车场管理系统&#xff0c;采用基于深度学习的车牌识别算法&#xff0c;通过卷积神经网络对车牌图像进行处理和分析&#xff0c;实现车牌字符的识别和车牌信息的提取。同时&#xff0c;本文还设计了一个智能停车场管理系统&#x…

RTSP/Onvif视频安防监控平台EasyNVR调用接口返回匿名用户名和密码的原因排查

视频安防监控平台EasyNVR可支持设备通过RTSP/Onvif协议接入&#xff0c;并能对接入的视频流进行处理与多端分发&#xff0c;包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等多种格式。平台拓展性强、支持二次开发与集成&#xff0c;可应用在景区、校园、水利、社区、工地等场…

[opencv]VideoWriter写出fourcc格式

fourcc支持的格式 fourcc全名Four-Character Codes&#xff0c;四字符代码&#xff0c;该编码由四个字符组成 cv2.VideoWriter_fourcc(O,O,O,O) cv2.VideoWriter_fourcc(*OOOO) 通常写法有上述两种形式&#xff0c;O代表一个字符&#xff0c;通常有 支持avi格式的有&#…

react17+18 中 setState是同步还是异步更新

在类组件中使用setState&#xff0c;在函数式组件中使用hooks的useState。 setstate目录 1. 类组件1.1 react 17版本1.2 react 18版本 2、函数式组件 1. 类组件 1.1 react 17版本 参考内容&#xff1a;第十一篇&#xff1a;setState 到底是同步的&#xff0c;还是异步的&…

Java 集合Collection

集合的体系 Collection的结构体系 List系列集合&#xff1a;添加的元素是有序的、可重复、有索引。Set系列集合&#xff1a;无序、不重复、无索引 HashSet&#xff1a;无序、不重复、无索引LinkedHashSet:有序、不重复、无索引TreeSet&#xff1a;按照大小默认升序排序、不重复…

数据结构--单链表

一.单链表的设计 1.单链表的结构定义: typedef struct Node{ int data;//数据域 struct Node* next;//后继指针 }Node,*List; 2.单链表的设计示意图: 3.注意: 单链表的最后一个节点的next域为NULL; 4.为什么要有一个头节点? 简单方便,不用传二级指针; 二.单链表的实现 …

韩顺平 | 零基础快速学Python(9~11) 排序查找

排序和查找 排序 排序&#xff1a;将多个数据按顺序排列。有冒泡、选择、插入、希尔、归并、快速、堆、计数、捅、基数排序。 冒泡排序 冒泡排序 Bubble Sorting&#xff1a;重复地走访需要排序地元素列表&#xff0c;依次比较相邻的元素&#xff0c;如果顺序错误就交换位置…

SSH穿透ECS访问内网RDS数据库

处于安全考虑&#xff0c;RDS一般只会允许指定的IP进行访问&#xff0c;而我们开发环境的IP往往是动态的&#xff0c;每次IP变动都需要去修改RDS的白名单&#xff0c;为我们的工作带来很大的不便。 那么如何去解决这个问题&#xff1f; 假如我们有一台ESC服务器&#xff0c;E…

简介:基于Web的产品3D

基于 Web 的产品 3D 通过可视化界面获得各种选项来个性化他们的产品&#xff0c;例如颜色、材料、尺寸、文字、徽标、零件等。 在过去几年中&#xff0c;随着 3D 建模和渲染软件的出现&#xff0c;3D 渲染现在更常用于营销和促销目的。设计师、制造商和营销人员使用 3D 产品渲…

【Kafka】Kafka 架构深入

Kafka 工作流程及文件存储机制 Kafka 中消息是以 topic 进行分类的&#xff0c;生产者生产消息&#xff0c;消费者消费消息&#xff0c;都是面向 topic 的。 topic 是逻辑上的概念&#xff0c;而 partition 是物理上的概念&#xff0c;每个 partition 对应于一个 log 文件&am…