
用户画像这四个字这几年被聊得有点烂大街。经常有人拿一份几十页的PRD来找我说要搭一套人工智能驱动的用户画像中台一问数据在哪、标签怎么算、业务方拿它干什么、靠什么指标衡量价值全都答不上来。老实说我自己最早也踩过这种坑总以为画像系统是个非得上Hadoop、Spark不可的大工程。后来真正从头到尾落地过几个项目才明白用Python完全可以搭出一套能支撑精细化运营的用户画像系统关键在于先把边界、分层和路径想清楚再动手写代码。这篇文章就把完整的过程拆开讲从数据清洗、ID打通、标签建设、用户分群到最终的查询可视化和业务落地每一步都有可运行的代码和踩坑经验。适合有一定Python基础、正准备做用户画像或者在做用户增长、精细化运营的读者参考。如果你以为画像系统就是给用户打几个标签那这篇可能会刷新你的认知。1. 用户画像系统先想清楚再动手核心概念与边界定义1.1 用户画像到底在解决什么问题用户画像不是一个凭空造出来的概念它是从海量用户数据中抽象出用户的属性、行为、偏好的结构化描述。它的核心价值在于回答三个问题我的用户是谁他做过什么他接下来可能想要什么举个例子在电商场景里我们需要知道最近30天购买超过3次的高活跃用户是谁也要知道收藏了运动鞋但从未下单的潜力人群有多少。如果没有画像系统运营只能看大盘数据根本做不到差异化沟通。但这里有个很多人容易搞错的点用户画像不等于标签。标签只是画像的一种载体画像是从业务问题倒推出来的数据解决方案。如果业务方只是想发个优惠券那你只需要圈选人群不需要完整画像如果想做个性化推荐那你需要偏好的模型标签如果想做流失预警又需要另外一套特征。所以在立项之前一定要反复问业务方你们拿到画像之后下一步做什么动作这个问题问不出来项目大概率会做成一个没人用的数据仓库。1.2 从业务角度看画像系统的输入输出画像系统的输入通常包括四类数据用户基础属性用户ID、性别、年龄、城市、注册渠道、注册时间。行为日志浏览、点击、搜索、收藏、加购、下单等行为以及对应的时间和目标对象。订单交易数据订单金额、购买商品、购买时间、支付方式。售后与客服数据退款记录、投诉类型、客服咨询内容文本可做NLP分析。输出则包括三个层面用户标签集合例如高价值用户美妆偏好人群价格敏感型用户。用户分群结果将用户划分为若干有业务意义的群体。查询与可视化能力业务方能够通过接口或报表看到某个用户、某个人群的画像特征。我特别想强调一句话画像是中间产物不是最终目标。所有设计都必须从业务方想拿画像做什么倒推。如果你一开始就想着把所有标签都做出来那基本就注定了项目延期。正确做法是先圈定一个核心业务目标比如提升高价值用户复购率然后只建设支撑这个目标的标签体系后续再逐步扩展。1.3 画像系统的三大组成模块数据层、标签层、应用层即使团队再小我也建议把系统拆成三层避免所有代码搅在一起数据层负责数据的采集、清洗和存储。在Python生态里离线数据用Pandas处理完全足够数据量大一点可以用Dask或Polars数据源存在PostgreSQL或数据仓库里。标签层这是画像系统的核心。它从数据层读取原始数据经过加工计算产出标签和分群结果。这一层通常是跑批任务每天凌晨定时执行。应用层对外提供查询接口、可视化报表、人群圈选功能。MVP阶段用FastAPI写API再用Jupyter或者Superset出可视化完全能撑起内部使用。这样分层的最大好处是每层可以独立演进标签规则改了不用动应用层应用层新增了功能不用重跑数据。很多小团队一上来就想搭大数据平台结果光环境搭建就耗费几个月业务早就凉了。前期用Pandas PostgreSQL FastAPI这样的组合足够应对几千万用户量级的离线画像。2. 数据基础从埋点日志到用户属性表的清洗与整合2.1 数据源梳理与接入方式用户画像项目里最耗时间的往往不是算法而是数据基础。我见过太多项目死在数据质量上字段对不上、ID口径不统一、时间格式千奇百怪。所以在写任何标签计算代码之前先花两三天时间把数据源彻底摸清楚。以电商场景为例至少会有三张核心表表名字段示例来源用户属性表user_id, gender, age, city, register_time, channel业务数据库行为日志表user_id, item_id, behavior_type, timestamp, session_id埋点平台订单表order_id, user_id, item_id, amount, pay_time交易数据库接入方式取决于你们的数据基础设施。最省事的方式是让数据团队每天把数据导出成CSV或者同步到PostgreSQL然后用Pandas读取。示例import pandas as pd users pd.read_csv(users.csv) logs pd.read_csv(behavior_logs.csv) orders pd.read_csv(orders.csv) print(users.info()) print(logs.head())如果已经有了数据仓库直接用SQLAlchemy连接读取会更规范from sqlalchemy import create_engine engine create_engine(postgresql://user:passwordhost:5432/dbname) users pd.read_sql(SELECT * FROM dim_user, engine) logs pd.read_sql(SELECT * FROM fact_behavior_log, engine)这里有一个常见的坑行为日志表通常巨大一读就是几个GPandas会直接内存爆炸。我的处理方式是先用SQL在数据库里做一次聚合只抽取画像需要的最小粒度数据。比如计算用户近30天浏览次数直接在SQL里GROUP BY user_id而不是把明细数据全拉到内存里再算。2.2 用户唯一标识的打通ID-Mapping的简化实现做画像系统必谈ID-Mapping也就是识别一个人在多个设备、多个账号之间是不是同一个人。比如用户先在小程序里逛了一圈又去APP里下单如果你不打通这两个ID就会把他当成两个人画像数据就乱了。全量的ID-Mapping非常复杂需要用图计算做关系合并。但MVP阶段完全可以做一个简化版本如果有union_id统一用户ID最好直接用如果没有就用手机号、设备ID、登录账号这几个标识通过Union-Find并查集算法合并。Python里可以自己写一个并查集类也可以用networkx的连通分量来搞定import networkx as nx # 假设我们有一个映射表标识类型 - 标识值 - 用户ID G nx.Graph() # 例如存在多条记录(user_id, phone), (user_id, device_id) # 就把这些边加入图 G.add_edges_from([(U001, 13800001111), (U001, device_abc), (U002, 13800001111)]) # 找出连通分量 for component in nx.connected_components(G): user_ids [node for node in component if str(node).startswith(U)] # 这些user_id实际属于同一个人这里有个经验之谈如果你们的产品只有一个登录账号体系那就不要过度设计ID-Mapping。用户画像90%的价值来自对已注册用户的分析先把账号维度做扎实等需要做匿名用户识别时再上设备图谱。不要为了技术炫技把自己拖进黑洞。2.3 基于Pandas的数据清洗实战数据清洗没有很多教程里说的那么神秘总结起来就四步去重、缺失值处理、异常值过滤、类型转换。但每一步都有值得注意的细节。首先是去重。用户属性表可能因为数据导出有重复要按主键去重users users.drop_duplicates(subsetuser_id)然后是缺失值。年龄缺失怎么办不要用均值填建议保留一个unknown的标记因为用户画像里未知本身就是一个信息。性别缺失同理。城市缺失可以映射为未知。然后是异常值过滤。年龄大于120、金额为负数、注册时间早于平台创建时间都属于异常。处理方式看业务逻辑退款订单金额是负数不能直接删但注册时间为负数就一定是脏数据要过滤。# 年龄过滤 users users[(users[age] 0) (users[age] 120)] # 时间标准化 logs[timestamp] pd.to_datetime(logs[timestamp], errorscoerce) logs logs.dropna(subset[timestamp])最后是类型转换。user_id在CSV里经常被读成int一旦用户ID超过2^53就会精度丢失所以user_id一律用字符串读取users[user_id] users[user_id].astype(str)不要小看这一步多少项目因为用户ID精度丢失导致画像错乱排查起来极其痛苦。3. 标签体系搭建从原始数据到可用的用户标签3.1 标签分类框架事实标签、规则标签、模型标签标签是画像系统的核心产出物。我习惯把它分成三类事实标签来自用户注册信息或业务系统的事实记录比如性别、城市、是否会员、注册渠道。这类标签准确度高、几乎不变。规则标签基于业务规则对用户行为进行统计和判断比如近30天活跃天数10标记为高频活跃近90天消费金额1000标记为高价值。模型标签通过机器学习模型预测出来的标签比如流失概率0.8偏好类目运动户外。这三类标签的更新频率和稳定性完全不同。事实标签可以周更或月更规则标签建议每日更新模型标签则需要定期重训练同时要监控预测准确率。把它们分清楚后面做数据治理时才能有的放矢。如果你问业务方需要哪些标签他们往往会说越多越好。这时候你要学会做减法。我常用的方法是让业务方列出三个最紧急的运营场景每个场景罗列必需的标签其他标签一律暂缓。这能救命。3.2 统计类标签的实现RFM模型代码实例RFM是用户价值分析里最经典、最实用的模型没有之一。它通过三个维度刻画用户价值Recency最近一次消费时间距离上次下单有多少天越短代表越活跃。Frequency消费频率一段时间内的下单次数越高代表越忠诚。Monetary消费金额一段时间内的消费总额越高代表越有价值。Pandas写RFM非常顺手import pandas as pd from datetime import datetime # ref_date是统计截止日比如今天 ref_date pd.to_datetime(2025-01-01) rfm orders.groupby(user_id).agg( last_order_date(pay_time, max), frequency(order_id, count), monetary(pay_amount, sum) ).reset_index() # 计算recency rfm[recency] (ref_date - rfm[last_order_date]).dt.days # 分位数打分1-5分 rfm[r_score] pd.qcut(rfm[recency], 5, labels[5, 4, 3, 2, 1], duplicatesdrop) rfm[f_score] pd.qcut(rfm[frequency].rank(methodfirst), 5, labels[1, 2, 3, 4, 5], duplicatesdrop) rfm[m_score] pd.qcut(rfm[monetary].rank(methodfirst), 5, labels[1, 2, 3, 4, 5], duplicatesdrop)注意这里recency越小越好所以打分是反的最近消费的给5分。qcut如果遇到重复分位数会报错我一般先对列做rank(methodfirst)再分箱避免相同值导致区间重叠。打完分之后可以合并成用户价值等级rfm[rfm_score] rfm[r_score].astype(int) * 100 rfm[f_score].astype(int) * 10 rfm[m_score].astype(int) def value_level(score): if score 445: return 重要价值用户 elif score 334: return 一般价值用户 elif score 223: return 潜力用户 else: return 低价值用户 rfm[value_level] rfm[rfm_score].apply(value_level)这套代码我几乎在每个项目里都会复用。它最大的优点就是解释成本低业务方一看就懂而且能直接指导运营动作重要价值用户要优先维护低价值用户可以通过优惠券唤醒。3.3 规则标签与机器学习标签的落地方式规则标签的落地方式很简单写函数批量apply。比如定义活跃度等级def active_level(row): if row[login_days_30] 20: return 高频活跃 elif row[login_days_30] 5: return 中频活跃 else: return 低频活跃 users[active_level] users.apply(active_level, axis1)但规则标签最怕的是规则打架。比如一个用户被标记为高价值同时又被标记为沉睡用户这在业务上很矛盾。所以规则标签一定要建一个统一的管理文档记录每条规则的业务定义、SQL/代码入口、更新周期和维护人。否则三个月后连写代码的人都不记得这个标签什么意思。模型标签则稍微复杂一些。以一个简单的流失预警为例确定正负样本比如把未来30天未登录定义成流失过去有登录且后续流失的用户为1未流失为0。提取特征近7天活跃天数、近30天订单数、平均会话时长、最近一次登录距离今天的间隔。训练模型用XGBoost或者LightGBM训练一个二分类器。输出概率标签将预测概率按阈值切成0~1分大于0.7标记为高流失风险。示例代码框架import pandas as pd from xgboost import XGBClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score features [login_days_7, order_cnt_30, avg_session_sec, days_since_last_login] X train_df[features] y train_df[is_churn] X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) model XGBClassifier(n_estimators200, max_depth4, random_state42) model.fit(X_train, y_train) print(AUC:, roc_auc_score(y_test, model.predict_proba(X_test)[:, 1]))模型标签有个关键点必须监控预测分布的变化。如果这周预测流失的用户比例突然从10%涨到30%可能不是用户真的变了而是数据特征出现异常。所以模型上线后最好也记录每日预测分数的分位数看到异常波动要第一时间报警。4. 用户分群如何从标签到有业务价值的群体4.1 基于标签的规则分群业务运营最常提的需求就是给我圈一波人。比如近7天加购但没下单的女性用户这属于规则分群直接对标签宽表做筛选就行。我一般把所有标签合并成一张宽表每行是一个用户列是各种标签然后就可以用Pandas做任意组合筛选# 标签宽表 tag_df users[[user_id, gender, age, city]].merge( rfm[[user_id, value_level]], onuser_id, howleft ).merge( behavior_tags[[user_id, add_cart_not_order_7d]], onuser_id, howleft ) target_users tag_df[ (tag_df[gender] 女) (tag_df[add_cart_not_order_7d] True) ] print(target_users[user_id].tolist())这种分群方式逻辑透明最适合活动运营。但要注意效率问题如果宽表有几百万行每次都在Pandas里筛选是可以接受的但如果是实时接口最好把标签宽表同步到数据库并加索引用SQL查询。实际项目里我一般把最终标签数据写入PostgreSQL建一个复合索引业务方可以通过一个简单的后台页面自行圈选人群。4.2 使用K-Means做用户聚类分群当标签数量很多规则组合变得复杂时可以用聚类算法帮我们自动寻找人群结构。K-Means是最常用的聚类方法因为它简单、快、解释性也不差。使用sklearn的流程from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler # 选取聚类特征 features tag_df[[total_amount, order_cnt_90, avg_order_value, days_since_last_order, browse_cnt_7d]] # 标准化K-Means基于距离计算特征量纲不一致会使大数值特征主导 scaler StandardScaler() X_scaled scaler.fit_transform(features) # 训练 kmeans KMeans(n_clusters4, random_state42, n_init10) tag_df[cluster] kmeans.fit_predict(X_scaled)选择K值常用肘部法则把K从2试到10分别计算簇内误差平方和inertia画出来找拐点。但是实际业务中我更喜欢直接用业务经验先定一个K比如把用户分成高、中、低三档先看结果解释是否合理再微调K值。因为最优K未必符合业务直觉而业务不认可的分群等于没用。4.3 分群结果的评估与业务解释聚类不是算完就完了最重要的是给每个群取名字。我给群起名的流程是每个群用均值或中位数描述特征找出最突出的几个标签然后结合业务语义命名。例如一个聚类结果群平均消费金额平均订单数最近一次消费间隔(天)平均浏览数/周命名0320015328高价值活跃11802455沉睡低价280061218中间潜力312003602高额流失风险除了聚类质量指标轮廓系数、Davies-Bouldin指数我对分群结果最看重的是业务可否解释。如果运营团队看过之后能立刻说出这群人应该用推送券激活或者这群人要重点客服回访这个分群就是成功的。如果运营一脸茫然那就赶紧调整特征和K值。另一个容易忽略的点分群结果要考虑稳定性。不要这个月分成4群下个月聚类结果完全变样业务方根本没法积累认知。解决办法是锁定一部分种子用户每次聚类后看这些用户群标签的迁移情况也可以用有监督的迁移学习框架但MVP阶段不必那么复杂保持特征定义稳定即可。5. 画像查询与可视化让数据产生实际作用5.1 构建标签存储与查询接口画像系统最终要给业务方使用不能每次让运营跑SQL。MVP阶段最简单的做法就是把所有标签合成一张宽表导入PostgreSQL再用FastAPI写一个查询接口。先建表并导入数据CREATE TABLE user_tags ( user_id VARCHAR(64) PRIMARY KEY, gender VARCHAR(8), age INT, city VARCHAR(32), value_level VARCHAR(16), active_level VARCHAR(16), cluster INT, update_time TIMESTAMP );然后从Pandas写到数据库from sqlalchemy import create_engine engine create_engine(postgresql://user:passwordhost:5432/dbname) tag_df.to_sql(user_tags, engine, if_existsreplace, indexFalse)查询接口用FastAPIfrom fastapi import FastAPI, HTTPException from sqlalchemy import create_engine, text app FastAPI() engine create_engine(postgresql://user:passwordhost:5432/dbname) app.get(/v1/user/{user_id}) def get_user_tags(user_id: str): sql text(SELECT * FROM user_tags WHERE user_id :uid) with engine.connect() as conn: row conn.execute(sql, {uid: user_id}).mappings().first() if not row: raise HTTPException(status_code404, detailuser not found) return dict(row)这样一个接口就够内部系统用了。如果后续有人群圈选、画像分布统计等需求再加一个查询参数就行。不要一开始就上Elasticsearch、ClickHouse关系型数据库扛不住再加缓存或者换分析型数据库都是后话。5.2 用Python快速实现画像可视化可视化的目的是帮助业务发现问题而不是为了好看。我平时用得最多的是三种图标签分布图比如各价值等级用户占比用柱状图。用户分群散点图将高维特征降维到二维展示聚类效果用PCA或者TSNE。用户生命周期漏斗图比如从新注册到首单、再到复购、再到流失的转化链路用漏斗图。示例画各价值等级分布import matplotlib.pyplot as plt import pandas as pd counts tag_df[value_level].value_counts() plt.figure(figsize(8, 5)) plt.bar(counts.index, counts.values, color#4C72B0) plt.title(User Value Level Distribution) plt.xlabel(Value Level) plt.ylabel(User Count) for i, v in enumerate(counts.values): plt.text(i, v 5, str(v), hacenter, vabottom) plt.show()聚类结果的散点图from sklearn.decomposition import PCA pca PCA(n_components2) pca_result pca.fit_transform(X_scaled) plt.scatter(pca_result[:, 0], pca_result[:, 1], ctag_df[cluster], cmapviridis, alpha0.5) plt.title(User Clusters (PCA)) plt.xlabel(PC1) plt.ylabel(PC2) plt.colorbar() plt.show()要注意的是散点图如果样本太多画出来就是一团黑通常我会先随机抽样5000个点再画这样既能看到聚类轮廓也不会卡死。5.3 画像系统在推荐、运营、增长中的典型应用画像系统做完之后一定要接进业务否则就是自嗨。我参与过的项目里画像系统最常见的三个应用场景是第一个是推荐系统的冷启动。新用户没有行为数据时基于注册信息和个人资料画像比如城市、年龄段、注册渠道对应的偏好给出一批候选物品加权。比如北京地区24-30岁男性用户优先推荐数码和运动户外品类。这个用简单的规则加权就能实现比纯热门推荐效果好不少。第二个是精细化运营。对高价值用户提供专属客服和会员权益对流失风险用户推送优惠券对沉睡用户做召回短信。圈选人群、设置策略、追踪效果这套流程可以直接用画像系统的分群能力驱动。第三个是增长分析。按画像分群对比不同渠道的留存和转化比如通过短视频渠道来的用户画像偏向低龄段次日留存高但客单价低通过搜索渠道来的用户画像偏向高消费转化率高但量小。这些洞察能帮助市场投放做预算调整。我特别想说画像应用一定要配合试验评估。比如你要给中频活跃用户发优惠券最好设置对照组比较发券和没发券的群体在复购率上的差异。只有经过试验验证才能证明画像系统真的在产生价值而不是自我感觉良好。最后再分享一点我个人的经验前面讲了这么多其实我最想说的是搭建用户画像系统的关键不是算法多高级而是能不能快速形成业务闭环。我第一次做的时候光想搞统一ID就花了一个月结果业务方等不及直接自己写SQL把人群圈出来了。后来我学乖了第一版脚本先只打通注册账号把最核心的RFM标签和活跃度标签做出来两周就上线业务方立刻用起来再根据反馈迭代这个项目才真正活过来。还有一个很实在的技巧把所有标签的计算逻辑、更新频率、负责人都写进一个Markdown文档或者Notion页面里放在团队都能看到的地方。这个文档平时没人看但一旦标签口径变更或者新同学接手就能省下无数扯皮的时间。画像系统的数据血缘可能比它的算法精度更影响长期健康度。如果你正准备开始做用户画像我的建议是用一个真实的业务问题作为牵引比如三个月内提升复购率5%然后倒推需要什么数据、什么标签、什么分群、什么动作。用Python快速做出一个能跑通全流程的最小系统比什么都强。先让业务看到画像系统的价值后面你才有资源去迭代更复杂的模型和更完整的架构。