简介:本资源是一份面向数据分析初学者与电商运营人员的实战型顾客价值分析方案,聚焦于K-Means聚类与RFM模型在用户分群与消费行为挖掘中的联合应用。资源提供从原始订单数据清洗、时序趋势建模(含SARIMA预测)、消费者画像构建到RFM维度打分的完整Python实现流程,覆盖数据逻辑校验、多维特征分析及五类用户群体对比等关键环节。压缩包共13个文件,包含9张可视化结果图(如销售额时序图、聚类分布图等)、1个主程序脚本(main.py)、1个结构化订单数据集(orders_data.csv)、1份说明文档(readme.md)及1个配置文件(json),整体体积仅906KB,轻量易部署。目前已有566人学习下载,内容组织清晰,代码注释详尽,图表与分析结论一一对应,可直接复用于课程设计、实习报告或中小电商用户精细化运营实践。
1. 为什么用 K-Means + RFM 分析顾客消费,比直接看销售额更准?
你手上有 20 万条交易记录,导出 Excel 后发现:Top 5% 客户贡献了 63% 收入,但没人能说清这 5% 是谁、为什么稳定复购、哪类人正在流失。单纯按总金额排序,会把刚下单大单的新客和持续小额高频的老客混为一谈;按最近一次购买时间筛“活跃用户”,又可能漏掉周期性采购的 B 端客户。K-Means 聚类本身不理解业务语义——它只认数字距离,直接对原始交易字段聚类,结果常是噪声主导的碎片簇。RFM 模型(Recency, Frequency, Monetary)则把消费行为压缩成三个可解释、可干预的维度:R 值越小代表越近刚买过,F 值越大说明习惯性回购,M 值高反映客单价或总量强。当 K-Means 作用于标准化后的 RFM 三轴坐标时,它找的不再是“数值相近的人”,而是“行为模式相似的群体”——比如高 R+高 F+中 M 的“日常高频白领”,低 R+高 F+高 M 的“季度批量采购企业客户”,或 R 高但 F/M 双低的“沉默流失预警户”。本方案不是教你怎么调sklearn.cluster.KMeans的n_clusters,而是带你从原始订单表出发,用pandas构建 RFM 特征、用StandardScaler消除量纲干扰、用肘部法与轮廓系数双验证确定最优簇数,并最终输出每个簇的运营标签与行动建议。适合有 Python 基础、手头有交易流水表、急需把“顾客分群”从 PPT 概念落地为 CRM 可执行策略的从业者。
2. 从原始订单表到 RFM 三维向量:数据清洗与特征工程实操
2.1 原始数据结构识别与关键字段提取逻辑
典型电商/零售订单表包含order_id,customer_id,order_date,amount四个核心字段。注意:order_date必须是datetime64[ns]类型,否则无法计算 R 值;amount需确认是否含退款——若存在负值订单,必须先过滤或取绝对值后再聚合。常见陷阱是customer_id存在空值或匿名化 ID(如user_abc123),需统一清洗为非空字符串。以下代码段以真实数据结构为基准,跳过冗余列并强制类型转换:
import pandas as pd import numpy as np # 假设原始数据已加载为 df_orders df_orders = pd.read_csv('orders.csv', parse_dates=['order_date']) # 清洗 customer_id:去空格、转字符串、剔除空值 df_orders['customer_id'] = df_orders['customer_id'].astype(str).str.strip() df_orders = df_orders.dropna(subset=['customer_id', 'order_date', 'amount']) # 过滤异常金额(如负数、超大值) df_orders = df_orders[(df_orders['amount'] > 0) & (df_orders['amount'] < 1e6)] # 验证关键字段分布 print(f"有效订单数: {len(df_orders)}") print(f"唯一客户数: {df_orders['customer_id'].nunique()}") print(f"订单日期范围: {df_orders['order_date'].min()} ~ {df_orders['order_date'].max()}")提示:若
order_date是字符串格式(如"2023-05-12"),parse_dates=['order_date']会自动转为 datetime;若为时间戳整数(毫秒级),需用pd.to_datetime(df_orders['order_date'], unit='ms')。未做此转换直接计算max(order_date)会导致 TypeError。
2.2 RFM 三指标计算:Recency、Frequency、Monetary 的业务定义与实现
RFM 中每个字母对应一个聚合指标,其计算逻辑必须贴合业务周期。例如:R 值应以“分析截止日”为基准,而非数据最大日期——因为最后一天可能有未结算订单。我们取df_orders['order_date'].max()作为截止日,确保所有客户 R 值在同一时间刻度下可比:
# 设定分析截止日(取数据中最新订单日,非系统当前日) analysis_date = df_orders['order_date'].max() # 计算 RFM 三指标 rfm_df = df_orders.groupby('customer_id').agg( R=('order_date', lambda x: (analysis_date - x.max()).days), # R: 最近一次购买距今天数 F=('order_id', 'count'), # F: 总购买次数 M=('amount', 'sum') # M: 总消费金额 ).reset_index() # 强制类型:R 必须为整数,F/M 为数值 rfm_df['R'] = rfm_df['R'].astype(int) rfm_df['F'] = rfm_df['F'].astype(int) rfm_df['M'] = rfm_df['M'].astype(float) print(f"RFM 表行数: {len(rfm_df)},与客户数一致: {rfm_df['customer_id'].nunique() == len(rfm_df)}")2.2.1 R 值陷阱:为什么不能直接用max(order_date)差值?
若某客户最后一次下单是2023-12-31,而分析截止日是2024-01-01,R=1 天;但若该客户在2024-01-0100:01 下单,R 就变成 0。这种微小时间差会导致 K-Means 将临近截止日的客户强行拉入“高活跃簇”,掩盖真实行为差异。解决方案是统一按日截断:analysis_date = df_orders['order_date'].dt.date.max(),再计算(analysis_date - x.max().date()).days。这样无论下单是当天几点,R 值都相同。
2.2.2 F 与 M 的业务校准:是否计入退换货?
F 统计的是订单数,不是商品件数——即使一单买 10 件,F 仍为 1。M 若需反映“净贡献”,应减去退款金额。若原始表无退款字段,需额外关联退款表或标记amount < 0的订单为退款,从 M 中扣除:
# 若存在退款标识(如 amount 为负),修正 M 值 refund_mask = df_orders['amount'] < 0 df_orders.loc[refund_mask, 'amount'] = 0 # 或取绝对值后加负号,视业务而定 # 重新聚合 M rfm_df['M'] = df_orders.groupby('customer_id')['amount'].sum()2.3 RFM 标准化:为什么必须用 StandardScaler 而非 MinMaxScaler?
K-Means 依赖欧氏距离,而 R、F、M 的量纲与分布差异极大:R 值通常在 0–365 天,F 可能是 1–500 次,M 可达 0–100000 元。若直接聚类,M 的数值范围会主导距离计算,导致簇中心几乎只由消费金额决定。StandardScaler将每列转为均值为 0、标准差为 1 的分布,使三维度在距离计算中权重相等。MinMaxScaler虽也缩放,但会压缩异常值(如某客户 M=500000),导致多数客户挤在 [0,0.1] 区间,K-Means 无法区分。代码实现如下:
from sklearn.preprocessing import StandardScaler # 提取 RFM 数值列 rfm_features = rfm_df[['R', 'F', 'M']].values # 标准化 scaler = StandardScaler() rfm_scaled = scaler.fit_transform(rfm_features) # 转回 DataFrame 便于后续操作 rfm_scaled_df = pd.DataFrame(rfm_scaled, columns=['R_scaled', 'F_scaled', 'M_scaled'], index=rfm_df.index) rfm_final = pd.concat([rfm_df[['customer_id']], rfm_scaled_df], axis=1) print("标准化后各维度统计:") print(rfm_scaled_df.describe())注意:
scaler.fit_transform()必须在训练集上拟合,若后续要预测新客户,需保存scaler对象并复用scaler.transform(),不可重新拟合。
3. K-Means 聚类落地:肘部法选簇数、轮廓系数验证与模型训练
3.1 肘部法(Elbow Method)确定最优 K 值:不止画图,更要读懂拐点
肘部法通过计算不同 K 值下簇内平方和(WCSS)来寻找“收益递减”的拐点。但 WCSS 曲线常无明显肘部,需结合业务场景判断。例如:K=3 时 WCSS 下降剧烈,K=4 开始平缓,但业务上需区分“高价值沉睡客户”与“中价值活跃客户”,则 K=4 更合理。代码实现需遍历 K=2 到 10,每次训练 K-Means 并记录 WCSS:
from sklearn.cluster import KMeans import matplotlib.pyplot as plt # 计算不同 K 值的 WCSS k_range = range(2, 11) wcss = [] for k in k_range: kmeans = KMeans(n_clusters=k, random_state=42, n_init=10) kmeans.fit(rfm_scaled_df) wcss.append(kmeans.inertia_) # inertia_ 即 WCSS # 绘制肘部图 plt.figure(figsize=(8, 5)) plt.plot(k_range, wcss, marker='o') plt.xlabel('聚类数量 (K)') plt.ylabel('簇内平方和 (WCSS)') plt.title('肘部法确定最优 K 值') plt.grid(True) plt.show() # 打印 WCSS 数值供人工判断 for k, w in zip(k_range, wcss): print(f"K={k}: WCSS={w:.2f}")3.1.1 如何避免肘部图误判?看相对下降率而非绝对值
WCSS 绝对值随 K 增大必然下降,关键看下降率。计算相邻 K 的下降比例:(wcss[i-1] - wcss[i]) / wcss[i-1]。若 K=3→4 下降率仅 8%,而 K=2→3 达 35%,则 K=3 是更优解。以下代码自动计算并标记拐点:
# 计算相对下降率 drop_rates = [] for i in range(1, len(wcss)): drop_rate = (wcss[i-1] - wcss[i]) / wcss[i-1] * 100 drop_rates.append(drop_rate) # 找下降率首次低于 15% 的 K 值(业务经验值) optimal_k = 2 for i, rate in enumerate(drop_rates): if rate < 15 and k_range[i+1] > 2: optimal_k = k_range[i+1] break print(f"基于下降率阈值 15%,推荐 K={optimal_k}")3.2 轮廓系数(Silhouette Score)二次验证:量化簇间分离度
肘部法易受主观影响,轮廓系数提供客观指标:取值范围 [-1, 1],越接近 1 表示簇内紧密、簇间分离好。需对每个 K 计算平均轮廓系数,选择最高值对应的 K:
from sklearn.metrics import silhouette_score silhouette_scores = [] for k in k_range: kmeans = KMeans(n_clusters=k, random_state=42, n_init=10) labels = kmeans.fit_predict(rfm_scaled_df) score = silhouette_score(rfm_scaled_df, labels) silhouette_scores.append(score) print(f"K={k}: 轮廓系数={score:.3f}") # 找最高分对应的 K best_k_silhouette = k_range[np.argmax(silhouette_scores)] print(f"轮廓系数最高 K={best_k_silhouette},得分为 {max(silhouette_scores):.3f}")提示:若轮廓系数最高值出现在 K=2,但业务需至少 4 类客户(如 VIP/普通/潜在/流失),应优先满足业务需求,再检查 K=4 时的系数是否 >0.4(可接受下限)。低于 0.25 表示聚类效果差,需检查 RFM 特征质量或尝试其他算法(如 DBSCAN)。
3.3 训练最终 K-Means 模型并分配簇标签
选定 K 后,用n_init=20降低局部最优风险,max_iter=300确保收敛:
# 采用肘部法与轮廓系数共识的 K 值(取两者交集,若冲突则选轮廓系数更高者) final_k = max(optimal_k, best_k_silhouette) if abs(optimal_k - best_k_silhouette) <= 1 else best_k_silhouette # 训练最终模型 kmeans_final = KMeans(n_clusters=final_k, random_state=42, n_init=20, max_iter=300) rfm_final['cluster'] = kmeans_final.fit_predict(rfm_scaled_df) # 查看各簇客户数分布 cluster_dist = rfm_final['cluster'].value_counts().sort_index() print("各簇客户数量分布:") print(cluster_dist)3.3.1 检查簇中心坐标:理解每个簇的行为含义
K-Means 输出的cluster_centers_是标准化后的坐标,需逆变换回原始 RFM 尺度,才能解读业务意义:
# 获取簇中心(标准化尺度) centers_scaled = kmeans_final.cluster_centers_ # 逆变换回原始尺度 centers_original = scaler.inverse_transform(centers_scaled) # 构建簇中心解读表 centers_df = pd.DataFrame(centers_original, columns=['R_original', 'F_original', 'M_original'], index=range(final_k)) # 添加簇内客户数 centers_df['customer_count'] = cluster_dist.values print("各簇中心原始 RFM 值(R:天数, F:次数, M:元):") print(centers_df.round(2))| cluster | R_original | F_original | M_original | customer_count |
|---|---|---|---|---|
| 0 | 120.3 | 5.2 | 850.6 | 12400 |
| 1 | 8.7 | 42.1 | 12500.3 | 3800 |
| 2 | 290.5 | 2.1 | 320.8 | 8900 |
解读示例:簇 1 的 R=8.7 天(极近)、F=42.1 次(高频)、M=12500 元(高客单),是典型的“高价值活跃客户”;簇 2 的 R=290 天(超半年未购)、F=2.1 次(低频)、M=320 元(低额),属“沉默流失风险户”。
4. 业务标签生成与运营策略映射:让聚类结果真正驱动动作
4.1 基于 RFM 原始值定义客户分层标签(非标准化值)
簇标签是数学结果,运营需可理解的名称。我们依据 R/F/M 在原始尺度的四分位数(Q1/Q3)划分高中低档,组合生成标签。例如:R ≤ Q1 为“近期”,R ≥ Q3 为“沉睡”;F ≥ Q3 为“高频”,F ≤ Q1 为“低频”;M ≥ Q3 为“高价值”。代码实现:
# 计算 RFM 原始值的四分位数 q1_r, q3_r = rfm_df['R'].quantile(0.25), rfm_df['R'].quantile(0.75) q1_f, q3_f = rfm_df['F'].quantile(0.25), rfm_df['F'].quantile(0.75) q1_m, q3_m = rfm_df['M'].quantile(0.25), rfm_df['M'].quantile(0.75) # 为每个客户打标签 def get_rfm_label(row): r_label = '近期' if row['R'] <= q1_r else ('沉睡' if row['R'] >= q3_r else '一般') f_label = '高频' if row['F'] >= q3_f else ('低频' if row['F'] <= q1_f else '中频') m_label = '高价值' if row['M'] >= q3_m else ('低价值' if row['M'] <= q1_m else '中价值') return f"{r_label}{f_label}{m_label}" rfm_df['rfm_label'] = rfm_df.apply(get_rfm_label, axis=1) print("RFM 标签分布示例:") print(rfm_df['rfm_label'].value_counts().head())4.1.1 将 K-Means 簇与 RFM 标签对齐:发现算法与业务的偏差
将rfm_final['cluster']与rfm_df['rfm_label']关联,检查簇内标签一致性。若簇 0 中 80% 客户为“近期高频高价值”,则簇 0 可命名为“核心活跃客户”;若簇 1 中混杂“沉睡低频高价值”与“近期低频高价值”,说明 K-Means 未能区分价值来源,需检查 M 值是否受单一大单扭曲(如某客户一年只买一次但金额 50 万):
# 合并簇标签与 RFM 标签 merged_df = rfm_df.merge(rfm_final[['customer_id', 'cluster']], on='customer_id') # 统计每簇内 RFM 标签占比 label_by_cluster = pd.crosstab(merged_df['cluster'], merged_df['rfm_label'], normalize='index') * 100 print("各簇内 RFM 标签占比(%):") print(label_by_cluster.round(1))4.2 输出可执行的运营策略矩阵:每个簇对应具体动作
根据簇中心解读与标签一致性,制定差异化策略。以下为典型策略映射表,直接嵌入 CRM 系统:
| 簇ID | 簇名 | 核心特征 | 短期动作(1个月内) | 长期动作(季度) |
|---|---|---|---|---|
| 0 | 核心活跃客户 | R低、F高、M高 | 发送专属折扣码;邀请参与 VIP 社群 | 定制化新品优先体验;年度忠诚度计划 |
| 1 | 潜力成长客户 | R中、F中、M中,但 F增速快 | 推送复购激励(满减券);推荐互补商品 | 个性化内容推送;建立客户成功经理对接 |
| 2 | 沉默流失预警户 | R高、F低、M低 | 触发唤醒短信(“您有未使用的积分”);发放无门槛券 | 分析流失原因(问卷);优化首单体验 |
| 3 | 价值波动客户 | R低但 M极高,F不稳定 | 紧急联系确认需求;提供定制化服务包 | 建立大客户专线;签订年度框架协议 |
注意:策略必须可量化。例如“发送专属折扣码”需明确折扣力度(如 95 折)、有效期(7 天)、适用品类(全店通用);“唤醒短信”需设定触发条件(R>180 天且近 3 月无互动)。
4.3 导出结果至业务系统:生成 main.py 可调度的标准化输出
最终交付物需为main.py可一键运行的脚本,输出customer_segmentation.csv,含customer_id,cluster,rfm_label,strategy_code四列,供 BI 工具或营销平台调用:
# 生成策略编码映射字典 strategy_map = { 0: 'VIP_ACTIVE', 1: 'POTENTIAL_GROWTH', 2: 'CHURN_RISK', 3: 'VOLATILE_HIGH_VALUE' } # 添加策略编码列 merged_df['strategy_code'] = merged_df['cluster'].map(strategy_map) # 保存结果 output_cols = ['customer_id', 'cluster', 'rfm_label', 'strategy_code'] merged_df[output_cols].to_csv('customer_segmentation.csv', index=False) print("客户分群结果已保存至 customer_segmentation.csv")5. 进阶技巧:处理冷启动客户与动态更新机制
5.1 新客户无历史订单时的 RFM 初始化策略
新注册用户无订单,R/F/M 均为空。直接排除会丢失潜在价值,需设计默认值。常见做法是:R 设为最大值(如 365),F=0,M=0,但这样会全部归入“沉睡”簇。更优方案是引入行为埋点数据:若用户完成注册、浏览商品页≥3 次、加入购物车,则赋予虚拟 F=0.5、M=50(预估首单金额),R=0(视为即时活跃)。代码示例如下:
# 假设新客户行为数据在 df_new_users 表中 df_new_users = pd.read_csv('new_users_behavior.csv') # 初始化 RFM 值 df_new_users['R'] = 0 # 新用户视为今日活跃 df_new_users['F'] = df_new_users['cart_adds'] * 0.5 + df_new_users['page_views'] * 0.1 # 权重经验公式 df_new_users['M'] = 50.0 # 首单预估金额 # 标准化并预测簇 new_rfm_scaled = scaler.transform(df_new_users[['R', 'F', 'M']]) df_new_users['cluster'] = kmeans_final.predict(new_rfm_scaled) # 合并到主表 rfm_final_new = pd.concat([rfm_final, df_new_users[['customer_id', 'cluster']]], ignore_index=True)5.2 每月自动更新分群:用 crontab 调度 main.py
将完整流程封装为main.py,支持命令行参数指定数据路径与分析日期:
# Linux 下每日凌晨 2 点执行 0 2 * * * cd /path/to/project && python main.py --data_path ./data/orders.csv --analysis_date 2024-06-01main.py内部需解析参数并调用前述函数,关键点在于:不重训 K-Means 模型,只用已有 scaler 和 kmeans_final.predict(),确保新客户归属与历史一致:
import argparse if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument('--data_path', required=True) parser.add_argument('--analysis_date', required=True) args = parser.parse_args() # 加载新订单数据 df_new = pd.read_csv(args.data_path, parse_dates=['order_date']) analysis_date = pd.to_datetime(args.analysis_date) # 构建新客户 RFM(同前文逻辑) # ...(省略中间步骤) # 用已有 scaler 和模型预测 rfm_new_scaled = scaler.transform(rfm_new[['R', 'F', 'M']]) rfm_new['cluster'] = kmeans_final.predict(rfm_new_scaled) # 保存增量结果 rfm_new.to_csv(f'customer_segmentation_{args.analysis_date}.csv', index=False)提示:生产环境需添加异常处理——若新数据中
customer_id为空,记录日志并跳过;若scaler或kmeans_final文件丢失,程序应退出并报警,而非用默认值硬编码。
5.3 监控分群稳定性:检测簇漂移的三个关键指标
每月更新后,需验证分群是否稳定。计算以下指标,任一超标即触发人工复核:
- 簇成员变动率:某簇本月客户数 vs 上月客户数,变动 >20%;
- 中心偏移距离:当前簇中心与上月簇中心的欧氏距离 >0.5(标准化尺度);
- 标签一致性衰减:同一客户连续两月被分入不同簇的比例 >5%。
# 加载上月结果 last_month = pd.read_csv('customer_segmentation_2024-05-01.csv') current_month = pd.read_csv('customer_segmentation_2024-06-01.csv') # 计算变动率 change_rate = abs(current_month['cluster'].value_counts() - last_month['cluster'].value_counts()) / last_month['cluster'].value_counts() print("簇成员变动率:") print(change_rate.round(3)) # 计算中心偏移(需保存历史 centers_scaled) # centers_last = np.load('centers_2024-05-01.npy') # drift = np.linalg.norm(centers_current - centers_last, axis=1) # print("簇中心偏移距离:", drift.round(3))当发现簇 2 的变动率达 35%,且中心偏移 0.72,说明“沉默流失预警户”定义可能已失效——需检查近期是否上线了唤醒活动,导致大量沉睡客户回流,此时应重新评估 RFM 分界点或调整 K 值。
本文还有配套的精品资源,点击获取