用Python實(shí)現(xiàn)RFM模型——互聯(lián)網(wǎng)產(chǎn)品用戶分層必備技巧

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
dtypes.png

可以看到我們各個(gè)字段的數(shù)據(jù)類型蜂怎,注意到其中的ORDERDATE是字符串類型,而不是時(shí)間Datetime類型置尔,這個(gè)需要在后面進(jìn)行轉(zhuǎn)換杠步。其它字段都正常。

df[df.isna().values == True]
df = df.dropna(how='any', axis=0)
查看空值.png

可以看到上面有些數(shù)據(jù)出現(xiàn)了空值榜轿,由于空值無法計(jì)算出RFM幽歼,所以直接刪除。

plt.figure(figsize=(10,5))
sns.distplot(df.AMOUNTINFO)
數(shù)據(jù)整體分布.png

通過直方圖我們可以很清晰地看到數(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
添加一列R.png

數(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外冀,將其合并成一張新表寡键,如下圖:


RFM表1.png

然后就是最關(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表2.png

好鸭你,那么至此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表3.png

這里的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()
消費(fèi)者分層結(jié)構(gòu).png

可以看到無價(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()
不同分層消費(fèi)者2018年消費(fèi)總額.png

結(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ù)的作用崇堵。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末型诚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鸳劳,更是在濱河造成了極大的恐慌俺驶,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棍辕,死亡現(xiàn)場離奇詭異暮现,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)楚昭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門栖袋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人抚太,你說我怎么就攤上這事塘幅。” “怎么了尿贫?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵电媳,是天一觀的道長。 經(jīng)常有香客問我庆亡,道長匾乓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任又谋,我火速辦了婚禮拼缝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘彰亥。我一直安慰自己咧七,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布任斋。 她就那樣靜靜地躺著继阻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瘟檩,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天犬第,我揣著相機(jī)與錄音,去河邊找鬼芒帕。 笑死歉嗓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的背蟆。 我是一名探鬼主播鉴分,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼带膀!你這毒婦竟也來了志珍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤垛叨,失蹤者是張志新(化名)和其女友劉穎伦糯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嗽元,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡敛纲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了剂癌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淤翔。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖佩谷,靈堂內(nèi)的尸體忽然破棺而出旁壮,到底是詐尸還是另有隱情,我是刑警寧澤谐檀,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布抡谐,位于F島的核電站,受9級特大地震影響桐猬,放射性物質(zhì)發(fā)生泄漏麦撵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一课幕、第九天 我趴在偏房一處隱蔽的房頂上張望厦坛。 院中可真熱鬧五垮,春花似錦乍惊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春莉撇,著一層夾襖步出監(jiān)牢的瞬間呢蛤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工棍郎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留其障,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓涂佃,卻偏偏與公主長得像励翼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子辜荠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容