K-Means结合RFM实现用户精准分群的Python实践
2026/9/10 9:03:51 网站建设 项目流程

简介:本资源是一份面向数据分析初学者与电商运营人员的实战型顾客价值分析方案,聚焦于K-Means聚类与RFM模型在用户分群与消费行为挖掘中的联合应用。资源提供从原始订单数据清洗、时序趋势建模(含SARIMA预测)、消费者画像构建到RFM维度打分的全流程Python实现,覆盖数据逻辑校验、多维可视化(9张分析图表)及可复用的聚类评估逻辑。压缩包共13个文件,包含核心分析脚本main.py、原始订单CSV数据、5类用户对比分析结果图(png)、结构化说明文档(md)及配置文件(json),整体仅906KB,轻量易部署。已有566人学习下载,读者可直接运行代码复现完整分析链路,获取含时间序列分解图、聚类轮廓系数曲线、RFM四象限矩阵等关键产出,并参考readme.md快速理解各模块设计意图与参数调优思路。

1. 为什么用 K-Means + RFM 做顾客分群,比单纯看“最近购买”更准?

某电商运营发现:后台导出的“近30天活跃用户”名单里,有大量下单金额不足50元、客单价极低的用户;而真正贡献70%GMV的高价值客户,反而因购买周期长(如母婴类客户每2个月复购一次)被系统自动过滤。这不是数据不准,而是传统时间窗口法缺乏对用户行为维度的结构性刻画。本项目编号 500010102 提供了一套可落地的 Python 实现路径:先用 RFM 模型将原始订单数据(orders_data.csv)转化为 R(最近一次消费距今时长)、F(消费频次)、M(消费金额)三个可量化的数值指标,再通过 K-Means 聚类算法在三维空间中自动识别用户自然分群——不是按运营主观定义“高/中/低”,而是让数据自己说话。整个流程封装在 main.py 中,含完整数据清洗、RFM 标准化、肘部法则确定最优聚类数(k=5)、聚类结果可视化(Figure_*.png)及五类用户画像对比表。适合有 Python 基础、正处理真实销售数据的分析师或数据工程师,尤其适用于订单结构清晰但用户生命周期波动大的零售、SaaS 订阅类业务。


2. RFM 指标构建与标准化:从原始订单到可聚类的三维向量

RFM 不是简单统计字段,而是对用户行为逻辑的数学转译。本项目中 orders_data.csv 包含 order_id、customer_id、order_date、amount 四个核心字段,需严格按时间粒度和业务语义生成 R、F、M。关键在于:R 必须是“距今时长”而非“订单日期”,F 是去重后的订单次数(非商品件数),M 是该用户所有订单金额总和(非平均值)。若直接使用原始数值进行 K-Means,R 的单位是“天”(量级百位),M 的单位是“元”(量级千位),会导致欧氏距离计算时 M 维度完全主导聚类结果——这正是本项目在 data_preprocessing 阶段必须做 MinMaxScaler 标准化的根本原因。

2.1 数据加载与基础清洗

import pandas as pd import numpy as np from datetime import datetime, timedelta # 加载原始订单数据 df = pd.read_csv('data/orders_data.csv') # 强制转换日期格式,避免字符串导致排序错误 df['order_date'] = pd.to_datetime(df['order_date'], errors='coerce') # 删除无效日期行(如 NaT) df = df.dropna(subset=['order_date']) # 检查 customer_id 是否存在空值或异常字符 print("customer_id 缺失值数量:", df['customer_id'].isnull().sum()) print("customer_id 唯一值数量:", df['customer_id'].nunique())

注意errors='coerce'将无法解析的日期转为 NaT,后续dropna可确保时间计算安全。若customer_id存在空值,需确认是否为匿名访客订单——本项目默认剔除,因 RFM 要求明确用户身份。

2.2 RFM 三指标计算逻辑与代码实现

RFM 的 R、F、M 并非独立计算,而是强依赖分析截止点(analysis_date)。本项目采用数据集中最大订单日期作为截止点,确保所有用户 R 值在同一基准下可比:

