尧图精选

电商用户聚类分析实战:Python K-Means用户分群全流程

🕒 发布时间:2026/9/26 20:12:23 📁 来源:尧图网络
上个月有个做电商的朋友来找我说他后台几百万条订单数据就躺在数据库里但运营问他要“高价值用户长什么样”“哪些人快流失了”他半天给不出一个能落地的结论。这是电商用户分析里最典型的问题数据有了但人群分不出来。后来我帮他用Python跑了一遍聚类分析把用户分成四个群体每个群体贴上业务标签运营拿过去直接就能做针对性动作。这篇文章就是那次项目经验的完整复盘从数据准备、特征工程、算法选型到踩坑排查一条线讲清楚。内容围绕“电商用户分析与聚类Python”展开适合有一定Python基础、想系统做用户分群的数据分析师、运营和开发者如果你刚入门跟着代码流程也能跑通跑完再回头补基础概念就顺了。1. 项目拆解电商用户分析为什么非要用聚类刚接到需求时别急着写代码先把业务问题翻译成算法问题。用户分群这件事本质上是要把用户按照“行为特征”分成若干个集合同一个集合里的人足够相似不同集合之间差异足够明显。聚类算法干的正好就是这件事——让数据自己说话而不是靠人拍脑袋定规则。1.1 手工分群和RFM模型卡在哪里很多人最开始会想到RFM模型用最近一次消费时间Recency、消费频率Frequency、消费金额Monetary三个指标各切高/低两档组合出8类用户。这个框架本身没问题问题出在落地细节上。第一阈值不好定。“消费金额高于500算高价值”为什么是500而不是800不同客单价的类目答案完全不同。我见过团队为这个阈值开会吵一下午最后拍了个数字结果模型上线两周就被业务质疑。第二维度太少。RFM三个维度看用户的“消费近度、频度、额度”还行但电商场景里还有平均客单价、购买类目数、大促活跃度、退货率、访问购买转化比等大量信息手工规则很难全部覆盖。第三8类组合的结果解释成本很高。运营同事记不住那么多复杂的人群定义最后也就停留在报表里根本用不起来。聚类的好处是你只需要把用户特征丢给算法它根据样本之间的相似度自动分组。最终得到的每个簇你再结合业务口径起名字比如“高价值忠诚用户”“流失风险用户”。这种方法不再依赖你事先拍阈值而且可以容纳更多维度。1.2 聚类算法选型为什么主力一定是K-Means聚类算法很多K-Means是电商用户分群的首选没有之一。原因很简单算法成熟、计算快、可解释性好。K-Means的思路是先随机放K个中心点把每个样本分给离自己最近的中心欧氏距离然后重新计算每簇中心迭代直到收敛。sklearn里一行KMeans(n_clustersk)就能跑起来几万到百万级用户都能扛得住。但别指望一个算法包打天下。我会同时用层次聚类做辅助验证——它不用提前指定K能画出树状图帮你看数据天然分几群适合小样本时交叉检查K-Means的结果。DBSCAN则用来识别离群用户比如短时间大量下单又大量退单的“羊毛党”K-Means容易把这些异常点硬塞进某一个簇里而DBSCAN可以明确把它们标为噪声。我给这个项目的选型策略是主流程用K-Means分群小样本验证用层次聚类异常识别单独用DBSCAN。用一个表格对比更直观算法是否预设K可解释性适用规模在电商里的场景K-Means是中万~百万全量用户分群层次聚类否可后续定高千~万小样本画像、辅助选KDBSCAN否中万级异常用户识别2. 数据准备与特征工程聚类效果差的根源多数在这步说句不好听的很多项目聚类效果差问题根本不出在算法而是出在喂给算法的数据上。“垃圾进、垃圾出”特征是聚类分析的上限算法只是逼近这个上限的工具。我甚至会把整个项目60%的时间花在数据清洗和特征构造上。2.1 从订单明细到用户画像一张表怎么聚合聚类要求输入是一个二维矩阵每一行是一个用户每一列是一个数值特征。可电商的原始数据往往是订单明细表、浏览日志表、用户信息表乱七八糟的。第一步就是聚合——把用户在所有历史时间里的行为汇总成一行。以订单表为例我通常先做这些特征最近一次消费距今天数recency、累计消费次数frequency、累计消费金额monetary、平均客单价avg_order_value、购买过的去重类目数category_count。这个组合已经能覆盖用户价值的大部分信息。如果业务需要还可以继续加近30天消费次数、大促期间购买占比、退货率等。聚合的代码用pandas简单直接import pandas as pd order_data pd.read_csv(order_table.csv) # 假设order_data里有 user_id, order_time, amount, category_id user_profile order_data.groupby(user_id).agg( recency(order_time, lambda x: (pd.Timestamp(2025-06-01) - pd.to_datetime(x.max())).days), frequency(order_id, count), monetary(amount, sum), avg_order_value(amount, mean), category_count(category_id, nunique) ).reset_index()需要注意两点。第一聚合之后一定要把user_id单独摘出去不能让它参与聚类——用户ID是标识符不是特征放进算法里除了干扰距离计算没任何作用。第二特征不是越多越好。我最早堆了30多个特征聚类结果反而一团糟维度越多噪声越多簇的结构越容易被稀释。实际上10个以内的核心特征往往效果最好。2.2 清洗、标准化与降维别让量纲毁掉欧氏距离特征聚合完后原始值基本不能直接用。先说金额类特征消费金额是典型的长尾分布一小部分高价值用户贡献了大部分销售额大多数人消费金额都很低。这种分布喂给K-Means低消费用户会挤成一堆高价值用户被孤立成一个个“离群点”。处理办法是取对数np.log1p(monetary)把长尾压一压。类似地recency这类偏态指标也可以先看分布再决定要不要变换。再说标准化。K-Means计算距离用的是欧氏距离如果特征量纲不一样数字大的特征会主导整个距离。比如消费金额是几千元消费次数是几次金额维度直接决定了用户之间的“远近”。所以所有参与聚类的特征必须做Z-score标准化(x - mean) / std。sklearn的StandardScaler一行搞定。这个步骤不做聚类质量会大打折扣而且你得花很久排查为什么中心点总是怪怪的。类别变量怎么处理也经常绕晕人。像城市、支付方式这种直接label encoding会引入错误的顺序关系城市A不能比城市B“大”用one-hot又会把维度撑爆。我的经验是核心聚类只放连续价值特征性别、城市这类放在聚类完成之后做交叉分析用既不影响聚类结构又能辅助解释每个簇的画像。这个做法看起来朴素但真的省掉了很多麻烦。提示把user_id丢进聚类、不做标准化、金额不取对数这是我见过最多的三个新手坑。3. Python实战K-Means用户分群完整代码复盘这一节我把完整流程用代码走一遍。为了能直接演示我构造了一份模拟数据——现实中你只需要把第一步换成自己的订单表聚合结果后面所有代码不需要改。3.1 造一份模拟数据并完成预处理模拟数据我造了300个用户分4个潜在群体高价值活跃客、普通活跃用户、流失风险用户、一次性用户。注意我用的是多元正态分布模拟中心点设得比较有区分度这样能清楚地看到聚类算法怎么把人群分开。真实数据可能没有这么“听话”但流程完全一致。import numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler np.random.seed(42) seg1 np.random.normal(loc[8, 25, 10000, 480, 6], scale[3, 8, 1500, 80, 1.5], size(60, 5)) seg2 np.random.normal(loc[20, 10, 2500, 280, 4], scale[5, 4, 600, 70, 1.0], size(100, 5)) seg3 np.random.normal(loc[90, 2, 1200, 600, 2], scale[10, 1, 300, 150, 1.0], size(80, 5)) seg4 np.random.normal(loc[130, 1, 180, 180, 1], scale[15, 0.5, 90, 60, 0.6], size(60, 5)) df pd.DataFrame( np.vstack([seg1, seg2, seg3, seg4]), columns[recency, frequency, monetary, avg_order_value, category_count] ) df df.clip(lower0) # 把负值截断为0模拟真实计数字段 df[monetary_log] np.log1p(df[monetary]) features [recency, frequency, monetary_log, avg_order_value, category_count] scaler StandardScaler() X scaler.fit_transform(df[features])这里解释两个细节为什么monetary要另外造一个monetary_log而不是直接替换因为聚类时用log后的金额让分布更健康但最终做用户画像时业务方要看的是真实金额所以原始列保留着展示阶段再拿出来用。标准化是对全部5个特征做的重点观察monetary_log和frequency的量纲差异怎么被抹平的。3.2 选K值不能靠猜肘部法则与轮廓系数到底怎么看K-Means唯一需要你决策的超参数就是K分几群。我不建议直接拍脑袋定K4两个指标配合起来看更靠谱肘部法则和轮廓系数。肘部法则看的是SSE簇内平方误差即每个样本到它所属中心的距离平方和。随着K增大SSE一定会下降但下降速度会由快到慢拐弯的位置像个手肘那个K就是合理选择。轮廓系数则同时衡量内聚度和分离度每个样本算一个得分最终取均值取值范围[-1, 1]越接近1说明簇越紧凑、簇间越分离。代码可以这样写from sklearn.cluster import KMeans import matplotlib.pyplot as plt sse [] for k in range(2, 11): km KMeans(n_clustersk, random_state42, n_init10) km.fit(X) sse.append(km.inertia_) plt.plot(range(2, 11), sse, o-) plt.xlabel(K) plt.ylabel(SSE) plt.show()轮廓系数部分from sklearn.metrics import silhouette_score for k in range(2, 11): km KMeans(n_clustersk, random_state42, n_init10) labels km.fit_predict(X) score silhouette_score(X, labels) print(fK{k}, silhouette_score{score:.3f})我的判断顺序是先看轮廓系数通常电商场景里K在3到6之间分数还行太小没有区分度太大业务没法解释再看肘部图有没有明显拐点。如果肘部不明显以轮廓系数最高且业务能讲得通的那个K为准。这个项目里K4时轮廓系数和簇间差异都比较理想所以下面就用K4。3.3 聚类训练、用户画像与结果可视化选定了K训练聚类、把标签放回原始数据框km KMeans(n_clusters4, random_state42, n_init10) df[cluster] km.fit_predict(X) profile_cols [recency, frequency, monetary, avg_order_value, category_count] profile df.groupby(cluster)[profile_cols].mean().round(1) print(profile)运行结果大概是下面这张表的样子因为是随机生成的数据数值会有一点点波动但分组逻辑是不变的clusterrecencyfrequencymonetaryavg_order_valuecategory_count0130.51.1182.3178.61.1189.72.21215.8601.32.0219.810.12512.4281.63.938.224.810067.5478.75.8看这张画像表业务标签已经在“自己冒出来”了簇3最近才买、买得多、客单价不低、类目非常多妥妥的高价值忠实用户。簇2消费频率不错但金额一般属于正常活跃用户。簇1最近购买间隔接近90天、历史金额还在但不再来了明显是沉睡预警用户。簇0基本就是一次性用户消费时间久远、金额极低、类目窄。给簇起名之后可视化能帮你和业务方沟通。最常用的是先PCA降维到二维再画散点图看簇与簇之间的分离情况from sklearn.decomposition import PCA pca PCA(n_components2) coords pca.fit_transform(X) df[pc1], df[pc2] coords[:, 0], coords[:, 1] for c in sorted(df[cluster].unique()): sub df[df[cluster] c] plt.scatter(sub[pc1], sub[pc2], labelfcluster {c}) plt.legend() plt.show()把散点图、画像表、簇标签放到一起给业务方他们能马上看出四个群体的差异也能顺着标签去拆解各自的运营动作高价值忠实用户拉VIP维护群活跃用户促复购、做交叉销售流失预警用户发召回券一次性用户控制投放成本。这一步才算真正把“聚类”变成了“用户分析”。4. 层次聚类和DBSCAN是鸡肋吗交叉验证与异常识别主流程跑完K-Means之后不代表这个项目就结束了。我在实际项目里还会用层次聚类和DBSCAN做两件“额外”的事验证分群是否稳定以及把异常用户从人群里摘出去。4.1 层次聚类树状图定K顺便监督K-Means层次聚类不用先指定K它自底向上不断合并最近的簇直到全部归为一个。sklearn里有现成的AgglomerativeClustering不过我更喜欢用scipy画树状图因为它能直观地看到“数据自然分成几群”。from scipy.cluster.hierarchy import linkage, dendrogram Z linkage(X, methodward) dendrogram(Z, truncate_modelevel, p5) plt.show()树状图中间像“树干”的分叉越明显说明自然分成那几群的结构越稳定。我在这个项目里看到4个大分支和肘部法则、轮廓系数判断出来的K4能对上分群结论就更有底气了。层次聚类的另一个用途是给K-Means做交叉验证在同样的数据上跑完层次聚类把两种算法的标签做交叉列联表如果两个簇之间高度对应说明K-Means的随机初始化没有“撞”出虚假结构如果对应关系很散就要怀疑K-Means是不是陷进了局部最优。你甚至可以换个random_state多跑几次K-Means来检验稳定性。注意层次聚类的复杂度接近O(n²)用户超过几万就别硬跑抽样两万跑一下做验证就够了。4.2 DBSCAN把异常用户从分群里摘出来DBSCAN是基于密度的聚类在半径为eps的邻域内如果样本数不少于min_samples就认为这里是密集区域并不断扩展连通最终密度连接的区域形成一个簇剩下的孤立点被标记为噪声-1。这个特性让它特别适合识别“离群的人”批量注册的羊毛党、测试账号、数据采集异常产生的垃圾用户。from sklearn.cluster import DBSCAN db DBSCAN(eps1.2, min_samples10) df[db_label] db.fit_predict(X) # -1 表示噪声用DBSCAN参数要耐心调eps可以用k-距离图的拐点来选min_samples一般取特征维度的两倍左右。还有一点高维标准化数据上距离度量会变飘建议先用PCA降到2~3维再跑DBSCAN效果稳定很多。DBSCAN分出噪声不等于这些人一定是坏用户也许是行为确实稀疏的正常消费者需要结合业务再下一轮判断。但把它单独拎出来至少不会污染接下来K-Means的分群结构。5. 这些坑我全踩过K-Means分群的常见问题与排查实录最后这部分是硬通货全是实操中遇到的问题。按场景整理成排查手册遇到类似情况直接对照处理。5.1 聚类结果糊成一片先回去查这三件事现象是画出来的散点图几个簇叠在一起画像表里每个簇的特征均值都差不多业务方看了一头雾水。我遇到过好几次排查顺序如下。第一查特征。是不是把很多无关变量塞进去了比如注册设备类型、渠道来源这种维度如果本身和用户价值关系不大很容易变成噪声。第二查分布。金额类、间隔类特征往往偏态严重取log和标准化做了没有第三查信息量。现有特征能不能区分开人群可以做一个小实验先强行聚成2类看两个簇在核心指标上的均值差异明不明显如果2类都分不开问题基本就在特征不在算法。排查时画箱线图对比各簇的每个特征分布也很管用——某个簇在某一项上明显分离画像才有讲头。我个人的经验是对金额类特征取log之后很多“糊状”聚类瞬间变得棱角分明。5.2 异常值和量纲把中心点拽偏了怎么办K-Means用均值作为簇中心最怕极端值。比如某个企业采购账号单笔消费几十万直接把所在簇的中心拽着跑。处理方法是三层套先把超过99分位的极端值截断再对长尾特征取log最后做标准化。这套组合拳下来大部分量纲和偏差问题都能压住。还有一个容易踩的坑数据里如果有大量“零消费用户”别直接扔进K-Means。他们会因为特征几乎一样而抱成一个巨型簇把正常用户的距离结构全挤走。正确做法是先按是否消费分成两层有消费的用户单独做聚类零消费用户单独归为一类最后再合并成完整的人群体系。高价值用户如果数量少但金额跨度大也可以单独拎出来做子聚类避免被普通用户稀释掉。5.3 特征太多、太杂降维和编码的正确姿势特征堆到30个以上时聚类结果经常不稳定——换一个随机种子分群结果大变样。这就是高维诅咒维度越高欧氏距离越趋向于平均每个簇之间的差异会被稀释。解决办法是砍特征而不是盲目求多。核心价值特征控制在5~10个效果往往好于30个。面对类别变量我的正确姿势是“聚类时不用聚类后交叉”。比如城市特征聚类前不参与计算聚类后统计每个簇的城市分布既能看出人群地域特征又不会因为one-hot把维度撑爆。如果非要把多个数值和类别特征混在一起建模建议先PCA降维到10维以内。固定random_state也是好习惯保证同一份数据反复训练时结果可复现。下面把常见问题汇总成一张速查表问题主要表现排查方向常用解法聚类糊成一片簇间均值差异小、散点重叠特征、分布处理砍特征、log变换、标准化、先聚2类验证中心点被拽偏某个簇均值异常大或异常小异常值、量纲分位数截断、log、标准化、分层聚类结果不稳定换随机种子分群变化大维度太多、初始化固定random_state、减少特征、PCA降维零消费用户扎堆低消簇过大群体差异太大按是否消费分层再分别聚类噪声用户污染分群出现难以解释的微小簇异常注册、羊毛党DBSCAN单独识别噪声再跑K-Means最后再分享一个项目管理里的体会给业务方交付时光有“cluster3”这样的标签是不够的我会把每个簇的用户占比、核心指标均值、典型用户故事和下一步运营动作写成一段“人群说明书”业务才能真正用起来。分群结果不是一劳永逸的用户行为一直在变建议每个月重跑一次聚类同时追踪用户在不同簇之间的迁移——哪些高价值用户掉进了流失预警簇及时跟进挽回这才是用户聚类在电商运营里产生价值的关键时刻。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →