哪个用户会再来买?RFM模型+plotly画出用户分层,数据分析实战第2期

上一期我们洗了40行销售脏数据,日期格式、金额符号、产品名写法,挨个收拾干净。今天换个更值钱的活:电商老板问你,500个用户里,哪些值得发优惠券唤醒,哪些已经流失别浪费钱?拍脑袋不行,得用数据说话。

RFM模型就是干这个的,电商分析用了几十年的老办法。R是Recency,最近一次买是多久以前;F是Frequency,买过几次;M是Monetary,一共花了多少钱。三个维度一交叉,用户分层清清楚楚。

环境:pandas 2.x加plotly,没装的跑pip install pandas plotly。数据我用代码现场生成500个用户的模拟订单,和真实订单一个结构,你换成自己的CSV直接跑。

第一步,造数据,算出RFM三列:

python 复制代码
import pandas as pd
import numpy as np

np.random.seed(7)
users = [f"U{i:04d}" for i in range(500)]
orders = []
for u in users:
    for _ in range(np.random.randint(1, 15)):
        orders.append({
            "user": u,
            "date": pd.Timestamp("2026-01-01")
                    + pd.to_timedelta(np.random.randint(0, 280), "D"),
            "amount": round(float(np.random.gamma(2, 80)), 2),
        })
df = pd.DataFrame(orders)

ref = pd.Timestamp("2026-10-20")
rfm = df.groupby("user").agg(
    Recency=("date", lambda s: (ref - s.max()).days),
    Frequency=("date", "count"),
    Monetary=("amount", "sum"),
)

第二步,打分,每个维度5分制。注意R的方向是反的:离现在越近分越高,别打反了。打分我用rank(pct=True)转成分位数再切,这样真实数据里大量重复值也不会让qcut报错:

python 复制代码
rfm["R"] = pd.cut(rfm["Recency"].rank(pct=True, ascending=False),
                  bins=5, labels=[5, 4, 3, 2, 1]).astype(int)
rfm["F"] = pd.cut(rfm["Frequency"].rank(pct=True),
                  bins=5, labels=[1, 2, 3, 4, 5]).astype(int)
rfm["M"] = pd.cut(rfm["Monetary"].rank(pct=True),
                  bins=5, labels=[1, 2, 3, 4, 5]).astype(int)

第三步,分群。四象限最实用:R、F都高于中位数的是明星用户;R高F低是潜力新客;R低F高是沉睡用户,曾经爱买现在不来了;双低的是流失用户,发券都未必回来。

python 复制代码
rm, fm = rfm["R"].median(), rfm["F"].median()

def segment(row):
    if row["R"] >= rm and row["F"] >= fm:
        return "明星用户"
    if row["R"] >= rm:
        return "潜力新客"
    if row["F"] >= fm:
        return "沉睡用户"
    return "流失用户"

rfm["segment"] = rfm.apply(segment, axis=1)
print(rfm.groupby("segment").agg(
    人数=("R", "count"),
    平均客单价=("Monetary", "mean"),
    GMV=("Monetary", "sum"),
))

跑完打印一张分群表:每群人数、平均客单价、贡献的GMV。我的模拟数据跑出来头部效应明显:明星用户139人贡献21万,沉睡用户173人贡献近30万,两群加起来六成用户、八成销售额。优惠券该发给谁,一目了然。

第四步,plotly画出来。散点图x轴是距今天数,y轴是购买次数,点的大小是总花费,颜色是分群。一张图,老板看一眼就懂:

python 复制代码
import plotly.express as px

fig = px.scatter(
    rfm.reset_index(),
    x="Recency", y="Frequency",
    size="Monetary", color="segment",
    title="RFM用户分层",
    labels={"Recency": "距今多少天没买了", "Frequency": "买过几次"},
)
fig.show()

验证:检查四群人数加起来等于500,分群标签没有空值,散点图四个颜色都出现了。数字对不上,后面全是错的,先对数再画图。

踩坑四个。第一,qcut重复边界:真实订单日期大量重复,qcut报"Bin edges must be unique"。用rank(pct=True)加cut,或者rank(method="first"),提前避开。第二,R打分方向搞反:距今30天给了1分,距今300天给了5分,分群全反,结论反着交差,灾难。第三,日期没转datetime就做减法,字符串相减直接报错,先pd.to_datetime。第四,分群完不看业务:沉睡用户里混着只买过一次的和买过20次的,唤醒策略能一样吗?分群是起点,不是终点。

收藏这篇,下次老板要用户分层,换上真实订单CSV,这套代码改个文件名就能交差。

下期预告:数据分析实战第3期讲时间序列。销售额的周末效应怎么量化,节假日到底拉动了多少,pandas重采样加plotly一次讲透,关注我不错过。

讨论时间:你们公司做用户分层用RFM还是别的模型?效果怎么样?评论区交流下。

------ Cyrus

相关推荐
AI源码14 分钟前
基于深度学习的热带水果检测系统设计与实现
人工智能·深度学习
AGI_Eval17 分钟前
AI 3D生成技术:从“可用”走向“可靠”还有多远?
人工智能·3d
程序员cxuan18 分钟前
LobeHub 是真的夯,简直 Super-Agent 最佳实现。
人工智能·后端·程序员
QXWZ_IA24 分钟前
高楼遮挡RTK失锁:三层安全网怎么搭
人工智能·安全
sun8612207735 分钟前
Hy4 Preview 高效预览与协作应用指南
人工智能
码云之上1 小时前
10 颗一模一样的「Edit」:中后台 Web Agent 怎么找到对的那一行
前端·人工智能·浏览器
阿里云大数据AI技术1 小时前
EMR Serverless Ray:让 AI 推理融入数据处理
人工智能·spark
TDengine (老段)1 小时前
TDengine IDMP 十大实用功能(9)
大数据·数据库·人工智能·物联网·时序数据库·tdengine·idmp
MobotStone1 小时前
毛选从哪篇开始读?
人工智能