# 确定分析截止日期:取数据中最新订单日期 analysis_date = df['order_date'].max() # 计算 R:每个用户最后一次下单距 analysis_date 的天数 r_df = df.groupby('customer_id')['order_date'].max().reset_index(name='last_order_date') r_df['R'] = (analysis_date - r_df['last_order_date']).dt.days # 计算 F:每个用户订单总数(去重 order_id) f_df = df.groupby('customer_id')['order_id'].nunique().reset_index(name='F') # 计算 M:每个用户订单金额总和 m_df = df.groupby('customer_id')['amount'].sum().reset_index(name='M') # 合并为 RFM 表 rfm_table = r_df.merge(f_df, on='customer_id').merge(m_df, on='customer_id') print("RFM 表维度:", rfm_table.shape) print(rfm_table.head())
参数说明:
  • dt.days:将timedelta转为整数天数,避免浮点误差;
  • nunique():确保 F 统计的是订单笔数,而非商品行数(若数据含明细行,需先按 order_id 去重);
  • merge顺序:以r_df为主表,保证所有有下单记录的用户均被保留。

2.3 RFM 标准化:解决量纲差异对 K-Means 的致命干扰

K-Means 基于欧氏距离,若 R(0–365)、F(1–200)、M(0–50000)不统一量纲,聚类中心将严重偏向 M 维度。本项目采用 MinMaxScaler 将各指标压缩至 [0,1] 区间,公式为:
$$x_{\text{scaled}} = \frac{x - x_{\min}}{x_{\max} - x_{\min}}$$

from sklearn.preprocessing import MinMaxScaler # 提取 RFM 数值列 rfm_features = rfm_table[['R', 'F', 'M']].copy() # 初始化标准化器 scaler = MinMaxScaler() rfm_scaled = scaler.fit_transform(rfm_features) # 转为 DataFrame 便于后续操作 rfm_scaled_df = pd.DataFrame(rfm_scaled, columns=['R_scaled', 'F_scaled', 'M_scaled'], index=rfm_table.index) rfm_final = pd.concat([rfm_table[['customer_id']], rfm_scaled_df], axis=1) print("标准化后 RFM 前5行:") print(rfm_final.head())

提示MinMaxScalerStandardScaler更适合 RFM,因 R 值越小代表用户越活跃(R=0 是当天下单),而 M、F 越大越好。Min-Max 保留了这种方向性,且避免负值影响后续聚类解释。

2.4 RFM 分箱与业务解读(可选增强步骤)

虽然 K-Means 直接作用于连续值,但为辅助业务理解,项目在 readme.md 中提供了分箱建议:

  • R 分 5 档:0–30 天(高活跃)、31–90 天、91–180 天、181–365 天、>365 天(流失风险);
  • F 分 5 档:1 次、2–3 次、4–6 次、7–12 次、≥13 次;
  • M 分 5 档:按金额四分位数切分。
    此分箱不参与聚类,仅用于将聚类结果映射为“重要价值客户”“新客户”“流失预警客户”等业务标签。

3. K-Means 聚类全流程:从肘部法则选 k 到五类用户画像生成

K-Means 在本项目中不是黑箱调用,而是需验证其适用性、调试超参数、并确保结果可解释。核心挑战在于:如何证明 k=5 是最优解?聚类后如何避免“数字正确但业务无感”?本节基于 main.py 中的 clustering.py 模块,拆解从距离计算到画像输出的完整链路。

3.1 肘部法则(Elbow Method)确定最优聚类数 k

K-Means 的 k 值选择直接影响分群质量。盲目设 k=3 或 k=8 会导致过拟合或欠拟合。肘部法则通过计算不同 k 值下的簇内平方和(WCSS, Within-Cluster Sum of Squares)绘制曲线,拐点即为最优 k。本项目在 Figure_2.png 中展示了 k=1 至 k=10 的 WCSS 曲线,明确显示 k=5 处出现显著拐点。

