简介这份《用户画像基础》PDF聚焦互联网行业用户画像的完整知识框架适合数据产品、数据分析与运营人员系统入门。内容从画像定义与标签体系讲起覆盖统计类、规则类、机器学习挖掘类三类标签并延伸到数仓分层、Spark/Hive/HBase/MySQL/Redis/Elasticsearch等基础设施以及用户画像八大模块与项目开发上线流程。资源共1个文件类型为PDF压缩包大小约795KB便于随时翻阅。目前已有1015人学习下载可作为搭建用户画像体系、规划标签开发任务和开展精准运营的参考手册尤其适合需要理解数据如何从仓库走向业务应用的学习者。1. 用户画像基础是什么先解决“标签打架”问题做过三年以上的数仓或推荐工程师多半见过这种场景运营要“高价值用户”数据团队从订单侧给出高消费用户从访问侧又给出高频活跃用户两批标签词面相近人群重合率却不到六成。问题不出在算法而出在“用户画像基础”没打牢。用户画像基础不是拉一张宽表、堆几十个特征而是把用户标识、标签口径、计算时效、访问性能串起来的一套规范。本文沿着这个标题从数据底座、标签体系、批量计算到线上存储讲清一套可复现的画像基础方案。读者如果正在搭画像平台或者被业务疑问追问到怀疑人生本文能给你一张完整的地图。2. 用户画像基础的数据底座ID打通与数据分层在建立任何标签之前先要回答一个问题一个用户到底是谁。移动端场景下同一个用户可能用手机号登录过一次老版本App里一直以device_id匿名访问后来微信授权又生成一个新的业务ID。如果只按单一ID聚合画像宽表里会有一模一样的物理用户出现多行标签也随之分裂。2.1 先梳理用户标识u_id、device_id、cookie常见的平台内标识有登录用户ID、设备ID、Cookie ID、手机号、第三方OpenID。它们产生的场景不同可信度也不同。建议先把这些标识列成一张枚举表再决定映射关系。标识产生场景更新频率可信度典型问题userId注册/登录低频高未登录用户无此标识device_idApp首次启动固定不变中高可被清缓存或刷机识别为新人cookie_idWeb/H5访问会话级低浏览器清Cookie即丢失手机号下单/绑定极低高换号后影响连续归因openid微信/支付宝授权低频中同一人在不同开放平台id不同我的习惯是在ODS层保留原始标识不直接修改在DWD层建一张dim_user_id_mapping映射表以userId为主键把device_id、cookie_id都指向这个主键。未登录用户先以device_id作为占位主键等业务侧触发登录后再合并身份。这样既能上报匿名数据又能保证登录后画像连续。2.2 数仓分层与标签宽表设计画像基础表通常放在DWS层因为它属于轻度汇总的公共维度数据。数据链路建议是ODS统一收集业务日志 → DWD清洗成行为明细 → DWS加工用户维汇总层 → ADS面向应用提取标签。宽表不是越宽越好字段过多会降低查询效率也会让下游依赖变得脆弱。下面是一张最小可用的日级画像宽表DDL覆盖了事实指标和简单标签CREATE TABLE dws.user_profile_daily ( user_id STRING COMMENT 统一用户ID, first_seen_date STRING COMMENT 首次出现日期, last_active_date STRING COMMENT 最近活跃日期, order_cnt_30d BIGINT COMMENT 近30天下单次数, order_amt_30d DECIMAL(12,2) COMMENT 近30天下单金额, tag_value_level STRING COMMENT 价值分层标签, tag_channel_prefer STRING COMMENT 渠道偏好标签 ) PARTITIONED BY (dt STRING COMMENT 日期分区) STORED AS PARQUET;之所以用PARTITIONED BY (dt)是为了按天刷新全量快照便于下游按分区读取。存储使用Parquet一方面压缩比高另一方面按列裁剪能减少查询IO。宽表里的字段分三类用户身份字段、事实聚合字段、派生标签字段。事实字段尽量保留原始聚合值派生标签则可以随时由事实字段重算。2.3 最小可跑的ID-Mapping SQL下面这个SQL例子可以把登录日志和设备注册表合并成统一映射。它只处理两类来源但思路可以扩展INSERT OVERWRITE TABLE dwd.dim_user_id_mapping SELECT COALESCE(a.user_id, b.reg_user_id) AS user_id, COALESCE(a.device_id, b.device_id) AS device_id, CURRENT_TIMESTAMP() AS update_time FROM ( SELECT device_id, MAX(login_user_id) AS user_id FROM dwd.app_login_log WHERE dt ${bizdate} GROUP BY device_id ) a FULL OUTER JOIN ( SELECT device_id, user_id AS reg_user_id FROM dwd.app_device_register WHERE dt ${bizdate} ) b ON a.device_id b.device_id;逻辑说明子查询a从登录日志中取每个设备最近登录过的一个用户ID子查询b从设备注册表取注册用户ID。FULL OUTER JOIN保证只出现过一次访问行为的设备也能得到一行映射。COALESCE两边都适用因为JOIN的两侧都保留device_id。实际生产环境比这复杂得多还要处理一台设备多人使用、多个设备对应一个用户等情况但核心思想就是先把已知登录行为作为可信证据再逐步用规则合并。3. 用户画像基础的标签体系从维度到权重的设计ID打通之后画像基础的核心就是标签体系。很多团队的标签表里几百个字段但业务方不知道每个字段的准确含义数据团队自己也不敢改口径。要避免这种局面得从标签分类、命名、生命周期三个维度先定规矩。3.1 标签的三种类型事实、规则、模型用户画像标签可以从加工程度上分成三类。事实标签直接从行为明细聚合而来规则标签基于逻辑判断模型标签则需要训练或打分。三者各有取舍类型典型示例更新频率优点缺点事实标签最近购买时间、累计消费金额日级/小时级准确、可解释相对用户意图滞后规则标签高价值用户、沉睡用户日级口径灵活、易调整阈值依赖运营经验模型标签价格敏感度、流失概率周级/月级覆盖广、预测性强需要训练评估和模型维护我在项目里偏向把事实标签和规则标签优先做因为它们在早期就能快速产生业务价值。模型标签要等数据积累到一定量级再上否则样本太少模型输出的分数会震荡。3.2 标签命名、口径与生命周期标签命名是团队协作的基础。推荐格式为{业务域}_{标签名}_{周期}_{类型}。例如trade_order_amt_30d_fact表示交易域近30天下单金额事实标签trade_value_level_rule表示交易域价值分层规则标签。这种命名让数据字典可以自动关联也方便下游按前缀权限管控。标签口径必须沉淀成文档。我的习惯是维持一张“标签口径表”字段包含标签名、中文名、定义描述、口径SQL、负责人、更新状态。这样当业务方质疑“为什么用户A是高价值用户B不是”时能直接打开文档看到判断阈值和SQL而不是听口头解释。生命周期也要区分对待日更新标签跑T1任务月更新标签只做月末调度一次性标签必须带版本号避免下次跑数把历史人群刷掉。无论哪种都要保证万一任务失败时上一份数据不被立刻覆盖。3.3 用Python生成标签权重衰减模型类标签经常需要对用户行为做时间衰减否则三个月前一次大额购买会永远把用户顶到“高价值”。一个常用的衰减函数是半衰期指数衰减import math def decay_weight(days_ago: int, half_life: int 30) - float: 按距离今天的天数计算事件权重。 half_life30 表示30天前的事件权重为0.560天前为0.25。 return 0.5 ** (days_ago / half_life)使用示例计算某用户对“3C数码”的加权兴趣分浏览记0.3分、加购记0.6分、下单记1.0分再乘以时间衰减系数action_score { view: 0.3, cart: 0.6, order: 1.0, } user_actions [ (3, order), # 3天前下单 (12, cart), # 12天前加购 (45, view), # 45天前浏览 ] score sum( decay_weight(days_ago, half_life30) * action_score[action] for days_ago, action in user_actions ) print(f全部行为加权后的兴趣分: {score:.3f})逻辑说明decay_weight的底数取0.5意味着每个半衰期周期权重减半。half_life是核心参数调小会让衰减过快适合短期促销活动调大则保留较长历史记忆适合用户生命周期价值预测。要注意这种连续衰减对周期性行为比如每周固定购物不友好那种场景建议先做“周期内归一化”再叠加周期间衰减。4. 用户画像基础的计算与存储批量跑数与线上读取标签定义完之后就要考虑怎么算、怎么存。画像基础数据的消费者一般分两类离线报表、数据开发他们能接受T1延迟线上推荐和实时风控他们要求毫秒级拿到标签。所以计算和存储要分层设计。4.1 用Spark SQL从明细加工标签宽表假设DWD已有订单明细表dwd.fact_order_detail可以用Spark SQL生成前面那张宽表。下面是一个典型模板INSERT OVERWRITE TABLE dws.user_profile_daily PARTITION (dt ${bizdate}) SELECT t.user_id, MIN(t.order_date) AS first_seen_date, MAX(t.order_date) AS last_active_date, COUNT(IF(t.order_date date_sub(${bizdate}, 30), t.order_id, NULL)) AS order_cnt_30d, SUM(IF(t.order_date date_sub(${bizdate}, 30), t.amount, 0)) AS order_amt_30d, IF(SUM(IF(t.order_date date_sub(${bizdate}, 30), t.amount, 0)) 5000, high, normal) AS tag_value_level FROM ( SELECT user_id, order_id, amount, order_date FROM dwd.fact_order_detail WHERE dt date_sub(${bizdate}, 90) ) t GROUP BY t.user_id;逻辑说明COUNT和SUM里的IF不是过滤而是在用户维度内做条件聚合。放在WHERE里会把没有近30天订单的用户整行滤掉导致宽表缺失“沉睡用户”类标签。外层查询拿到90天明细指标只统计近30天这样既能保留所有有历史行为的用户又不会让30天外的数据污染指标。4.2 画像结果落到HBase和Redis离线宽表适合数据分析师跑SQL不适合线上服务做高并发读取。常见做法是把画像标签灌入HBaserowkey采用反转的user_id。HBase表profile_daily rowkeyreverse(user_id) -- 示例10091220 - 02219001 column familyattr qualifierorder_cnt_30d, tag_value_level, tag_channel_prefer TTL按实际业务保留30~60天rowkey反转是HBase常用技巧让前缀散列在Region上避免连续userId写爆同一个Region。对毫秒级读取要求更高的标签可以再同步一份到Redisredis-cli SET profile:10091220 {order_cnt_30d:3,tag_value_level:high} EX 86400这里的EX 86400表示缓存一天配合离线任务凌晨刷新白天服务读取Redis时不会命中过期数据。如果标签多且频繁访问建议改成Redis Hash一次性HMGET多个字段减少网络往返。4.3 调度与参数调优批处理任务通常要挂在调度平台上。如果用crontab加Spark提交一个最小化的任务形如30 2 * * * spark-submit \ --master yarn \ --deploy-mode client \ --executor-memory 8G \ --executor-cores 4 \ --num-executors 20 \ --conf spark.sql.shuffle.partitions400 \ profile_daily_job.py --bizdate $(date -d yesterday %F)几个关键参数说明--executor-memory 8G和--executor-cores 4决定每个Executor的计算资源--num-executors 20控制并行度spark.sql.shuffle.partitions直接影响JOIN和GROUP BY的reduce数量设置过小会OOM设置过大会产生大量小文件。小表数据可以先广播例如在Spark代码里加spark.sql.autoBroadcastJoinThreshold配置把维表大小阈值调高些。此外建议每天对画像任务做“新数据量和空分区”巡检。空分区常常意味着上游日志停送如果忽略画像会静默变差。5. 用户画像基础的验证覆盖率和准确率怎么算标签表和指标表不同指标有业务原始定义标签则由多层加工产生一旦口径错了下游很难察觉。因此在输出给应用方之前至少要看两个指标覆盖率和准确率。5.1 覆盖率画像覆盖了多少用户覆盖率的定义不能简单写成COUNT(*) / 总用户数。分母不同会得到完全相反的结论。我一般按业务用途区分分母用于站内推荐的分母取近30天有访问行为的活跃用户用于短信营销的分母取近30天有真实手机号的用户用于全量统计的分母取注册表全部用户。下面这个SQL按价值标签计算覆盖率SELECT tag_value_level, COUNT(*) AS tagged_user_cnt, COUNT(*) / MAX(total_ucnt) AS coverage_rate FROM ( SELECT p.user_id, p.tag_value_level, (SELECT COUNT(DISTINCT user_id) FROM dws.user_profile_daily WHERE dt ${bizdate}) AS total_ucnt FROM dws.user_profile_daily p WHERE dt ${bizdate} ) t GROUP BY tag_value_level;逻辑说明内层子查询把总用户数和每个用户行绑定在一起MAX(total_ucnt)在GROUP BY后只取到同一个总数不会因分组被放大。coverage_rate可以比较不同标签之间的填充度如果某标签覆盖率低于预期要回查是数据源缺失还是加工逻辑里的过滤条件太严格。5.2 准确率抽检回来了覆盖率说明标签有没有准确率说明标签对不对。实际操作中不可能校验全量通常按用户ID分层随机抽取100200个样本由业务方对每个标签做“是/否/存疑”的三分类判断。判断结果回来后用Python快速算一个准确率import random check_result [] # 模拟抽检结果实际应来自人工标注 # 每条记录格式: {user_id: 1001, pred: high, label: high} correct sum(1 for r in check_result if r[pred] r[label]) accuracy correct / len(check_result) print(f抽检准确率: {accuracy:.2%} 抽检量: {len(check_result)})这里最容易被忽略的是“存疑”样本。如果一个标签在业务方眼里经常“存疑”说明标签定义对业务无感要么改定义要么拆成多个更细的标签。准确率抽检不能替代口径文档它是口径文档的日常校验手段。5.3 用A/B测试评估画像效果标签好不好最硬的标准是它能否驱动业务指标。常见做法是做一个面向策略的小流量A/B测试实验组使用画像标签分流控制组保持原有策略。假设要验证“高价值用户标签”的优惠券转化效果最后得出四格表可以用卡方检验判断差异显著性from scipy.stats import chi2_contingency # 每行是 [消费人数, 未消费人数] table [ [experiment_convert, experiment_non_convert], [control_convert, control_non_convert], ] chi2, p_value, _, _ chi2_contingency(table) print(fp_value {p_value:.4f})解释p_value 0.05说明实验组和对照组差异不是随机波动画像标签对业务有可度量影响反之则说明该标签目前没有实际价值需要调标签侧策略而不是急着推广。让画像标签直接背业务指标很危险建议观察至少一个完整的业务周期避免新功能带来的短期波动。6. 把用户画像基础沉淀成PDF从标签文档到可评审的交付物数据文档比代码更容易过期但用户画像基础恰恰需要一份跨团队能评审、能归档的静态文档。我习惯把所有标签口径表、ID映射说明、宽表DDL和验证结果放在同一个Markdown文件里再转换成PDF版本文件名带上日期和版本号。生成PDF最省事的方式是用pandoc加xelatex引擎。在装有TeX发行版的Linux或macOS环境中运行下面命令即可pandoc user_profile.md \ --pdf-enginexelatex \ -V mainfontNoto Sans CJK SC \ -V monofontNoto Sans Mono CJK SC \ -V geometry:margin2.5cm \ -o 用户画像基础_v1.0_20250608.pdf参数说明--pdf-enginexelatex负责把Markdown里的中文内容编译成PDFmainfont和monofont指定中文字体geometry:margin2.5cm控制页边距文档内容多时可以把边距调小内容少时调大。如果不想安装TeX也可以先用pandoc生成HTML再用wkhtmltopdfwkhtmltopdf --enable-local-file-access --footer-center [page] user_profile.html 用户画像基础.pdf生成PDF后建议把每一张标签口径表截图放进去并在文档末尾附上最近一次覆盖率验证结果。这样团队评审时不需要打开数仓单靠一份PDF就能统一对“用户画像基础”的理解后续人事变动时也能保证知识和业务口径不流失。本文还有配套的精品资源点击获取
