4、特征工程(上):RFM模型详解(最近一次消费、频率、金额)、从原始数据中提取RFM特征。
好,咱们进入特征工程环节。
说实话,在客户分群这个领域,特征工程比算法本身重要得多。你想想看,你喂给模型的是垃圾,它吐出来的肯定也是垃圾。我见过太多团队,一上来就调参、换模型,结果效果不好,回头一看,连最基本的RFM都没算清楚。
今天咱们就先把RFM模型吃透。这是客户分群的基石,也是我个人最常用的特征组合。
4.1 什么是RFM模型?
RFM,三个字母,三个维度:
- R(Recency):最近一次消费时间。说白了,就是客户多久没来了。
- F(Frequency):消费频率。客户在一段时间内买了多少次。
- M(Monetary):消费金额。客户总共花了多少钱。
为什么这三个维度这么重要?
我举个例子。你开了一家店,有个客户上个月刚来过,另一个客户一年没来了。你会把资源投给谁?肯定是那个刚来过的,对吧?这就是R的作用。
再比如,一个客户一个月来10次,另一个一个月来1次。谁更忠诚?显然是频率高的那个。这就是F。
至于M,更不用说了。大客户和小客户,服务策略能一样吗?
核心思想:RFM模型认为,客户的未来行为,很大程度上可以由这3个历史行为指标预测。说白了,过去的行为是未来行为最好的预测器。
4.2 从原始数据中提取RFM特征
好,理论说完了,咱们来点实际的。怎么从原始数据里把RFM算出来?
我假设你的原始数据长这样:一张交易记录表,每行是一笔订单。字段至少包含:customer_id(客户ID)、transaction_date(交易日期)、amount(交易金额)。
嗯,这里要注意:原始数据往往很脏。我曾经遇到过日期格式不统一、金额有负数、甚至客户ID重复的情况。所以第一步,永远是数据清洗。
4.2.1 数据清洗与准备
咱们先做几件事:
- 把日期列转成标准格式(比如
datetime类型)。 - 去掉金额为负的记录(可能是退款,但咱们先不考虑)。
- 去掉缺失值。
import pandas as pd
import numpy as np
from datetime import datetime
# 加载原始数据
df = pd.read_csv('transactions.csv')
# 数据清洗
df['transaction_date'] = pd.to_datetime(df['transaction_date'])
df = df[df['amount'] > 0] # 只保留正金额
df = df.dropna(subset=['customer_id', 'transaction_date', 'amount'])
print(f"清洗后数据量: {len(df)}")
我的习惯:在清洗这一步,我会先看一眼数据的基本统计信息,比如 df.describe()。如果金额的最大值比平均值大几百倍,那很可能有异常值,需要进一步处理。
4.2.2 计算R值(最近一次消费时间)
R值怎么算?很简单:用分析截止日期,减去每个客户最近一次消费的日期。差值就是R值。
注意:R值越小,说明客户最近刚来过,活跃度越高。
# 设定分析截止日期(比如今天)
analysis_date = datetime.now()
# 计算每个客户最近一次消费日期
recency_df = df.groupby('customer_id')['transaction_date'].max().reset_index()
recency_df.columns = ['customer_id', 'last_purchase_date']
# 计算R值(天数)
recency_df['R'] = (analysis_date - recency_df['last_purchase_date']).dt.days
print(recency_df.head())
这里有个坑:如果你的数据不是最新的,分析截止日期一定要选对。我曾经犯过这个错,用当前日期去算历史数据,结果R值全偏大,模型完全失效。
4.2.3 计算F值(消费频率)
F值就是每个客户在分析周期内消费了多少次。注意,这里的时间窗口很重要。你是看过去3个月?还是过去1年?
我个人习惯:如果是做月度分析,就看过去3个月;如果是做季度分析,就看过去6个月。具体看业务场景。
# 设定分析时间窗口(比如过去90天)
time_window = 90
cutoff_date = analysis_date - pd.Timedelta(days=time_window)
# 筛选时间窗口内的数据
df_window = df[df['transaction_date'] >= cutoff_date]
# 计算每个客户的消费次数
frequency_df = df_window.groupby('customer_id')['transaction_date'].count().reset_index()
frequency_df.columns = ['customer_id', 'F']
print(frequency_df.head())
避坑指南:我曾经遇到过一个客户,他在时间窗口内只消费了1次,但金额巨大。如果只看F值,他会被归为低频客户。但实际上,他是高价值客户。所以,F和M一定要结合起来看。
4.2.4 计算M值(消费金额)
M值就是每个客户在分析周期内的总消费金额。同样,时间窗口要和F值保持一致。
# 计算每个客户的总消费金额
monetary_df = df_window.groupby('customer_id')['amount'].sum().reset_index()
monetary_df.columns = ['customer_id', 'M']
print(monetary_df.head())
4.2.5 合并RFM特征
最后,把三个表合并到一起,就得到了完整的RFM特征矩阵。
# 合并RFM
rfm_df = recency_df.merge(frequency_df, on='customer_id', how='left')
rfm_df = rfm_df.merge(monetary_df, on='customer_id', how='left')
# 填充缺失值(有些客户可能在时间窗口内没有消费)
rfm_df['F'] = rfm_df['F'].fillna(0)
rfm_df['M'] = rfm_df['M'].fillna(0)
print(rfm_df.head())
你看,代码其实不多。但真正难的是理解每个维度背后的业务含义。
4.3 RFM特征的应用场景
RFM算出来之后,能干嘛?
- 客户分层:把客户分成高价值、低价值、流失风险等群体。
- 精准营销:对R值小的客户发新品推荐,对R值大的客户发召回优惠券。
- 流失预警:R值突然变大,说明客户可能快流失了。
我记得有一次,帮一个电商平台做客户分群。他们之前一直用消费金额来分客户,结果发现很多高消费客户其实已经半年没来了。后来我们用RFM重新分群,把R值高的客户单独拉出来做召回活动,效果立竿见影,复购率提升了15%。
总结一下:RFM模型虽然简单,但非常实用。它把客户行为浓缩成了3个核心指标,是后续所有聚类分析的基础。下一节,咱们会讲怎么对RFM特征做标准化和降维,为聚类算法做准备。
好,今天就到这儿。记住,特征工程是数据挖掘的灵魂。RFM算好了,你的客户分群就成功了一半。