from sklearn.cluster import KMeans import matplotlib.pyplot as plt # 计算不同 k 值的 WCSS wcss = [] K_range = range(1, 11) for k in K_range: kmeans = KMeans(n_clusters=k, random_state=42, n_init=10) kmeans.fit(rfm_final[['R_scaled', 'F_scaled', 'M_scaled']]) wcss.append(kmeans.inertia_) # inertia_ 即 WCSS # 绘制肘部图 plt.figure(figsize=(8, 5)) plt.plot(K_range, wcss, marker='o') plt.title('Elbow Method for Optimal k') plt.xlabel('Number of Clusters (k)') plt.ylabel('WCSS') plt.grid(True) plt.savefig('image/Figure_2.png', dpi=300, bbox_inches='tight') plt.show()
关键参数说明:
  • n_init=10:K-Means 随机初始化 10 次,取最优解,避免局部最优;
  • random_state=42:保证结果可复现,调试时必需;
  • inertia_:模型训练后自动计算的 WCSS 值,无需手动实现。

3.2 执行 K-Means 聚类并分配标签

选定 k=5 后,对标准化后的 RFM 数据执行聚类,并将结果回写至原始 RFM 表:

# 使用最优 k=5 运行 K-Means kmeans_final = KMeans(n_clusters=5, random_state=42, n_init=10) rfm_final['Cluster'] = kmeans_final.fit_predict(rfm_final[['R_scaled', 'F_scaled', 'M_scaled']]) # 查看各类别用户数量分布 cluster_dist = rfm_final['Cluster'].value_counts().sort_index() print("各聚类用户数量分布:") print(cluster_dist)

注意fit_predict一步完成训练与预测,比先fitpredict更简洁。value_counts().sort_index()确保 Cluster 0–4 按序输出,方便后续关联画像。

3.3 五类用户画像对比:用原始 RFM 值而非标准化值解读

聚类标签(0–4)本身无业务含义,需结合原始 R、F、M 均值还原用户特征。例如:Cluster 0 的 R 均值为 12 天、F 为 8 次、M 为 3200 元,则可定义为“高价值常客”;Cluster 4 的 R 均值为 210 天、F 为 1 次、M 为 85 元,则为“低频低额流失用户”。本项目在 Figure_4.png 中用雷达图直观展示五类用户的 R/F/M 相对强度。

# 按聚类分组,计算原始 RFM 均值(非标准化值) cluster_profile = rfm_table.groupby('Cluster')[['R', 'F', 'M']].mean().round(2) print("五类用户原始 RFM 均值:") print(cluster_profile) # 保存为 CSV 供业务方查阅 cluster_profile.to_csv('data/cluster_profile.csv')
输出示例(模拟):
ClusterR(天)F(次)M(元)
015.212.84250.6
145.73.2890.3
2180.51.0120.8
38.31.0215.4
4220.11.075.2

提示:R 值越小越活跃,故 Cluster 3(R=8.3)是“新客”,Cluster 0(R=15.2)是“高价值常客”,Cluster 2/4 是“流失风险户”,Cluster 1 是“潜力客户”(R 中等、F/M 适中)。

3.4 聚类结果可视化:三维散点图与雷达图

二维散点图无法展现三维 RFM 关系,本项目采用主成分分析(PCA)降维至二维,并用不同颜色标记聚类结果(Figure_3.png);同时用雷达图(Figure_4.png)直接对比五类在 R/F/M 上的相对位置,避免 PCA 引入的信息损失。

from sklearn.decomposition import PCA # 对标准化 RFM 数据做 PCA 降维 pca = PCA(n_components=2) rfm_pca = pca.fit_transform(rfm_final[['R_scaled', 'F_scaled', 'M_scaled']]) # 绘制 PCA 散点图 plt.figure(figsize=(10, 6)) scatter = plt.scatter(rfm_pca[:, 0], rfm_pca[:, 1], c=rfm_final['Cluster'], cmap='viridis', alpha=0.6) plt.colorbar(scatter) plt.title('PCA of Scaled RFM Data (2D Projection)') plt.xlabel(f'PC1 ({pca.explained_variance_ratio_[0]:.2%} variance)') plt.ylabel(f'PC2 ({pca.explained_variance_ratio_[1]:.2%} variance)') plt.savefig('image/Figure_3.png', dpi=300, bbox_inches='tight') plt.show()

