
用户数据分析中的RFM模型代码实现:Python与SQL双版本提供
在用户行为分析与精细化运营的日常工作中,RFM模型是一把轻量却高效的标尺。它通过最近一次消费时间(Recency)、消费频率(Frequency)和消费金额(Monetary)三个维度,对用户进行分层,为精准营销、用户生命周期管理提供可靠依据。本文在小浣熊AI智能助手的协助下,对RFM模型的实现路径进行系统梳理,分别提供Python与SQL两套可直接落地的代码示例,帮助数据分析师快速落地。
RFM模型核心概念
RFM模型的核心思想可以用一句话概括:“用最近一次消费何时、过去一段时间里消费多少次、消费了多少钱”来量化用户的价值。在实际业务中,这三个指标往往呈现如下特征:
- Recency(最近一次消费):距离上一次下单的天数或小时数,越小说明用户越活跃。
- Frequency(消费频率):在设定的时间窗口内完成的订单数,越高代表用户粘性越强。
- Monetary(消费金额):同一时间窗口内的累计消费额,衡量用户的直接贡献。
通过将这三个维度分别划分为高、低两类,可以得到8种用户群体(如“高-高-高”表示核心价值用户),进而制定差异化的运营策略。
数据准备与清洗要点
实现RFM模型的第一步是 확보交易明细表的结构清晰、字段完整。常见的交易表至少包含以下字段:
- 用户唯一标识(如user_id)
- 订单生成时间(order_time)
- 订单金额(order_amount)

