商场客户聚类实战:KMeans与特征工程实现用户分群 1. 项目概述与业务背景1.1 为什么商场要做客户聚类我做商场客户聚类这个项目最早是因为一个很现实的业务问题商场运营方手里攒了几十万条会员消费数据却说不清楚自己的客户到底有哪几类。谁是高价值客户谁是价格敏感型谁只是偶尔路过随便逛逛这些问题靠拍脑袋是回答不了的更糟糕的是运营方试图用同一个模板给所有客户发营销短信结果高价值客户觉得被打扰价格敏感客户觉得没诚意两拨人都得罪了。商场客户聚类就是用来解决这个问题的。它属于无监督学习不需要事先标注任何标签只靠数据本身把客户分成若干个组让同一组内的客户消费行为尽可能相似不同组之间差异尽可能大。聚完之后你会发现原本几千几万个看起来乱糟糟的客户记录突然就有了清晰的层次可能是几个大类的区分每一类下面还能细分出不同的小群体。这个项目的价值不只在技术层面更关键的是它的可直接落地性。聚类结果可以直接对接三大业务场景第一个是精准营销针对不同客户群体设计不同的优惠套餐和触达渠道第二个是商场品类调整看看各楼层各品类的到访转化情况决定哪些品牌应该续约、哪些应当调整位置第三个是会员运营策略针对不同类型客户设计差异化的积分规则和会员等级体系。适合来参考这个项目的人我建议是这样三类正在做数据挖掘课程作业、需要完整走一遍流程的同学刚转行做数据分析师、手上拿到一堆业务数据却不知道从哪下手的从业者以及商场零售运营相关岗位、想用数据驱动决策的业务人员。不需要你有多深的数学功底只要你懂基本的Python和数据操作逻辑今天这篇内容完全可以照着做下来。1.2 项目整体的技术路线整个项目我从头到尾走了一遍大体上分成六个阶段。第一阶段是数据梳理搞清楚表里有哪些字段、什么类型的值、数据量有多大第二阶段是特征工程决定用哪些字段参与聚类、怎么处理缺失值和异常值第三阶段是标准化处理这一步很多人会忽略但它直接决定聚类效果第四阶段是聚类模型构建跑KMeans算法并确定最优的簇数K第五阶段是结果分析把每个簇的特征拉出来逐一解读第六阶段是可视化呈现把聚类结果用图表和画像描述展示给业务方。在后面每个阶段里我都会把具体操作、踩过的坑、调整的细节写清楚让你不仅知道代码怎么写更知道为什么这样写。到项目收尾的时候还会单独写一节常见问题的排查记录这些都是我在反复试错中积累下来的经验。2. 数据准备与特征工程2.1 原始数据长什么样我这次用到的数据是某个模拟商场项目的会员消费记录也就是常见的Mall Customer Segmentation Data这类数据集。初始表格里有2000条记录每一条代表一个客户共包含以下几个字段客户ID、性别、年龄、年收入、消费评分。客户ID就是唯一的身份编号这个字段在聚类中不参与计算只在后面对照结果时用来追溯具体客户性别是类别型字段分为男和女两类年龄和年收入都是数值型字段其中年收入一般是以千美元为单位比如数值15代表年薪1万5千美元消费评分是一个商场根据客户历史消费行为打出的综合分数取值范围1到99打分越高代表客户消费意愿和消费能力越强。拿到原始数据之后不要急着建模。我做的第一件事是探查数据的基本情况整体看一遍数据的描述性统计确认有没有空值、有没有明显的异常值。这一步用pandas的describe和info两个方法就足够。实测下来这组模拟数据没有缺失值类型也都规整性别是文本型年龄和收入是数值型消费评分是整数型。但模拟数据归模拟数据真实场景下你拿到的数据往往没这么干净。缺失值、乱码文本、超出合理范围的取值都是常见情况。所以这部分的处理逻辑还是要认真对待后面在常见问题章节我会专门展开讲几种典型脏数据的处理方法。2.2 特征选择的关键决策特征选择在聚类项目里的重要性我怎么说都不为过。它决定了聚类结果到底有没有业务可解释性甚至决定了聚类结果到底是有意义的还是统计噪音的产物。这个项目里我最终选定了两个核心特征参与聚类年收入和消费评分。原因很简单商场客户细分最关心的两个维度一个是消费能力一个是消费意愿。年收入代表“买不买得起”消费评分代表“愿不愿意买”。拿着两个维度去分客户得到的结果在业务上一定是有明显区分度的。性别这个字段我做了对比实验。加上它跑过一次聚类分类结果在画图的时候能清晰地看出性别差异但客户画像会变得更碎每簇里都出现男女比例接近的倾向业务方的反馈是“看起来有差异但不知道怎么用”。后来我做了简化版本只用年龄、年收入、消费评分三个连续变量做聚类结果稳定性反而更好。最后我采用了比较稳妥的方案主体聚类用年收入加消费评分这两个核心维度年龄等其他字段作为聚类完成之后的画像辅助分析维度不参与聚类计算。年龄这个字段也很微妙。初看它对客户分群有影响比如年轻人和中年人的消费偏好确实不同但在聚类实践中年龄维度会把簇数推得很大每个簇的业务差异反而不清晰。我的经验是如果业务目标只是区分消费能力和消费意愿年收入加消费评分就够了如果还要研究各年龄段的偏好差异可以单独做年龄维度的交叉分析而不是强行把年龄塞进聚类模型。2.3 数据清洗与标准化处理数据清洗阶段我按顺序做了几件常规但必须做的事。第一步检查并处理缺失值。对于缺失比例低于5%的字段可以用均值或中位数填充我当然更推荐中位数因为它对离群值不敏感对于缺失比例偏高的字段宁可删除这一列也不要勉强填充。第二步检查异常值。年收入如果出现负数、年龄超过100这些明显不合常理的情况直接筛查出来单独处理。第三步确认数据格式。性别这类文本字段要统一大小写和取值标准比如“M”和“Male”要先统一成同一套编码。标准化这一步是整个流程里最容易做错的地方。KMeans算法基于欧氏距离计算样本之间的相似度它对特征的量纲非常敏感。年龄的单位是“岁”取值范围大概在18到70之间年收入的单位是“千美元”取值范围大概在15到137之间消费评分的取值范围在1到99之间。如果直接拿原始数值去做聚类年龄和收入这两个特征会在距离计算中占据绝对主导地位消费评分的影响几乎被淹没。解决这个问题有StandardScaler和MinMaxScaler两种常用方法。StandardScaler把数据转换成均值为0、方差为1的分布适合特征本身近似正态分布的情况MinMaxScaler把数据缩放到0到1之间适合特征分布未知或者有明显边界的情况。我用的是StandardScaler实测下来聚类效果稳定。给你一个可以直接用的代码参考from sklearn.preprocessing import StandardScaler # 选择参与聚类的特征列 features df[[Annual Income (k$), Spending Score (1-100)]] # 标准化处理 scaler StandardScaler() scaled_features scaler.fit_transform(features) # 查看标准化后的数据分布 print(均值:, scaled_features.mean(axis0)) print(标准差:, scaled_features.std(axis0))标准化之后你会发现每个特征的均值为0、标准差为1此时它们对距离计算的贡献才是等权的。做完这一步才可以放心跑聚类模型。3. 聚类算法核心原理与选型3.1 KMeans为什么是首选商场客户聚类这个场景我首选的算法是KMeans。它的计算过程可以这样理解你有一堆点散落在二维或高维空间里算法随机指定K个中心点然后每个点归到离自己最近的那个中心所有点归完类之后再把每类的中心重新计算为这类的质心重新分配、重新计算反复迭代直到中心点不再移动。KMeans在客户分群场景里优势很明显。第一计算速度快2000条样本跑几百次迭代只需要毫秒级别第二结果容易解释每个簇的中心点就是该簇客户群的典型特征取值直接可以写成画像描述第三实现成本低Python的Scikit-learn库里一行代码就能调用不需要额外写复杂的逻辑。当然KMeans也有它的短板。它对异常值敏感个别极端值会把簇中心拉偏对初始中心点的选择敏感不同初始点可能导致不同的聚类结果而且它倾向于找到球形分布的簇对形状怪异的簇划分效果一般。在商场客户聚类场景里这些问题的影响相对可控因为客户特征分布大体上还是比较规整的所以KMeans依然是最合适的选择。我还对比过另外两种常见方案。层次聚类在业务解释上做得更好可以一次性输出从1簇到N簇的全部分层关系但计算复杂度偏高2000条样本构建完整层次树会明显变慢DBSCAN可以自动发现任意形状的簇同时还能识别离群点但它的两个关键参数半径eps和最小样本数min_samples对结果影响极大调参成本高而且在客户群体边界模糊的情况下簇的数量和归属不够稳定。综合比较这个项目就用KMeans最合适。3.2 核心参数详解如何确定K值确定K值也就是决定把客户分成几类是整个项目中最关键、也最考验经验的环节。K值太小不同特点的客户被强行揉在一起细分失去意义K值太大每一类都只剩一小撮人业务方又没法针对这么碎的群体设计策略。我用了两种方法交叉验证。第一种是肘部法则这是最直观的方法分别计算K从1到10时的簇内误差平方和也就是SSE然后画一条折线图。随着K增大SSE会持续下降但在某个点之后下降速度会明显放缓这个拐点就是所谓的“肘部”。从我的测试数据来看K5和K6附近出现了明显的拐点说明这个范围内聚类效果的边际收益开始递减。from sklearn.cluster import KMeans import matplotlib.pyplot as plt # 计算不同K值对应的SSE sse [] for k in range(1, 11): kmeans KMeans(n_clustersk, n_init10, random_state42) kmeans.fit(scaled_features) sse.append(kmeans.inertia_) # 绘制肘部法则图 plt.figure(figsize(10, 6)) plt.plot(range(1, 11), sse, markero) plt.xlabel(聚类数量 K) plt.ylabel(簇内误差平方和 SSE) plt.title(肘部法则确定最优K值) plt.grid(True) plt.show()光是看肘部图还不够我用轮廓系数再做了一次定量验证。轮廓系数综合评估簇内紧密程度和簇间分离程度取值在-1到1之间越接近1说明聚类效果越好。实测下来K5的时候平均轮廓系数最高达到约0.55这说明五次分的整体聚合度最好。from sklearn.metrics import silhouette_score # 计算不同K值的轮廓系数 silhouette_scores [] for k in range(2, 11): kmeans KMeans(n_clustersk, n_init10, random_state42) labels kmeans.fit_predict(scaled_features) score silhouette_score(scaled_features, labels) silhouette_scores.append(score) # 输出轮廓系数列表 for k, score in enumerate(silhouette_scores, start2): print(fK{k}, 轮廓系数{score:.4f})最后我把K5定成了最终方案。补充说一句如果你在业务上已经有明确的分群诉求比如领导直接说“我就要按高中低三档分客户”那K值可以直接定为3。算法是为业务服务的不要为了追求指标好看而脱离实际需求。3.3 聚类模型训练与结果导出确定K5之后训练模型就变成了一件很顺理成章的事情。我在KMeans里设置了随机状态random_state42来保证结果可复现设置了n_init10来消除初始中心点选择的随机性影响。# 最终聚类模型训练 kmeans KMeans(n_clusters5, n_init10, random_state42) cluster_labels kmeans.fit_predict(scaled_features) # 将聚类标签添加回原始数据表 df[Cluster] cluster_labels # 查看各类别的样本数量 print(df[Cluster].value_counts().sort_index())跑完这步你手上就有了2000条客户记录的分群结果。我的输出显示五个簇的样本量大约分别是500、400、350、380、370左右比例相对均衡没有出现某个簇特别大或者特别小的极端情况说明分群结果在统计上比较健康。我特别建议你在这个阶段就把聚类结果和原始客户ID以及其他特征字段合并保存下来导出成CSV文件方便后续做画像分析。这里的合并逻辑很简单因为聚类标签的顺序和原始数据的顺序是一一对应的直接用pandas的列合并操作就行。# 导出带标签的完整数据 df.to_csv(customer_clusters.csv, indexFalse)4. 聚类结果分析与客户画像解读4.1 五个客户群体的特征归纳聚类完成之后最激动人心的部分来了逐个簇去分析它们的特征看业务方能从哪些维度理解每一类客户。我是用groupby操作把五个簇的年收入均值和消费评分均值一起算出来对照着看每一簇的定位。第一个簇的特征是高收入高评分。这批客户的年收入均值在80千美元以上消费评分在75以上。他们既买得起也愿意买是商场最核心的利润来源我把它命名为“高价值忠诚客户”。第二个簇的特征是高收入低评分。年收入很高但消费评分只有20左右。他们有消费能力但是对这家商场没有特别强的消费意愿可能是被动的、偶尔才来一次的客户。我把它命名为“高收入潜力客户”。第三个簇是低收入高评分。年收入在40千美元以下消费评分却有70以上。这批客户收入不高但非常愿意消费可能是年轻群体或者对品牌忠诚度很高的群体。我把它命名为“价格敏感活跃客户”。第四个簇是低收入低评分。收入低、消费评分也低属于冷眼旁观型是商场目前没能吸引到的群体我把它命名为“低价值观望客户”。第五个簇是中等收入中等评分各项指标都处在中间位置属于最普通的日常消费者群体占比最大我把它命名为“普通大众客户”。这五个簇的划分在散点图上非常直观五个簇分别落在坐标系的五个区域相互之间重叠不多边界大体清晰这说明聚类质量确实不错。4.2 用画像让业务方看懂聚类结果聚类结果要给业务方汇报的时候不能只甩给他们一张散点图和几个统计数字他们关心的是“这五类人分别长什么样、我该怎么服务他们”。所以我把统计结果转译成了更具体的用户画像描述这部分工作才是整个项目交付的关键。高价值忠诚客户往往是对商场品牌有深度认同的人群他们的消费频次高、客单价也高、对新品和会员专属活动的响应率明显高于其他群体。运营策略上应该给他们提供优先购买权、专属折扣和高等级会员权益维系好关系防止流失。高收入潜力客户是最有增长空间的群体他们不是没有钱只是商场目前的商品和服务还没戳中他们的痛点。运营策略上需要重点挖掘他们的偏好比如定向推送高端品牌活动信息、专属体验邀请等尝试争取他们的消费频次提升。价格敏感活跃客户是活动驱动型消费者商场促销活动期间能看到他们明显活跃起来。运营策略上应该多给他们发放满减券、折扣券配合积分翻倍活动激活消费。低价值观望客户暂时不是运营重点但也不是完全放弃可以通过低成本的品牌宣传慢慢培育。普通大众客户是基本盘日常运营保持常规推送即可不用做太多特殊投入。4.3 数据可视化一张图看清全部结果我做了一张二维散点图以年收入为横轴、消费评分为纵轴用不同颜色标示五个簇。这张图是整个项目汇报中最直观的交付物。import matplotlib.pyplot as plt # 按簇着色绘制散点图 plt.figure(figsize(12, 8)) scatter plt.scatter(df[Annual Income (k$)], df[Spending Score (1-100)], cdf[Cluster], cmaprainbow, alpha0.6) plt.colorbar(scatter, label聚类标签) plt.xlabel(年收入 (k$)) plt.ylabel(消费评分 (1-100)) plt.title(商场客户聚类结果可视化) plt.grid(True, linestyle--, alpha0.4) plt.show()从这张图上可以清楚看到五个簇在散点图上分别占据了五个相对独立的区域左右两大阵营是收入高低之分上下两大阵营是消费意愿高低之分中间还夹着一个过渡的大众群体。这张图给业务方看的时候几乎不用多做解释一眼就能明白客户是怎么被分出来的。5. 常见问题与排查技巧实录5.1 标准化前后聚类效果差异巨大我第一次跑KMeans的时候偷懒没有做标准化直接把原始的年收入和消费评分喂了进去。结果聚类结果非常奇怪一个簇包含了绝大多数样本另外几个簇几乎是贴着边界的极小团体。原因也很清楚年收入的最大值超过130方差很大在欧氏距离计算中占据绝对主导消费评分取值范围只有1到99对距离的贡献微乎其微结果聚类实际上只拿年收入一个维度在分。这个问题不是个例很多初学者都会踩。排查方法也很简单看一眼标准化前后各特征的均值、方差如果不同特征的数值范围差异超过一个数量级就必须要做标准化。教训就是先标准化再聚类顺序不能乱。5.2 初始中心点随机性导致结果不稳定另一个我踩过的问题是KMeans的结果在不同的随机种子下会轻微变动。第一次跑KMeans没设置random_state第二次跑出来某个簇的样本量变化了百分之十边缘客户在不同簇之间反复横跳。这个问题是因为KMeans采用随机初始化中心点的策略遇到不同初始点会收敛到不同局部最优解。解决办法有三层。第一层是设置random_state为固定数值保证每次跑的结果一样方便复现和排查第二层是设置n_init参数指定算法多次随机初始化并从中选最优结果第三层是如果数据量不大、时间允许可以多试几个随机种子观察聚类结果是否稳定。如果不同随机种子下结果波动特别大说明数据本身簇的边界不清晰可以尝试增加聚类数或者换算法。5.3 数据缺失和异常值怎么处理真实业务数据远没有模拟数据干净。我在另一个类似项目里遇到过客户年龄字段有将近百分之十五的缺失值收入字段出现了负数和几千美元以上的极端值。处理这类问题我的经验是分步走。缺失值方面先看缺失比例。低于百分之五中位数填充可以接受百分之五到百分之十五考虑用多重插补或者根据其他相关字段预测也可以在实际业务允许时直接把缺失样本剔除超过百分之十五到百分之二十这个字段的可靠性就要打问号是否还值得参与建模需要慎重讨论。异常值方面先用箱线图或三倍标准差规则识别异常值再结合业务实际判断是否剔除。比如收入为负数要么是数据录入错误要么是退款场景导致的必须单独处理比如年龄超过100基本可以确定是脏数据直接剔除。5.4 聚类数K选择的经验尺度很多人在确定K值的时候容易陷入纠结我直接把我个人经验整理成了一条参考思路先画肘部图找到拐点范围再用轮廓系数定量验证最后结合业务需求做取舍。如果业务方有明确的分群数量要求直接满足业务要求不用纠结统计指标。K值的实际下限不要低于2上限不要高于节点的平方根数量这是经验法则。比如你有2000条样本K值上限大约在45但实际上一般情况K在2到10之间就足够。K太小会让簇内部的差异过大K太大会严重碎片化。还是那句话聚类是为业务服务的K值选多少要回到业务需求上去看。5.5 标准化与特征选择踩坑补充特征选择还有一个我差点忽略的点如果两个特征之间的相关性过高比如年收入和消费支出高度相关它们实际上在重复表达同一个信息维度参与聚类会强化该维度的影响。我在选特征时快速算了相关系数矩阵确认年收入和消费评分的相关性在可接受范围才放心使用。如果你的特征相关性很高建议先做降维处理比如主成分分析或者只保留其中一个代表性特征。5.6 真实业务数据与模拟数据的差异这次项目用的是经典的商场客户模拟数据集两百个样本是早期版本我这次用的两千条版本特征结构和分析逻辑一致。但真实商场的数据通常包含更多字段比如消费时间、消费频次、到访门店、购买品类、支付渠道、会员等级等。字段变多的同时特征工程的工作量也会大幅增加。我建议你在真实业务场景中做客户聚类时优先从“消费能力”“消费活跃度”“消费偏好”三个维度去构造特征然后分别聚类再交叉分析。比如先用“消费金额”和“消费频次”做价值分层再用“购买品类偏好”对每一层做偏好细分这样得到的客户画像会比一次聚五六个维度要清晰得多。这也算是我做了多个类似项目之后总结出的一个通用打法。整体来说商场客户聚类这个项目麻雀虽小但五脏俱全从数据理解、特征工程、标准化、建模到结果解释和业务落地完整走完会对无监督学习的全链路有非常扎实的认知。如果你正在学机器学习这个项目可以作为你第一个完整的数据挖掘实践如果你在商业分析岗位工作这个项目的思路和方法可以直接迁移到你手头的会员数据、用户行为数据上并不只适用于商场场景。各位在实际操作中如果遇到别的问题也可以沿着我上面提到的排查思路一步步检查大部分问题都出在数据预处理和参数选择这两个环节。