4. 聚类稳定性验证与业务落地技巧:避免“伪科学分群”

K-Means 结果易受初始质心、数据噪声影响。若直接将聚类标签用于精准营销,可能因单次运行结果波动导致策略失效。本节提供两种低成本验证方法,并给出将结果嵌入业务系统的实操技巧。

4.1 用轮廓系数(Silhouette Score)量化聚类质量

肘部法则判断“k 是否合适”,轮廓系数则评估“当前 k 下各点聚类是否合理”。取值范围 [-1, 1],越接近 1 表示聚类越紧密。本项目在 main.py 中计算得 silhouette_score ≈ 0.42(k=5),属中等偏上水平,证明分群有效。

from sklearn.metrics import silhouette_score # 计算轮廓系数 sil_score = silhouette_score( rfm_final[['R_scaled', 'F_scaled', 'M_scaled']], rfm_final['Cluster'] ) print(f"Silhouette Score for k=5: {sil_score:.3f}") # 若 score < 0.25,建议检查数据或尝试其他算法(如 DBSCAN) if sil_score < 0.25: print("警告:轮廓系数偏低,聚类质量可能不佳")

注意:轮廓系数对 k 敏感,需配合肘部法则使用。单独高分不保证业务意义,但低分一定意味着分群失败。

4.2 时间切片交叉验证:检验聚类结果的时序鲁棒性

真实业务中,用户行为会随时间漂移。本项目提供validate_over_time.py脚本(未在压缩包列出但可快速补充),其逻辑是:将 orders_data.csv 按时间分为训练集(前80%订单)和测试集(后20%订单),分别构建 RFM 并聚类,计算两套 Cluster 标签的 Adjusted Rand Index(ARI)。ARI=1 表示完全一致,ARI<0.5 则说明分群不稳定。

# 示例:用前80%数据训练聚类模型 cutoff_date = df['order_date'].quantile(0.8) train_df = df[df['order_date'] <= cutoff_date] # ...(重复 RFM 构建与 K-Means 步骤)... train_clusters = kmeans_final.predict(train_rfm_scaled) # 用后20%数据预测(不重新训练) test_df = df[df['order_date'] > cutoff_date] test_rfm = build_rfm_from_df(test_df) # 自定义函数 test_clusters = kmeans_final.predict(test_rfm[['R_scaled','F_scaled','M_scaled']]) # 计算 ARI(需安装 scikit-learn 1.0+) from sklearn.metrics import adjusted_rand_score ari = adjusted_rand_score(train_labels, test_labels) # 需对齐 customer_id

4.3 业务落地:将 Cluster 标签同步至数据库与 BI 工具

聚类结果需脱离 Jupyter 环境,进入生产系统。本项目在 readme.md 中明确给出 MySQL 同步方案:

-- 创建用户画像表 CREATE TABLE customer_segment ( customer_id VARCHAR(50) PRIMARY KEY, cluster_id TINYINT NOT NULL, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); -- 用 pandas 批量插入(main.py 最后部分) rfm_final[['customer_id', 'Cluster']].to_sql( 'customer_segment', con=engine, if_exists='replace', index=False );

提示if_exists='replace'确保每日全量更新,避免增量同步的主键冲突。BI 工具(如 Tableau、QuickSight)可直连该表,将cluster_id作为筛选器,实现“针对 Cluster 0 用户推送高毛利新品”的自动化营销。

4.4 关键参数速查表:main.py 中可调整的核心配置

参数名默认值说明修改建议
ANALYSIS_DATEdf['order_date'].max()RFM 计算截止日若需分析历史快照,可设为固定日期如'2023-12-31'
N_CLUSTERS5K-Means 的 k 值依据 Figure_2.png 肘部点调整,勿硬编码
SCALER_TYPE'minmax'标准化方法若数据含异常值,可改用'robust'(中位数+四分位距)
RANDOM_STATE42随机种子调试时固定,上线后可设为int(time.time())增加随机性

最终,五类用户画像已固化为data/cluster_profile.csv,可直接导入 CRM 系统;聚类过程生成的全部图表(Figure_*.png)均存于image/目录,支持向管理层汇报。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询