1.前言
RFM模型即"R"——Recency(最近一次消費(fèi)時(shí)間)饮睬、"F"——Frequency(一段時(shí)間內(nèi)消費(fèi)頻次)胎食、"M"——(一段時(shí)間內(nèi)消費(fèi)總額)。這三個(gè)指標(biāo)可以將我們的用戶劃分成不同的等級和層次肴颊,目的是為了衡量他們的用戶價(jià)值奸例,從而能夠更準(zhǔn)確地將成本和精力花在更精確的用戶層次身上。一個(gè)典型的例子就是針對一個(gè)明顯無意愿的流失用戶赡模,對其繼續(xù)push自己的核心產(chǎn)品田炭,費(fèi)時(shí)費(fèi)力也費(fèi)錢。
2.如何用Python建立RFM模型
RFM模型纺裁,雖然字眼中帶著“模型”二字诫肠,但實(shí)際它根本不需要任何的算法支撐,和數(shù)據(jù)建模中的邏輯回歸欺缘,聚類分析等是完全不同的概念栋豫。因而實(shí)現(xiàn)RFM的工具和方法有很多:SQL, Excel, R等等都能夠做到,當(dāng)然Python也不例外谚殊,RFM模型的核心就是將三個(gè)指標(biāo)進(jìn)行標(biāo)簽化丧鸯,然后根據(jù)實(shí)際場景業(yè)務(wù)需求進(jìn)行分層即可。下面的文章我就通過一個(gè)簡單的例子來通過代碼實(shí)現(xiàn)RFM模型的建立嫩絮。
2.1數(shù)據(jù)導(dǎo)入
鏈接:https://pan.baidu.com/s/1YbZrdsg2dOoe_5lylTwhQw
提取碼:nf2f
數(shù)據(jù)是某電商的一款SKU于2018年的銷售表單丛肢,字段本身只有4個(gè),但是足夠建立RFM模型了剿干。
import os
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.serif'] = ['SimHei']
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus']=False
sns.set_style('ticks', {'font.sans-serif':['simhei','Droid Sans Fallback']})
2.2 數(shù)據(jù)清洗
df = pd.read_csv('sale.csv')
df.drop_duplicates(subset=['ORDERID'],keep='first',inplace=True)
df.dtypes
可以看到我們各個(gè)字段的數(shù)據(jù)類型蜂怎,注意到其中的ORDERDATE是字符串類型,而不是時(shí)間Datetime類型置尔,這個(gè)需要在后面進(jìn)行轉(zhuǎn)換杠步。其它字段都正常。
df[df.isna().values == True]
df = df.dropna(how='any', axis=0)
可以看到上面有些數(shù)據(jù)出現(xiàn)了空值榜轿,由于空值無法計(jì)算出RFM幽歼,所以直接刪除。
plt.figure(figsize=(10,5))
sns.distplot(df.AMOUNTINFO)
通過直方圖我們可以很清晰地看到數(shù)據(jù)的整體分布情況谬盐,呈現(xiàn)偏態(tài)分布甸私,不過沒有負(fù)值,因此不需要處理異常值飞傀,>0的值都是合理的皇型。
df['ORDERDATE'] = pd.to_datetime(df['ORDERDATE'])
df['Datediff'] = (pd.to_datetime('today') - df['ORDERDATE']).dt.days
df
數(shù)據(jù)的最后處理,就是將ORDERDATE轉(zhuǎn)換成Datetime時(shí)間類型砸烦,并添加一列時(shí)間差弃鸦,作為"R"的計(jì)算指標(biāo)。
2.3數(shù)據(jù)分析
R_Agg = df.groupby(by=['USERID'])['Datediff']
R_Agg = R_Agg.agg([('最近一次消費(fèi)','min')])
F_Agg = df.groupby(by=['USERID'])['ORDERID']
F_Agg = F_Agg.agg([('2018年消費(fèi)頻次','count')])
M_Agg = df.groupby(by=['USERID'])['AMOUNTINFO']
M_Agg = M_Agg.agg([('2018年消費(fèi)金額',sum)])
rfm = R_Agg.join(F_Agg).join(M_Agg)
rfm
上面的步驟是分別計(jì)算了RFM模型中的三個(gè)指標(biāo): R,F,M外冀,將其合并成一張新表寡键,如下圖:
然后就是最關(guān)鍵的一步了,通過pd.cut方法,將用戶分層并打上標(biāo)簽西轩,這里我用的分層方法是python中的quantile函數(shù)员舵,因?yàn)槲覀冎暗闹狈綀D看到是偏態(tài)分布,所以取均值來分層的誤差會很大藕畔,這時(shí)候選擇分位數(shù)來分層會更好马僻。
def rfm_convert(x):
rfm_dict = {0:'R',
2:'M'}
try:
for i in range(0,3,2):
bins = x.iloc[:,i].quantile(q=np.linspace(0,1,num=6),interpolation='nearest')
if i == 0:
labels = np.arange(5,0,-1)
else:
labels = np.arange(1,6)
x[rfm_dict[i]] = pd.cut(x.iloc[:,i],bins=bins,labels=labels,include_lowest=True)
except Exception as e:
print(e)
rfm_convert(rfm)
上面這個(gè)函數(shù)的目的是將R和M按照1-5的等級打上標(biāo)簽,給用戶分層注服,但由于F的數(shù)值大多集中在1韭邓,即消費(fèi)頻次只有1次,所以無法使用分位數(shù)方法來均分出5個(gè)等級溶弟,因此我下面額外再使用其它方式將其打上標(biāo)簽:
bins = [1,3,5,12]
labels = np.arange(1,4)
rfm['F'] = pd.cut(rfm['2018年消費(fèi)頻次'], bins=bins, labels=labels, include_lowest=True)
rfm.insert(4,'F',rfm.pop('F'))
rfm
F的分層是由我手動區(qū)分的女淑,由于RFM模型本身就是根據(jù)不同場景和業(yè)務(wù)需求來建立的,因此這里的F我就根據(jù)數(shù)據(jù)的實(shí)際分布情況來手動分層了辜御。
好鸭你,那么至此RFM模型已經(jīng)有了個(gè)雛形,最后要做的擒权,就是打上標(biāo)簽:
rfm_model = rfm.filter(items=['R','F','M'])
def rfm(x):
return x.iloc[0]*3+x.iloc[1]+x.iloc[2]*3
rfm_model['RFM'] = rfm_model.apply(rfm,axis=1)
bins = rfm_model.RFM.quantile(q=np.linspace(0,1,num=9),interpolation='nearest')
labels = ['流失客戶','一般維持客戶','新客戶','潛力客戶','重要挽留客戶','重要深耕客戶','重要喚回客戶','重要價(jià)值客戶']
rfm_model['Label of Customer'] = pd.cut(rfm_model.RFM, bins=bins, labels=labels, include_lowest=True)
rfm_model
這里的RFM分?jǐn)?shù)根據(jù)具體場景和業(yè)務(wù)來分配權(quán)重袱巨,這里我給的權(quán)重比是3:1:3,,那么其實(shí)也可以不要這個(gè)加權(quán)分?jǐn)?shù)碳抄,完全根據(jù)單獨(dú)的R,F,M來標(biāo)簽愉老,比如R>=4、F>1剖效、M>=4的嫉入,算核心用戶。
4.數(shù)據(jù)可視化
以上就是RFM模型了贱鄙,那么有了模型之后劝贸,我們就能直觀地看到各個(gè)層級下的用戶分布以及用戶價(jià)值了:
from pyecharts.charts import Grid, Pie, Bar
from pyecharts import options as opts
tmp = rfm_model.groupby('Label of Customer').size()
t = [list(z) for z in zip(tmp.index.values, tmp.values)]
# 繪制餅圖
pie = (
Pie()
.add('',t,
radius=['30%','75%'],
rosetype='radius',
label_opts=opts.LabelOpts(is_show=True))
.set_global_opts(title_opts=opts.TitleOpts(title='消費(fèi)者分層結(jié)構(gòu)',pos_left='center'),
toolbox_opts=opts.ToolboxOpts(is_show=True),
legend_opts=opts.LegendOpts(orient='vertical',pos_right='2%',pos_top='30%'))
.set_series_opts(label_opts=opts.LabelOpts(formatter='姨谷:pnn3pln%')))
pie.render_notebook()
可以看到無價(jià)值的流失客戶占據(jù)了很大一部分逗宁,但是同時(shí)我們也要關(guān)注到,新客戶的占比量是相當(dāng)大的梦湘,不過瞎颗,單純看占比是沒有意義的,因?yàn)槲覀冏罱K的目的是盈利捌议,因此下面我們再結(jié)合消費(fèi)金額分布情況哼拔,來看下我們實(shí)際應(yīng)該針對哪些層級的用戶進(jìn)行精準(zhǔn)投放:
consumer_r = df.groupby('USERID').AMOUNTINFO.agg([('消費(fèi)總額',sum)])
new_rfm = rfm_model.join(consumer_r)
filter_list = ['流失客戶','新客戶','重要挽留客戶','重要價(jià)值客戶']
new_rfm = new_rfm[new_rfm['Label of Customer'].astype('<U').isin(filter_list)]
new_rfm['Label of Customer'] = new_rfm['Label of Customer'].astype('<U')
tmp = new_rfm.groupby('Label of Customer').消費(fèi)總額.sum()
tmp = tmp.sort_values()
from pyecharts.charts import Bar
yindex = []
for i in list(tmp.values):
yindex.append(int(i))
# 繪制柱狀圖
bar = (Bar(init_opts=opts.InitOpts(theme='white',bg_color='papayawhip'))
.add_xaxis(list(tmp.index.values))
.add_yaxis('消費(fèi)者分層',yindex,color='lightcoral', category_gap='70%')
.set_global_opts(title_opts=opts.TitleOpts(title='不同分層消費(fèi)者2018年消費(fèi)總額',pos_left='center',title_textstyle_opts=opts.TextStyleOpts(color='lightcoral')),
legend_opts=opts.LegendOpts(pos_top='bottom'),
toolbox_opts=opts.ToolboxOpts(is_show=True),
yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(formatter='{value}')))
)
bar.render_notebook()
結(jié)合上面的餅圖,我們可以最終得出結(jié)論瓣颅,雖然新客戶的人群占比是最大的倦逐,但是他們實(shí)際貢獻(xiàn)的金額并不高,所以我們沒有必要在拉新這一塊花費(fèi)太多心思和精力宫补,而應(yīng)該著重將運(yùn)營的重心放在“留存”和“變現(xiàn)”上檬姥,如何留住核心收入來源的“重要價(jià)值用戶”以及通過用戶觸達(dá)的各種方法曾我,召回“重要挽留客戶”,是現(xiàn)階段的任務(wù)健民。
5.總結(jié)
通過上面的RFM模型建立和可視化抒巢,我們其實(shí)能夠發(fā)現(xiàn),通過對用戶的精準(zhǔn)分層秉犹,我們能夠?qū)τ脩魞r(jià)值有個(gè)更直觀的感受蛉谜,從而對互聯(lián)網(wǎng)產(chǎn)品的運(yùn)營,起到點(diǎn)對點(diǎn)服務(wù)的作用崇堵。