资讯动态

K-Means用户分群实战:从特征工程到人群画像与运营策略

发布时间:2026/10/20 16:12:46 来源:尧图企业网站定制
做数据分析这几年我越来越觉得所谓“懂用户”这件事其实可以拆成两步一是把用户行为摸清楚二是把用户群体分明白。摸清楚靠的是埋点、报表、漏斗分明白靠的就是聚类。而在聚类算法里K-Means又是最让人又爱又恨的一个——好用但容易用错。这篇博文我就拿一个电商场景的实战案例完整走一遍K-Means做市场细分的流程从数据清洗到特征构建从K值确定到人群画像解读把里面的关键细节和踩过的坑都摊开来讲。无论你是刚接触机器学习的数据新人还是已经在做用户增长但想换个思路的运营同学应该都能从中拿到点能直接用的东西。1. 市场细分这件事为什么非要用K-Means1.1 先搞清楚一个根本问题细分到底是在分什么很多人一提到用户细分第一反应就是“按性别分”“按年龄分”“按城市分”。但做过业务的都知道这种分法在实际运营里基本没什么用。你说你是女性用户25岁在一线城市然后呢你知道她喜欢什么吗你知道她多久打开一次你的小程序吗你知道她上一次下单是什么时候吗不知道。因为这些人口统计属性跟真实消费行为之间隔着一层很厚的“意愿”和“场景”。真正能指导运营动作的细分一定是建立在行为数据上的。也就是说一个用户被你归到“高活跃、高客单价、低价格敏感度”这个群里那你至少知道可以给他推新品另一个用户是“高活跃、低客单价、容易被优惠券打动”那你的策略就得换成促转化、拉复购。K-Means能做的就是把这种多维度的行为数据压缩成几个“行为特征相似的人群”让运营同学不用对着几万个用户ID发呆而是对着三五个有名字、有画像、有策略的人群做决策。我常打一个比方K-Means就像是在帮一群散落在操场上的学生排队它不管你的姓名和学号只看你现在站在哪里然后按照“离谁最近就跟谁站一起”的原则自然分组。最后每个小组自己推选一个“队长”质心组内成员围绕队长聚成一团。你回头看操场被分成了几块每一块都有自己的聚集特征这就是人群画像。1.2 K-Means的原理其实很简单但简单不等于没用K-Means的核心逻辑就四步选K个初始中心点把每个样本分给离它最近的质心重新计算每个簇的质心重复直到质心不再移动。听起来像废话但就是因为这种“局部迭代、逐步收敛”的思路让它在海量行为数据上的表现极其稳定。这里面有一个很多教材不会细讲的问题K-Means的“距离”到底是什么在实际的用户行为分析中如果你直接用原始字段比如“最近一次购买距今天数”和 “90天内购买次数”去算欧氏距离那么量级大的字段会彻底主导聚类结果。比如“90天购买次数”可能是0到30的范围而“最近一次购买距今天数”可能是1到365的范围两个字段直接算距离的话后者就把前者完全淹没了聚类出来的群基本等于按一个字段切分。所以数据标准化不是一个“可选的加分项”而是一个“不做就出错”的必选项。这一点后面实操部分我会再详细展开。可能还有人会问既然K-Means要事先指定K值、对初始中心点敏感、还只能处理球形簇为什么不用层次聚类或者DBSCAN答案是在真实的用户分群业务里数据量动辄几十万上百万层次聚类的时间复杂度撑不住DBSCAN的密度参数又难以调优。K-Means的可解释性、可并行性、可重复性让它依然是工业界做用户细分的默认起点。先用K-Means跑一个基线版本后续再考虑用更复杂的模型去细化这才是务实的路径。2. 把原始数据变成特征矩阵这一步决定聚类效果的上限2.1 别急着跑代码先想清楚“用什么特征来描述一个人”我在实际项目里见过太多人拿到一张用户订单表就急着导进Python跑K-Means结果跑出来一堆不知道含义的簇。问题的根源往往不是算法而是特征没构建好。你要记住一句话聚类是“输入垃圾输出垃圾”最严重的算法之一因为没人告诉你聚类结果错了你看到的只是“分成了5群每群人数还算均匀”但根本没法解释。那么对于用户行为分析什么特征是最合理的选择我的经验是参考RFM模型的思路但不局限于RFM的原始定义。经典的RFM是三个维度最近一次消费时间Recency、消费频率Frequency、消费金额Monetary。这三个维度能描述“活跃度”和“价值度”但还缺了“偏好度”和“行为模式”。所以我在实际业务里通常会在基础RFM上再增加几个维度用户生命周期时长从首次下单到现在的天数区分新老客品类偏好分布比如某用户购买的商品里食品类占比多少、美妆类占比多少价格偏好区间购买订单的平均折扣率或者客单价落在哪个区间活跃时段偏好用户偏向在白天打开应用还是凌晨打开应用这些字段加起来一个用户就变成了一个多维向量。每个维度都在回答一个问题“这个人在用什么方式跟我们发生交易”。当这些维度一起进入K-Means它找出来的“相似的人群”才真正具有业务含义。在处理这个阶段的实战项目时我当时拿到的数据大概是这样的某零售小程序过去90天的订单记录字段包括用户ID、订单号、实付金额、优惠金额、下单时间、商品一级类目、商品二级类目。加上用户维度的注册时间、最近一次登录时间。看起来字段不多但经过聚合就能生成30多个候选特征。我这里选了8个核心特征进入聚类一方面是控制维度数量防止过拟合另一方面是确保每个进入模型的特征都能讲出业务故事。2.2 数据清洗的3个关键动作去重、填充、滤极值数据清洗这部分直接影响聚类到底是在“看用户”还是在“看脏数据”。我按重要性排序提醒三个关键动作。第一个动作是去重。同一用户在一天内下了多笔订单这在数据里是正常的但如果同一个订单因为系统重试出现了两行记录这种脏数据会让“购买频次”和“消费金额”虚高。处理方式是按“订单号商品ID”去重。此外还有个很容易漏掉的点——退款订单。如果你分析的是“消费行为”而不是“交易流水”建议把退款率特别高的用户单独标记或者用“退款后净消费金额”作为特征避免聚类结果被“假土豪”扰乱。第二个动作是处理缺失值。用户行为表里最常见的缺失是登录时间和注册渠道这类字段如果不参与聚类可以直接不填充。但如果使用的是RFM特征比如“最近一次购买距今天数”对于注册但从未下单的用户这个值是空的我的做法是把这一部分用户单独拆出去因为他们属于“零成交用户”跟“有成交用户”混合聚类会制造出大量没有区分度的中间簇。第三个动作是过滤极值。用户行为数据里一定存在这种妖怪用户90天成交100单客单价5000块连续365天每天打开小程序。这类用户的特征向量是典型的离群点如果不处理K-Means会被他们拖着走最终可能产生一个“1个人的簇”和另一个“所有人挤在一起”的簇。处理方式不是粗暴删除而是做Winsorize截尾处理——比如把“90天购买次数”超过99百分位的值替换成99百分位的值把“客单价”超过上限的也压缩一下。这样既保留了用户的真实行为倾向又不会让极端值主导距离计算。2.3 标准化和编码为什么我推荐Z-score而不是Min-Max特征构建完成后进入聚类前的最后一个关键步骤数据标准化。前面提到不同字段的量级差异会主导距离计算所以必须把每个特征拉到相近的尺度上。常见的两种做法是Min-Max归一化和Z-score标准化。Min-Max归一化将数据压缩到0到1之间看起来很简单但对离群点极其敏感。如果“90天购买次数”最大值为60而99%的用户在10以下那归一化之后绝大多数用户的这个特征都集中在0.15以下等于把特征的分辨率都压没了。Z-score就不一样它按公式(x-μ)/σ缩放受极端值影响的方式更温和而且能保留数据的分布形状更适合K-Means这种基于距离的聚类算法。实际操作中我会在构建特征矩阵后用StandardScaler做转换同时在转换前对明显偏态的字段取对数比如“消费金额”和“购买次数”通常都服从长尾分布先取log再标准化能把大多数用户拉到中间区域让聚类结果更集中、更稳定。我第一次做的时候跳过取对数这一步聚出来的簇几乎就是把用户分成“大客户”和“普通客户”两类特征严重倾斜导致其他维度全部失效。后来加上对数变换细分效果才真正出来。3. 从零跑通一个K-Means分群项目完整代码流程3.1 K值选择的两种经典方法肘部法则和轮廓系数K-Means有一个绕不开的问题K怎么定选少了人群太粗一个簇里啥人都有选多了人群太碎运营没法为每个簇定制策略。两种最经典的方法配合着用可以做出比较科学的判断。第一种是肘部法则。原理很简单把K从1试到10每次计算所有样本到所属簇质心的距离平方和SSE也叫惯性值inertia。K越大SSE天然越小但下降速度会有一个从“快速下降”到“平缓下降”的拐点这个拐点就是“肘部”。其背后的逻辑是K还没到真实分组数时每增加一个簇能显著降低组内误差K超过真实分组数后增加的簇只是在把原本就已经很紧凑的组再切碎误差下降收益很小。画出来就是一根折线弯折点对应的K就是候选值。第二种是轮廓系数Silhouette Score它同时衡量两个指标一个样本到同簇其他样本的平均距离a以及到最近的其他簇样本的平均距离b轮廓系数(b-a)/max(a,b)。这个值的范围是-1到1越接近1说明样本离自己的簇越近、离其他簇越远聚类效果越好。这个指标比SSE更能告诉我们“分出来的群是不是真的边界清晰”。在实际代码里我会做一个综合评估import numpy as np import pandas as pd from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler from sklearn.metrics import silhouette_score import matplotlib.pyplot as plt # 假设 X_std 已经是对数变换 标准化后的特征矩阵 inertias [] silhouettes [] for k in range(2, 11): km KMeans(n_clustersk, n_init10, random_state42) labels km.fit_predict(X_std) inertias.append(km.inertia_) silhouettes.append(silhouette_score(X_std, labels)) # 画肘部图 plt.figure(figsize(12, 4)) plt.subplot(1, 2, 1) plt.plot(range(2, 11), inertias, markero) plt.xlabel(K) plt.ylabel(SSE / Inertia) plt.title(Elbow Method) plt.subplot(1, 2, 2) plt.plot(range(2, 11), silhouettes, markero) plt.xlabel(K) plt.ylabel(Silhouette Score) plt.title(Silhouette Score) plt.tight_layout() plt.show()从图中的变化趋势中可以观察到当K4或K5时SSE的下降幅度已经明显放缓且轮廓系数在这两个值附近达到相对高点我就会把K候选锁定在4到5之间。只讲一个最终结果当然是不负责任的但综合两个指标和业务可解释性最终采用K4作为细分人群的类别数分别是“高价值活跃型”“潜力成长型”“价格敏感型”和“低活跃沉睡型”这是后面画像分析的重点。3.2 运行聚类训练初始化方式、随机种子与结果稳定性确定K值之后正式跑聚类。但这里还有个很多人忽视的细节K-Means对初始化中心点敏感不同的起点可能收敛到不同的局部最优解。sklearn里KMeans默认使用K-Means初始化方法它会尽量让初始质心之间距离远一些大大降低落到局部最优的概率。此外n_init10表示随机初始化10次并选择SSE最小的一次这个参数在旧版本中默认是10新版本中略有调整但显式写上更保险。还要设置random_state。这个参数我每次都会强调因为它决定了可复现性。你这次跑出来的分组结果下次再跑如果结果完全变了那不是数据变了是你没设随机种子。尤其是在你需要在不同版本或不同同事之间对比结果的时候固定种子可以让整个分析过程对账清晰。我习惯设定为42这个数字其实不重要重要的是在报告里写清楚“本次分析使用K4、random_state42”所有结果都基于这一个版本。训练代码非常简洁但我建议加一步把聚类标签回填到原始数据集里方便后面做画像分析。k 4 final_model KMeans(n_clustersk, initk-means, n_init10, random_state42) cluster_labels final_model.fit_predict(X_std) # 回填标签到原始数据 analysis_df[cluster] cluster_labels回填之后第一步看每个簇的样本量和占比。这里要提醒一句K-Means倾向于产出大小差不多的簇如果跑出来一个簇只占0.1%一个簇占了70%大概率不是算法问题而是特征构造或者离群点处理出了问题。我在第一个版本的模型里就出现过“1人簇”排查下来发现是某个用户的退货率异常高又没有做截尾处理结果他一个人被单独分了出来。3.3 聚类质量评估不只是看指标还要看业务可解释性很多教程到这里就结束了但我会建议再多走一步算一下每个簇在各个特征维度上的均值对比总体均值做成一张明细表判断“这个簇到底长什么样”。轮廓系数高只能说明“数学上分得开”不代表“业务上看得懂”。一个优质的聚类结果必须每个簇都能讲出一个故事。下面就是我当时跑出来的簇中心对比数据为了便于阅读我已经把标准化后的均值还原成了业务口径用户群人数占比平均购买次数平均客单价平均折扣率最近购买间隔天品类集中度簇031.7%3.258.20.2445.8中等簇128.4%12.686.40.1811.3较低簇225.8%2.144.70.4731.6较高簇314.1%1.396.80.0992.4高光看这张表你就能大致给每个簇起名字簇0是平均用户人数最多各项指标都在中等水平运营策略应该是“激活与向上转化”。簇1是活跃忠诚用户买得多、隔得短、折扣率低对平台有很强的信任度适合推新品和会员升级。簇2是价格敏感用户折扣率高到0.47也就是几乎一半的订单都靠优惠拉动适合用券和满减活动维护。簇3是低频高价值用户买得少但客单价极高这类人可能只是来买特定品类适合做定向召回和专属服务。走到这一步一个K-Means聚类项目才算真正跑完。接下来你才能拿着这套标签去做人群画像、做策略分层、做个性化触达。实操过程到这里是闭环的但很多人都在中间某个环节出了问题导致结果不能落地后面我专门写一节讲讲最常见的几个坑怎么排查。4. 聚类结果怎么用三张图看懂人群画像与运营策略4.1 人群画像可视化从“数字簇”变成“有血有肉的人”聚类结果回填后模型的工作就算完成了但分析的工作才刚开始。纯看表格里的均值差异业务同事很难共情。我会习惯性做三张图让数据自己说话。第一张图是特征雷达图。把每个簇的标准化值画在同一张图上每个角是一个特征能一眼看出簇与簇之间的“形状差异”。比如簇2的价格敏感群在“折扣率”这个维度上会突出一个大角而簇1的活跃用户则在“购买频次”和“近度”上占优。雷达图的好处是非常直观你在复盘会上指一下“这个角翘起来的群就是优惠券依赖人群”全场立刻就懂。第二张图是二维散点图。利用主成分分析PCA把多维特征压缩到两个主成分上再按聚类标签着色。虽然有过信息损失但能看到簇与簇之间的边界是否重叠严重。如果两个簇交叉很多、边界模糊说明这两个群在行为特征上差异还不够大要么重新调K要么增加新的区分特征。第三张图是簇内典型样本摘录。我的做法是每个簇随机抽取3个真实脱敏用户把他们的原始行为列出来例如“最近30天浏览了18个SKU、下了2单、平均折扣0.35、偏好品类是零食”。这种做法在实际工作中最打动人因为运营同学一看到具体场景就会自动代入“这类人我应该怎么发消息”。我在项目复盘时发现一个很实用的技巧不要尝试描述“所有用户”先锚定两个最特殊的簇比如“价格敏感群”和“低频高价值群”分别为它们设计完整的人物故事再反过来用故事解释数据特征。这就是所谓“由特例到普遍”的表达方式在现场沟通中非常高效。4.2 从洞察到动作不同人群的差异化运营策略分群本身不是目的目的是让运营动作更精准。我见过很多项目死在“分完群就解散了”——因为没有把标签转化成运营策略。下面是我在“某商城”项目里针对4个簇实际落地过的运营动作列出来供参考用户群核心洞察运营动作预期指标活跃忠诚用户高频、近度好、信任度高新品首发体验官邀请、会员等级加速复购率提升、客单价提升平均用户行为中规中矩、没有明显偏好个性化推荐优化、每周精选推送次月转化率、活跃度价格敏感用户折扣驱动、品类集中限时秒杀、满减券包、低价试用装转化率提升、过敏率回访低频高价值用户买的少、买得贵、需求明确私人导购、专属客服、稀缺品定向推送年度复购次数增加这里面有一个重要原则同一个用户群不要同时推太多类型的活动。比如价格敏感用户你一边给他发满减券一边给他推高客单价的新品他大概率只点开优惠券那个消息。一次只围绕一个核心动机去刺激效果最好。从数据反馈上我当时跟进了一期活动针对价格敏感用户发了五元无门槛券整体核销率比对照组提升了1.6倍但这个群的客单价没有明显提升反而略有下降因为他们在券的诱惑下选择了更多低价商品。这说明策略是有效的但要非常清楚地知道你设定的北极星指标是什么——“唤醒沉睡用户”和“提升客单价”本质上是两件事不要被中间指标带偏。4.3 分群结果的迭代更新模型不是一次性的用户行为是会变化的上个月还是活跃忠诚用户这个月可能就变成了平均用户。所以聚类模型不能“一次建模终身使用”。我建议以月度或季度为周期重新跑一次K-Means分群对比相同人群的占比变化和流动性。有一个比较简单的方法可以追踪群体的迁移上一次的聚类结果作为标签本月的用户行为作为特征然后用同样的K和随机种子重新建模再统计“上个月的簇X用户在本月落入了哪些簇”。比如上个月的“高价值活跃型”有26%本月落到了“平均用户”那说明这部分用户正在流失需要提前介入。这种迁移分析比单纯看活跃率变化更有预警意义。做这部分工作时保持特征口径一致性特别重要。如果你的特征从“近90天购买次数”改成了“近30天购买次数”人群画像的变化就没法区分是“用户变了”还是“口径变了”。提供稳定的口径记录每次迭代的特征版本号是长期可持续运行的基础。5. 常见问题与排错实录K-Means实战中那些坑5.1 聚类结果全是“均匀大饼”找不到业务解释怎么办跑一遍K-Means出来4个簇每个簇的均值跟总体均值差不多差异小到没法讲故事。这个问题出现的频率远超想象。而且它在数据上不会报任何错误你只能靠“解读不了”来发现问题。从我的排错经验看最常见的起因是特征选择太宽泛混进了大量不相关的字段。比如你把“注册设备型号编码”也塞进特征矩阵这种高基数的类别特征在标准化后会带来大量噪声把所有用户都“拉平”了。另一个常见原因是K值选得太小或太大在错误的粒度下看人群差异当然不明显。排错思路是先减少特征维度只保留最核心的5到7个行为指标再跑一次聚类看轮廓系数是否有上升。如果依然没有业务解释力就把问题的层次降到“单特征分布对比”——直接看每个簇在各个特征上的标准差找出差异最大的两三个维度围绕这几个维度重新构建故事。我在一次跨境电商项目的经验是砍掉8个冗余特征后聚类结果从“看不了”变成了“能精准定位三类人群”这说明冗余特征对聚类的影响非常实在。5.2 离群点干扰严重一个用户撑起一个簇我前面已经提过这个坑但这里值得再深入展开一下。K-Means的目标函数是SSE最小化离群点因为距离大会在计算质心时产生不成比例的影响力。当某一个用户的“90天购买金额”是5000元而其余用户的中位数只有200元时这个用户在距离计算中几乎等于“独立王国”。解决办法除了Winsorize截尾还可以在进入聚类前先用IQR四分位距检测离群值。IQR等于上四分位数减去下四分位数经验法则是高于(上四分位数1.5倍IQR)或低于(下四分位数-1.5倍IQR)的值视为离群点。对于真实业务数据直接删除极端值并不是好选择因为那些极端值可能是你最重要的“ VIP用户”。更稳的做法是单独把离群点抽出来作为“特殊人群”单独分析不参与主体聚类让常规用户的分群更清晰然后为离群点单独制定服务策略。我在“某商城”项目里就是先把“近90天消费超过5000元”的用户单独取出大概占总人数的1.2%直接归为“高净值人群”剩下的98.8%进入K-Means分群最终4个簇的业务解释都很干净。这其实就是“从算法驱动到业务规则驱动”的折中方案。5.3 K值怎么选都有理陷入选择困难症肘部法则的“肘部”有时候并不明显轮廓系数在你关注的K范围也相差不大这时你就会陷入K值选择的纠结。我的经验是多选两个K跑业务验证而不是在数学指标里死磕。具体方法是分别在K4和K6的模型下抽出任意两个簇列出它们的画像数据和典型用户路径给业务同事看问一个很简单的问题“这两个分群下的运营策略你能各写出三条吗”。如果K6时你依然能轻松写出差异化策略那说明6个簇的粒度是可接受的如果写到第四个簇时已经开始胡编乱造那说明K4更符合业务承载能力。数据指标帮你缩小范围业务解释力帮你做最终决定。还有一个小技巧把K4和K6的聚类结果做个交叉表看K6的哪些簇是从K4的某个簇里分裂出来的。如果分裂出来的两个子簇又能讲出不同的故事那K6确实更好如果只是随机切碎那说明K4最合适。5.4 标准化后还是被单一维度主导有些伙伴跑来问我“我明明做了标准化为什么跑出来还是只按某一个字段分类”这种情况通常是因为这个字段本身携带的信息量太大比如“最近一次购买距今超过XX天”这种特征可能直接等于“是否流失”它天然就把人群分成两堆。处理方式有两种。第一种是对特征做“业务约束”如果不想让聚类过度关注这个维度可以降低它的权重最直接的做法是标准化后乘一个小于1的系数。第二种是增加相似维度进行“稀释”比如你担心“购买频次”独大可以同时加入“访问频率”“加购频率”等多个相关维度让刻画“活跃度”的信息分散到多个特征中而不是押注在一个指标上。我自己更推荐第二种因为它保留的信息更完整。踩过这个坑之后我也养成了一个习惯聚类跑完后顺手跑一个各特征与聚类标签之间的相关性分析。如果某一个特征与标签的相关系数接近1就说明模型基本上是在“靠这一道题做分组”这往往不是好事需要重新调整特征组合。最后分享一点个人经验如果你正在学K-Means做用户分群我的建议是从数据清洗和特征工程入手而不是急于调参和换算法。一个模型能不能用七分在特征三分在算法。先把你手头的数据吃透想清楚每个字段能回答什么业务问题再去考虑K值、初始化、评估指标。在项目里我见过最多的情况不是模型跑不出来而是模型跑出来了但没人看得懂、更没人敢用。如果你能避免这一点就已经超过大多数人的水平。另一个经验是别追求一次做到完美。先快速跑一个能解释的版本跟业务对齐再迭代特征和参数比在后台纠结一周有效得多。市场细分不是一锤子买卖它更像是你每月对用户的一次重新认识迭代本身就是工作的常态。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