资讯动态

Python实现Kmeans客户聚类:从RFM特征到客户分群实战

发布时间:2026/9/10 6:48:34 来源:尧图企业网站定制
简介基于Kmeans算法的客户分组项目实战资源包面向机器学习初学者与数据分析人员完整演示从问题定义、数据收集与预处理、探索性数据分析到聚类模型构建、可视化及业务应用落地的标准流程。资源共3个文件涵盖原始数据集CSV、可直接运行的Python代码以及PDF格式的说明文档说明文档对应问题定义、数据收集、预处理、探索性数据分析、聚类模型、可视化与实际应用七大模块系统讲解分析思路与代码解读。压缩包整体仅588KB轻量易下载便于快速复现适配课程设计、毕业设计或日常业务探索。目前已有5077人学习实用性广受认可。借助实战流程可掌握Kmeans聚类在客户分群中的完整实现方法学会数据标准化、K值选择、结果解读与可视化呈现并能迁移到电商、金融等真实业务场景中辅助用户细分与精准运营。1. 从“知道客户是谁”到“知道客户是哪类人”中间隔着一次聚类客户分组这件事几乎所有做会员运营、用户增长或销售分析的团队都绕不过去。最常见的做法是打标签高净值、活跃用户、沉睡用户、价格敏感型……但标签是人为定义的定义得好不好全看业务经验。而Kmeans聚类做的事情恰恰相反——它不问“你觉得客户应该分几类”而是让数据自己说话把特征空间里自然聚集的人群分开。这在机器学习项目实战中属于无监督学习里最经典、也最好落地的一类没有标注不需要人工标签只靠特征向量就能把客户划分成若干簇。本文要做的就是用Python完整实现一次Kmeans客户聚类分析从数据准备、特征标准化、K值选择到聚类结果的可视化与画像解读最终输出一份可以拿去给业务方讲清楚的“客户分组表”。适合刚接触机器学习、或者已经会用sklearn但没完整跑过项目流程的工程师。Kmeans虽然简单但在K值怎么定、特征要不要标准化、结果怎么解释这几个环节踩坑的人比比皆是。下文我会把参数、代码和边界一并讲透。2. Kmeans聚类原理与客户分组的匹配逻辑2.1 为什么客户分组场景优先选Kmeans机器学习算法里能做聚类的远不止Kmeans常用的还有层次聚类AGNES、密度聚类DBSCAN、高斯混合模型GMM。为什么客户分组这个场景Kmeans几乎是默认首选原因是客户数据本身的特点决定的——样本量大通常几万到几百万行、特征维度适中几个到几十个、期望产出的是“扁平的固定分组”每一类都能贴上一个业务标签这三点和Kmeans的能力边界正好吻合。DBSCAN擅长发现任意形状的簇且能识别噪声点但当客户数据密度不均匀时很难找到一对适合全数据集的min_samples和eps参数调参成本会高到不划算。层次聚类能产出树状结构供层次化分析但计算量是O(n²log n)级别几十万客户会直接卡到怀疑人生。而Kmeans的时间复杂度接近O(n·k·d)配合sklearn的k-means初始化即便百万行级别做十次迭代也基本在秒级完成。另一个从算法本身的考量是Kmeans是“硬聚类”每个样本自上而下地归属于唯一个簇。客户运营的现状绝大多数就是用固定分组去设计差异化策略——这个月给A组发券、给B组推新品、给C组做唤醒正好是硬边界的逻辑。而GMM这类软聚类虽然能给出归属概率但对运营系统的落地而言反而过于复杂你还要额外在概率之上再设一条阈值才能转成实际分组。2.2 Kmeans目标函数与“距离最近”的真正含义Kmeans没有标签它的学习目标是让簇内的样本彼此尽量接近簇间的样本尽量远离。形式化的目标函数是让总簇内平方和WCSS也叫inertia最小J \sum_{i1}^{k} \sum_{x_j \in C_i} ||x_j - \mu_i||^2其中C_i是第i个簇μ_i是该簇的质心簇内所有样本的均值向量||x_j - μ_i||²是欧氏距离的平方。整个迭代过程就是反复执行两步把样本分给离它最近的质心再用新簇的样本均值更新质心。这两步交替进行直到质心不再显著移动或inertia变化小于阈值。这里要特别提防一个误区Kmeans用的是欧氏距离不是余弦相似度也不是曼哈顿距离。欧氏距离对特征的量纲高度敏感——如果客户数据里有“年消费金额”万元级和“购买频次”个位数两类特征未经处理时金额维度算出的距离差会彻底淹没频次维度。这也是本文第3章要把特征标准化放在所有步骤之前的根本原因否则Kmeans聚出来的分组基本等于只按金额一维在切。训练过程中还有两个sklearn默认参数值得留意n_init10表示会用10组不同的随机质心初始化去分别跑最终返回inertia最小的那一组结果initk-means表示初始质心会尽量分散避免收敛到局部最优。现在完全可以认为k-means加多次初始化已经是行业标配手动随机初始化导致的结果不稳定在现代实践中已经很少作为踩坑来源了。3. Python做Kmeans客户聚类的最小完整实现3.1 确立特征体系客户分组用什么数据要做客户聚类第一步是明确用什么数据来代表一个客户。这不是纯粹的技术问题而是业务和特征工程的交界处。业内最通用的做法是用RFM模型作为基础——最近一次消费时间Recency、消费频率Frequency、消费金额Monetary。在Kmeans的项目里R、F、M刚好是3个连续数值型特征天然适合欧氏距离计算。我一般会在这个基础之上根据业务再加一两个特征比如“平均客单价”或“购买品类数”。但要注意一个原则特征不是越多越好。Kmeans在高维空间里会出现所谓的“维度灾难”——特征太多时样本间的距离趋于均匀聚类的区分度反而下降。对于客户分组场景48个特征是一个比较稳妥的区间再多就需要先做PCA降维了。下面用一个模拟的电商客户数据来演示完整流程。数据包含3万行客户记录每行代表一个客户字段包括Recency最近一次消费距今天数天数越小越活跃、Frequency过去一年购买次数、Monetary过去一年消费总金额。3.2 代码骨架从标准化到Kmeans训练import pandas as pd import numpy as np from sklearn.preprocessing import StandardScaler from sklearn.cluster import KMeans # 生成模拟客户数据3万行3个RFM特征 np.random.seed(42) data pd.DataFrame({ Recency: np.random.gamma(shape2, scale15, size30000).astype(int) 1, Frequency: np.random.gamma(shape2, scale5, size30000).astype(int) 1, Monetary: np.random.gamma(shape3, scale200, size30000).astype(int) 10 }) # 手动制造一小群高价值客户让聚类结果更好解释 high_value_idx np.random.choice(30000, 800, replaceFalse) data.loc[high_value_idx, Frequency] np.random.randint(10, 30, 800) data.loc[high_value_idx, Monetary] np.random.randint(2000, 8000, 800) # 1. 特征标准化对连续数值特征做Z-score归一化 features [Recency, Frequency, Monetary] scaler StandardScaler() X_scaled scaler.fit_transform(data[features]) # 2. 训练Kmeans模型k4固定随机种子保证可复现 kmeans KMeans(n_clusters4, initk-means, n_init10, random_state42) data[Cluster] kmeans.fit_predict(X_scaled) # 3. 查看每个簇的样本量与特征均值用于后续画像 cluster_profile data.groupby(Cluster)[features].mean().round(2) print(cluster_profile) print(data[Cluster].value_counts().sort_index())逻辑说明第1步中fit_transform会同时完成“计算均值方差”和“转换数据”两步。X_scaled的每一列均值约为0、标准差约为1第2步fit_predict训练模型后直接把每个样本打上簇标签得到03的编号第3步用groupby按簇聚合输出的就是每个簇的中心特征值这是后续做客户画像的核心依据。这里解释两个关键参数:n_init10意味着Kmeans会跑10轮完整迭代每轮用不同的随机起点最终选取簇内误差最小的那一轮结果这个参数直接消解了初始质心随机性的影响;random_state42则是固定随机种子保证多次运行结果一致这在项目汇报和复现实验时极其重要。参数max_iter保持默认300即可因为它只是单轮内允许的最大迭代次数绝大多数现实数据集在几十次内就能收敛300次完全不会形成瓶颈。3.3 聚类效果的第一眼验证拿到上面的输出结果后直接看一下簇样本量有没有极端失衡比如某个簇只剩几十个样本另一个簇占了90%以及各簇在维度均值上是否有明显区分。正常情况下K4的输出应该呈现“簇间均值差异大、簇内样本量相对均衡”的格局。如果发现某个簇几乎把所有样本都吸走了大概率是特征没有标准化或者数据里存在极端离群点。提示如果某个簇的样本量占比低于总体的3%这个簇大概率是离群点簇后续画像和策略制定会非常困难通常回到数据预处理阶段去处理极端值。否定式的判断也有必要说说。聚类结果不是“分得越散越好”有时两个簇的Recency均值几乎相同但Monetary差异很大这个结果依然是有意义的——它们代表了不同的消费力水平。不要只看单维指标要三个维度放在一起读。4. 用肘部法则与轮廓系数确定K值并评估聚类质量4.1 百试不爽的K值区间和肘部法则公式Kmeans最大的“超参数陷阱”就是K值。K设太小分组过粗“高价值但低频”和“低价值低频”会被塞进同一类K设太大每个簇的特征差异变得琐碎无法形成清晰的运营策略。实践中用的方法是画一条inertia随K变化的曲线——这就是“肘部法则”。肘部法则的核心逻辑是随着K增加簇内平方和必然下降但这个下降速度在某个K值处会产生显著放缓这个拐点就是理想的K。import matplotlib.pyplot as plt from sklearn.cluster import KMeans inertia_list [] K_range range(2, 11) for k in K_range: km KMeans(n_clustersk, initk-means, n_init10, random_state42) km.fit(X_scaled) inertia_list.append(km.inertia_) plt.figure(figsize(8, 5)) plt.plot(list(K_range), inertia_list, markero) plt.xlabel(Number of clusters (K)) plt.ylabel(Sum of squared distances (inertia)) plt.title(Elbow Method for Optimal K) plt.grid(True) plt.savefig(elbow_plot.png, dpi120)注意fit方法对无监督聚类而言其实就是训练训练后通过km.inertia_直接取出当前K值下损失函数的值这个属性不需要再额外计算。脚本运行后看x轴的“拐点位置”如果K从2到4时inertia急速下滑K从5开始掉得很平缓K4或5就是选择区间。这里要明确一个原则肘部法的拐点是“批量出现”而不是唯一确定的点所以通常还要配合轮廓系数来决定最终值。4.2 轮廓系数量化“簇内紧、簇间散”肘部法则看的是损失函数的全局下降而轮廓系数Silhouette Coefficient从单个样本出发做评估——每个样本与自身簇内样本的平均距离记为a与其他簇样本的最小平均距离记为b该样本的轮廓系数为s \frac{b - a}{\max(a, b)}s的取值范围落在[-1, 1]接近1意味着这个样本被分得很干净身边全是自己人、离其他簇远得很接近0说明它正好卡在两个簇的边界上负值表示这个样本可能被分错了。把所有样本的s取均值就得到整体聚类质量的评估。from sklearn.metrics import silhouette_score for k in [3, 4, 5]: km KMeans(n_clustersk, initk-means, n_init10, random_state42) labels km.fit_predict(X_scaled) sil silhouette_score(X_scaled, labels) print(fK{k}, silhouette_score{sil:.4f})silhouette_score接收两个参数标准化后的特征矩阵X_scaled和Kmeans给出的簇标签labels。它内部会自己计算上述的a和b不需要人工干预。K3、4、5的分数一出来结合业务背景的综合判断如下分数最高并不一定是最佳K。轮廓系数的得分在0.25到0.5之间已经属于“结构可辨识”的范围如果强行追求0.6以上留下的往往是把离群点单独拆成簇的结果反而对运营无益。4.3 业务验证K值不只是统计量最后一条选K的标尺来自业务本身——算出每个簇的RFM均值之后验证是不是每一簇都能用一句业务语言来描述。K4的典型效果是ClusterRecency均值(天)Frequency均值(次)Monetary均值(元)业务标签0283.2530新客1761.8240沉睡客户21226.55200高价值活跃客户3477.41100成长型客户如果聚类结果无法映射出“新客、沉睡、高价值、成长”这类可分策略的标签就说明K值选择虽然数学上成立但业务解释力不够应当回退几步重新调K或调整特征范围。5. 聚类结果的可视化与客户分组画像输出5.1 用PCA降维到2D做散点图3维以上的聚类结果没法直接画图看最常见的展示手法是先用主成分分析PCA把特征降到2维再用两个主成分作为x、y轴绘制散点。需要明确的是PCA是展示工具而不是聚类特征的一部分聚类过程依然使用原始特征PCA降维只发生在绘制结果之后。from sklearn.decomposition import PCA import matplotlib.pyplot as plt pca PCA(n_components2) X_pca pca.fit_transform(X_scaled) plt.figure(figsize(10, 7)) scatter plt.scatter(X_pca[:, 0], X_pca[:, 1], cdata[Cluster], cmapviridis, s8, alpha0.6) plt.colorbar(scatter, labelCluster) plt.xlabel(Principal Component 1) plt.ylabel(Principal Component 2) plt.title(Customer Clusters Visualized via PCA) plt.savefig(customer_clusters.png, dpi120)fit_transform在这里会通过线性映射把3个原始特征的信息压缩到2个新轴上Principal Component 1即保留了数据方差最大方向的方向向量投影。画完图之后可以观察到如果同一簇的颜色点聚成一块致密区域说明数据结构上这组客户确实扎堆。如果所有颜色完全混在一起看不出边界并不代表聚类绝对失败更可能是特征本身区分度不够应当回去检查特征设计并在原始空间中继续推理。现实里Kmeans聚类可视化的目的往往不是为了证明效果而是向非技术同事提供一个直观的认知——在最终汇报的PPT里这图能明显提升说服力。5.2 一键导出客户分组表聚类分析最终落地的对象是一张带簇标签的明细表这张表可以直接灌进CRM系统或运营后台做后续的动作配置。导出表的方式以及注意点是# 输出客户明细及对应的分组标签 output data[[Recency, Frequency, Monetary, Cluster]].copy() # 给每个簇统一重命名便于业务方阅读 cluster_names {0: 新客, 1: 沉睡客户, 2: 高价值活跃客户, 3: 成长型客户} output[Customer_Segment] output[Cluster].map(cluster_names) output.to_csv(customer_segments.csv, indexFalse, encodingutf-8-sig) print(客户分组表已导出,共, len(output), 条记录)使用encodingutf-8-sig导出是为了让Excel直接打开时不出现中文乱码这是数据分析里一个非常普遍的细节坑。map函数无需额外说明它只能做等值替换如果以后要在上线前对标签做细粒度的规则合并建议在SQL里做JOIN而不是在map里堆条件。5.3 三类特征的描述性统计交叉表除了scatter和CSV聚类结果在报告场景里还需要一张特征分布汇总表。用pandas的groupby describe就能快速生成更完整的东西profile data.groupby(Cluster)[[Recency, Frequency, Monetary]].describe().T print(profile)这个输出每一行是一个特征针对某个簇下的多个分位数描述能够迅速暴露“同一性能指标下某个簇的方差特别大”这类问题。此外也可以用pd.crosstab看聚类结果与性别、地域、渠道等非建模特征的交叉关系进一步丰富分组画像的维度。这种做法不会让聚类更有说服力但它能让业务方在做运营策略时有更多抓手。6. 聚焦实操边界Kmeans客户分组的4个高频性能和细节坑与两套精进解法6.1 特征不做标准化直接聚类在客户数据里哪怕有一点主观感觉“金额才是最重要的”也不能让Kmeans直接看原始值——欧氏距离对绝对数值大的维度天然强势。RFC三特征必须使用StandardScaler或MinMaxScaler其中之一反过来如果做了标准化后聚类结果仍然呈现“一整列都在支配分组”建议检查原始特征是否有异常值集群。处理手法是对所有连续型RFM类型的变量统一走StandardScaler如果已经做成分箱或one-hot后的离散值则不再重复标准化。6.2 离群点把质心拖走Kmeans的质心是簇内全部样本的平均值单一极端异常值能直接改变整个簇的位置。客户数据里那些“一年消费千万”的极值用户会拉出一个极小簇或者让邻近样本被误划分。在正式建模前建议做一步IQR剔除或截尾处理标准做法是把Monetary的分位数小于1%和大于99%的样本截断到边界值。删除样本会导致业务报表口径对不上账所以在真实项目里通常选择截尾而不是删除。6.3 Kmeans只吃数值特征这是最常被新手忽略的理解细节。客户的性别、所在城市、注册渠道这类字符型特征不能直接丢进KMeans()里因为sklearn内部不会自动做任何编码强行放入会直接报类型错误如果手动做了整数映射0、1、2之间会被强行解读为“数值距离”并扭曲聚类结果。对这类类别特征要么在预处理层完成one-hot后交给Kmeans要么干脆把它排除在模型之外只当描述标签使用。6.4 不要忽略聚类稳定性验证Kmeans对初始质心敏感的特性虽然在n_init10后大幅缓解但对离群点仍然不够稳健。反复运行后对比不同随机种子下的聚类结果常用的一致性判断指标是ARIAdjusted Rand Index代码很短from sklearn.metrics import adjusted_rand_score km1 KMeans(n_clusters4, random_state0).fit_predict(X_scaled) km2 KMeans(n_clusters4, random_state1).fit_predict(X_scaled) print(ARI between two runs:, adjusted_rand_score(km1, km2))ARI能稳定落在0.9以上说明聚类结构具备可复现性如果只有0.5或更低那么结果高度依赖随机种子K值或特征设计需要被整体重审。6.5 两套精进解法Mini-Batch KMeans与层次聚类当客户规模达到百万级时标准Kmeans每次迭代都要完整扫描所有样本内存和时耗都会明显上升。代替方案是scikit-learn里的MiniBatchKMeans它每次只随机抽取一小批样本更新质心其余完全一致from sklearn.cluster import MiniBatchKMeans mbk MiniBatchKMeans(n_clusters4, batch_size1024, random_state42) data[Cluster_MB] mbk.fit_predict(X_scaled)batch_size1024表示优化时每轮子抽样1024条样本在大数据量上跑出来的cluster质心和标准Kmeans差距很小速度却有成倍的提升。若业务上想看到“大客户群内部还有小群体”这种嵌套结构可以配合层次聚类AGNES先跑一个树状图看大概分层再用Kmeans的簇编号去做二次细分这一步的前提仍然是所有连续的数值都经过统一的标准化流程。本文还有配套的精品资源点击获取

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

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

免费获取报价