RFM模型是衡量客户价值和客户创造利益能力的重要工具和手段。在众多的客户关系管理(CRM)的分析模式中,RFM模型是被广泛提到的。该机械模型通过一个客户的近期购买行为、购买的总体频率以及花了多少钱3项指标来描述该客户的价值状况。
最近一次消费(Recency):最近一次消费意指上一次购买的时候——顾客上一次是几时来店里、上一次根据哪本邮购目录购买东西、什么时候买的车,或在你的超市买早餐最近的一次是什么时候。
消费频率(Frequency):消费频率是顾客在限定的期间内所购买的次数。
消费金额(Monetary):消费金额是所有数据库报告的支柱,也可以验证“帕雷托法则”(Pareto’s Law)——公司80%的收入来自20%的顾客。
理论上M值和F值是一样的,都带有时间范围,指的是一段时间(通常是1年)内的消费金额,在工作中我认为对于一般店铺的类目而言,产品的价格带都是比较单一的,比如:同一品牌美妆类,价格浮动范围基本在某个特定消费群的可接受范围内,加上单一品类购买频次不高,所以对于一般店铺而言,M值对客户细分的作用相对较弱。
在2018.xlsx和2019.xlsx两个数据表中获取'买家会员名', '买家实际支付金额', '订单付款时间'三列的数据合并后存入all.xlsx表格中。
- import pandas as pd
-
- # 读取Excel文件
- df_2018 = pd.read_excel('./data/2018.xlsx')
- df_2019 = pd.read_excel('./data/2019.xlsx')
- print(df_2018.head())
- # 取出相应列
- df_2018 = df_2018[['买家会员名', '买家实际支付金额', '订单付款时间']]
- df_2019 = df_2019[['买家会员名', '买家实际支付金额', '订单付款时间']]
- # 合并
- df_all = pd.concat([df_2018, df_2019])
- # 读取data_all的尾部数据
- print(df_all.tail())
- df_all.to_excel('./data/all.xlsx')
计算 all.xlsx 表格中的空值、最大值、最小值后导出到 result.xlsx 表格中。
- import pandas as pd
-
- """
- 返回缺失值个数以及最大最小值
- """
-
- df = pd.read_excel('./data/all.xlsx') # 读取Excel文件
-
- view = df.describe(percentiles=[], include='all').T # 数据的基本描述 describe()详解:http://8e9.cn/6j1K1
- # view.to_excel('./data/result111.xlsx') # 导出结果 可以查看describe有哪些数据
-
- view['null'] = len(df) - view['count'] # describe()函数自动计算非空值数,需要手动计算空值数
- view = view[['null', 'max', 'min']]
- view.columns = [u'空值数', u'最大值', u'最小值'] # 表头重命名
- view.to_excel('./data/result.xlsx') # 导出结果
计算RFM值:R最近消费间隔、F消费频率、M消费金额。
- import pandas as pd
- import numpy as np
-
- """
- 计算RFM值 R最近消费间隔 F消费频率 M消费金额
- """
-
- # 读取Excel文件
- df = pd.read_excel('./data/all.xlsx')
- data = df[df['订单付款时间'].notnull() & df['买家实际支付金额'] != 0] # 去除空值,订单付款时间非空值才保留 # 去除买家实际支付金额为0的记录
- data = data.copy() # 复制数据
-
- # 订单付款时间和2019-12-31之差-->最近消费时间间隔 消费频率 最近消费间隔 消费金额
- data['最近消费间隔'] = (pd.to_datetime('2019-12-31') - pd.to_datetime(data['订单付款时间'])).values / np.timedelta64(1, "D")
- df11 = data[['订单付款时间', '买家会员名', '买家实际支付金额', '最近消费间隔']]
-
- # 分组计算agg({'键1':'函数1','键2':'函数2'})
- # size属于count_values(计数函数)
- # 根据 买家会员名 分组,买家会员名出现的次数size-->消费评率
- # 因为买家可能多次购买因此会有多条消费时间间隔,取最小的那条
- # 计算多次消费的总金额-->买家实际支付金额
- df1 = df11.groupby('买家会员名').agg({'买家会员名': 'size', '最近消费间隔': 'min', "买家实际支付金额": 'sum'})
-
- df2 = df1.rename(columns={'买家会员名': '消费频率', '买家实际支付金额': '消费总金额'}) # 列重命名rename({'旧列名1':'新列名1',"旧列名2":"新列名2"})
- df2.to_excel('./data/RFM.xlsx')
进行数据转换,为 k-means 聚类模型做准备。
- import pandas as pd
-
- """
- 进行数据转换,为k-means聚类模型做准备
- """
-
- data = pd.read_excel('./data/RFM.xlsx') # 读取Excel文件
- data = data[['最近消费间隔', '消费频率', '消费总金额']] # 提取指定列数据
- data = (data - data.mean(axis=0)) / (data.std(axis=0)) # 标准化处理
- data.columns = ['R', 'F', 'M'] # 表头重命名
- print(data.head()) # 输出部分数据
- data.to_excel('./data/transformdata.xlsx', index=False) # 导出数据
k-means 聚类算法流程: ① 指定需要划分的簇的个数k值(类的个数) ② 随机地选择k个数据对象作为初始的聚类中心(不一定要是我们的样本点) ③ 计算其余的各个数据对象到这个k个初始聚类中心的距离,把数据对象划归到距离它最近的那个中心所处的簇类中; ④ 调整新类并且重新计算出新类的中心。 ⑤ 循环步骤③和④,看中心是否收敛(不变),如果收敛或达到迭代次数则停止循环; ⑥ 结束
- import pandas as pd
- from sklearn.cluster import KMeans
- import matplotlib.pyplot as plt
-
-
- pd.set_option('display.unicode.east_asian_width', True) # 解决数据输出时列名不对齐的问题
-
- data = pd.read_excel('./data/transformdata.xlsx') # 读取数据
- cdata = pd.read_excel('./data/RFM.xlsx') # 读取数据
- cdata = cdata[['买家会员名', '最近消费间隔', '消费频率', '消费总金额']] # 提取指定列数据
- # print("cdata.index", cdata.index)
-
- k = 4
- Kmodel = KMeans(n_clusters=k)
- Kmodel.fit(data)
- clientdata = pd.concat([cdata, pd.Series(Kmodel.labels_, index=cdata.index)], axis=1)
-
- clientdata.columns = list(cdata.columns) + [u'类别'] # 重命名最后一列为“类别”
- clientdata.to_excel('./data/client.xlsx')
- print(clientdata.head())
-
- data_mean = clientdata.groupby('类别').mean() # 按照类别分组统计R, F, M的指标均值
- print(data_mean)
- data_mean.to_excel('./data/client_mean.xlsx')
-
- new = data_mean.mean()
-
- df = data_mean.append(new, ignore_index=True) # 增加一行RFM平均值(忽略索引),判断RFM值的高低
- print(df)
-
- r1 = pd.Series(Kmodel.labels_).value_counts() # 统计每个类别都有多少个
-
- print(r1)
-
- r2 = pd.DataFrame(Kmodel.cluster_centers_) # 聚类中心点
- r = pd.concat([r2, r1], axis=1)
-
- print(r)
-
- r.columns = list(data.columns) + [u'聚类数量']
- r3 = pd.Series(Kmodel.labels_, index=data.index) # 类别标记
- r = pd.concat([data, r3], axis=1) # 数据合并
- r.columns = list(data.columns) + [u'聚类类别']
-
- print(r)
-
- r.to_excel('./data/type.xlsx') # 导出数据
- plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文乱码
- plt.rcParams['axes.unicode_minus'] = False # 解决负号不显示
- # 密度图
- for i in range(k):
- cls = data[r[u'聚类类别'] == i]
- cls.plot(kind='kde', linewidth=2, subplots=True, sharex=False)
- plt.suptitle('客户群=%d;聚类数量=%d' % (i, r1[i]))
-
- plt.show()
客户3 :重要保持客户(F,M高,R略高平均分。高价值客户,需作为VIP客户一对一营销,尽可能延长他们的高消费)
客户2:一般保持客户(F高,消费次数多,是比较忠实的客户,针对这种客户需要多传递促销活动,品牌消息,新品,活动消息)
客户1:一般发展客户(R,F,M都比较低,短时间内在店铺消费过,但是消费次数与消费金额都比较少,是店铺需要发展的潜在客户,应该该用户人群实 施密集的营销信息推送)
客户0:潜在客户(R高,F,M低,说明该客户很长时间都没用在店铺进行交易,并且消费金额与消费次数都很少。这类客户可能只有店铺打折的时候才会出来消费,想办法推动他们的消费心理,否则有流失的风险)