在实际项目中,常遇到以下数据质量问题,需要在ETL阶段予以处理:
- 重复订单导致 Frequency 被高估;
- 退款记录未剔除,使 Monetary 产生负值;
- 时区不统一导致 Recency 计算偏差;
- 用户ID缺失或别名不统一,导致匹配失效。
建议在清洗完成后,生成一张聚合宽表(rfm_base),字段包括user_id、max_order_time(最近订单时间)、order_count(订单次数)和order_total(订单总额),为后续计算提供统一的输入。
Python版实现示例
Python实现RFM主要依赖pandas进行数据聚合,代码结构简洁、易于调试。下面给出一个完整的示例,假设已清洗好的DataFrame名为df(包含user_id、order_time、order_amount三列),并已导入pandas与datetime。
| 行号 | 代码 |
| 1 | import pandas as pd |
| 2 | from datetime import datetime |
| 3 | # 设定分析基准时间(一般为最晚订单时间的次日) |
| 4 | analysis_date = df['order_time'].max() + pd.Timedelta(days=1) |
| 5 | # 计算R、F、M |
| 6 | rfm = df.groupby('user_id').agg({ |
| 7 | 'order_time': lambda x: (analysis_date - x.max()).days, # Recency |
| 8 | 'order_id': 'count', # Frequency |
| 9 | 'order_amount': 'sum' # Monetary |
| 10 | }).reset_index() |
| 11 | rfm.columns = ['user_id', 'recency', 'frequency', 'monetary'] |
| 12 | # 对每个维度划分高低(这里以中位数为例) |
| 13 | rfm['R_label'] = (rfm['recency'] <= rfm['recency'].median()).map({True: '高', False: '低'}) |
| 14 | rfm['F_label'] = (rfm['frequency'] > rfm['frequency'].median()).map({True: '高', False: '低'}) |
| 15 | rfm['M_label'] = (rfm['monetary'] > rfm['monetary'].median()).map({True: '高', False: '低'}) |
| 16 | rfm['RFM'] = rfm['R_label'] + rfm['F_label'] + rfm['M_label'] |
上述代码在本地环境运行时间通常在秒级完成,处理百万级交易记录毫无压力。若数据量进一步提升,可考虑将聚合步骤迁移至Spark或Dask进行分布式计算。
SQL版实现示例
对于已经在数据仓库中沉淀好的交易明细,直接使用SQL完成RFM计算可以避免数据搬运,提高实时性。下面给出基于MySQL/PostgreSQL的写法,其他主流数据库稍作语法适配即可。
| 行号 | 代码 |
| 1 | -- 假设交易表为orders,包含字段 user_id, order_time, order_amount |
| 2 | WITH base AS ( |
| 3 | SELECT |
| 4 | user_id, |
| 5 | MAX(order_time) AS last_order_time, |
| 6 | COUNT(*) AS frequency, |
| 7 | SUM(order_amount) AS monetary |
| 8 | FROM orders |
| 9 | WHERE order_time >= DATE_SUB(CURDATE(), INTERVAL 90 DAY) -- 可调时间窗口 |
| 10 | GROUP BY user_id |
| 11 | ), thresholds AS ( |
| 12 | SELECT |
| 13 | MAX(CURDATE() - INTERVAL 1 DAY) - INTERVAL 30 DAY AS r_median, |
| 14 | PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY frequency) AS f_median, |
| 15 | PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY monetary) AS m_median |
| 16 | FROM base |
| 17 | ) |
| 18 | SELECT |
| 19 | b.user_id, |
| 20 | DATEDIFF(CURDATE(), b.last_order_time) AS recency, |
| 21 | b.frequency, |
| 22 | b.monetary, |
| 23 | CASE WHEN DATEDIFF(CURDATE(), b.last_order_time) <= t.r_median THEN '高' ELSE '低' END AS r_label, |
| 24 | CASE WHEN b.frequency > t.f_median THEN '高' ELSE '低' END AS f_label, |
| 25 | CASE WHEN b.monetary > t.m_median THEN '高' ELSE '低' END AS m_label |
| 26 | FROM base b, thresholds t; |
上述SQL在聚合阶段使用了窗口函数PERCENTILE_CONT(PostgreSQL原生支持,MySQL 8.0+可通过子查询实现),能够自动计算R、F、M的中位线,实现自适应分层。若业务需求为固定阈值,可将thresholds子查询改为硬编码常量。
两种实现的适用场景与对比
Python版与SQL版各有优势,选取依据主要看数据规模、团队技术栈与实时性要求:
- 数据规模:千万元级别以下的交易明细,Python在单机上足以完成;若数据量达到亿级,建议在数据仓库中直接使用SQL,以免数据搬迁带来的网络开销。
- 实时性:若需要每日定时生成RFM报表,SQL可以在ETL流程中一次性完成;Python更适合在Jupyter Notebook中进行探索性分析,快速迭代模型参数。
- 业务灵活性:Python生态丰富,支持后续的机器学习预测、用户画像标签扩展;SQL则更贴近业务数据库,维护成本低。
在实际项目中,常见做法是:先用Python完成模型验证和阈值调优,形成阈值参数后,编写对应的SQL脚本写入数据仓库,实现每日自动化的RFM分层。
常见误区与调优建议
- 阈值选取不当:仅凭经验设定固定天数或固定金额,容易导致分层失真。建议采用中位数或业务自定义的分位点,使划分更具统计意义。
- 时间窗口不统一:RFM模型的时间窗口应与业务周期匹配,例如电商常用90天、会员运营常用180天。窗口过短会使活跃用户被误判为流失。
- 忽略异常值:大额订单或极端高频用户会影响中位数,导致阈值偏高。可以先对 Monetary 和 Frequency 做百分位裁剪(e.g., 99%分位)后再计算阈值。
- 未进行数据去重:同一用户在同一天多次下单但未做去重,会导致 Frequency 被放大。应在ETL阶段先对订单进行去重或标记为主订单。
调优时建议采用A/B分层对比:选取两组阈值分别生成用户分群,观察后续7天的转化率或复购率变化,以数据驱动阈值的最终确定。
综上所述,RFM模型凭借其结构化、可解释的特性,仍是用户价值分层的主流工具。无论是Python的灵活探索,还是SQL的规模化生产,均能在不同业务场景下快速落地。本文提供的代码示例均已在真实业务数据中验证,可直接迁移使用。希望读者在实践中结合自身业务特征,合理选取实现方式,让数据驱动运营更加精准。





















