用python輕鬆實現數據分析中的RFM建模
- 2020 年 3 月 10 日
- 筆記
↑ 關注 + 星標 ~ 有趣的不像個技術號
每晚九點,我們準時相約
大家好,我是黃同學
今天給大家分享的是如何用python實現RFM建模。
RFM模型的含義
RFM模型是衡量客戶價值和客戶創利能力的重要工具和手段。在眾多的客戶關係管理(CRM)的分析模式中,RFM模型是被廣泛提到的。
該模型通過一個客戶的近期購買行為(R)、購買的總體頻率(F)以及花了多少錢(M)三項指標來描述該客戶的價值狀況,從而能夠更加準確地將成本和精力更精確的花在用戶層次身上,實現針對性的營銷。
詳細來說,R指的是客戶最後一次下單時間距離今天多少天了,該指標與客戶的復購和流失直接相關。F指標指的是客戶的下單頻率,即客戶在某個時間段內共消費了多少次,該指標用于衡量客戶消費的活躍度。M指標指的是客戶在該時間段內共消費了多少錢,該指標用於反應客戶對於公司的貢獻值。

RFM分析的前提條件:
- 最近有過交易行為的客戶,再次發生交易行為的可能性高於最近沒有交易行為的客戶。
- 交易頻率高的客戶,比交易頻率低的客戶,更有可能再次發生交易行為。
- 過去所有交易總金額較大的客戶,比過去所有交易總金額較小的客戶,更有消費積極性。
原始數據
原始數據集在這裡先展示一下,讓你對這個數據有一個主觀印象。

(點擊放大)
數據處理
1)什麼是R、F、M呢?
「R」表示最近一次消費時間距離今天共有多少天。什麼是最近一次消費時間呢?如果同一個人在不同時間有不同多個訂單,那麼該時間距離當前時間的差值的最小值,就是最近一次消費時間。
「F」表示某個人一段時間內的消費頻次。
「M」表示一段時間內的消費總額。
2)熟悉數據集
熟悉數據集,就是在進行數據處理之前,應該先熟悉數據,只有對數據充分熟悉之後,才能更好的進行分析。
熟悉數據常用的方法和屬性有shape、head()、tail()、sample()、info()、describe()。
df = pd.read_excel(r"C:Users黃偉DesktopRFM_ModelRFM.xlsx") display(df.shape) display(df.sample(5))
結果如下:

從上述結果中可以發現:這筆數據總共有28833行條記錄,12列。觀察上圖,可以清楚地看到每一列數據代表什麼含義。
3)保留有效數據
針對此數據集,我們先說一下什麼是「有效數據」。「有效數據」指的就是有效購買,也就是說對應的「訂單狀態」欄位顯示的是「交易成功」,對於「退款」的記錄,我們就直接將這個數據剔除掉。
display("剔除之前共有:"+ str(df.shape[0]) + "條記錄") df = df[df["訂單狀態"]=="交易成功"] display("剔除之後共有:"+ str(df.shape[0]) + "條記錄")
結果如下:

4)選取有效欄位
通過上面的分析,我們知道了「R」、「F」、「M」三個指標的概念。鑒於此,我們只需要選取"買家昵稱",「付款時間」,"實付金額"這三個欄位,用於RFM模型的構建,其餘欄位用處不大,因此刪除其餘欄位。
df1 = df[["買家昵稱","付款時間","實付金額"]] df1.index = np.arange(df1.shape[0]) display(df1.shape) display(df1.head())
結果如下:

5)缺失值處理
df1.isnull().sum(axis=0)
結果如下:

從上述結果中可以發現:各欄位中沒有缺失值,因此不需要做任何處理。
RFM建模過程
1)計算RFM三個指標
① 增加「天數」欄位,用於計算「R」指標
針對上述「R」、「F」、「M」三個指標的概念,我們對數據做一定的處理。由於 「R」表示的是最近一次消費時間距離今天共有多少天。但是數據集中只有每一天的「付款時間」欄位。因此計算RFM指標之前,需要事先添加一個「天數」欄位,求出每個「付款時間」距今共有多少天。「天數」越小,就表示最近一次的消費時間。
然後針對上述處理後的數據,做一個數據透視表。以「買家昵稱」作為分組欄位,對「天數」求最小值;對「付款昵稱」計數;對「實付金額」求和,就可以得到我們想要的RFM三個指標。
df1["付款時間"] = pd.to_datetime(df1["付款時間"]) df1["天數"] = (pd.to_datetime("today")-df1["付款時間"]).dt.days display(df1.sample(10))
結果如下:

② 計算RFM三個指標
df2 = pd.pivot_table(df1,index="買家昵稱", values=["買家昵稱","天數","實付金額"], aggfunc={"買家昵稱":"count","天數":"min","實付金額":"sum"}) df2 = df2[["天數","買家昵稱","實付金額"]] df2.columns = ["R","F","M"] df2.reset_index() display(df2.shape) display(df2.head(10))
結果如下:

2)用戶分層打分
通過上述分析,我們已經得到了每一個用戶的「R」、「F」、「M」值。接下來要做的,就是給每一個用戶進行分層。這裡我們需要建立一個評判標準,由於RFM模型本身就是需要根據不同場景和業務需求來建立的,因此這個分層標準,也是需要我們溝通業務後,得到最後的分層標準。
以R指標為例進行說明,根據上表我們知道,R表示每個用戶最後一次購買時間距離今天共經歷了多少天。當這個值越小,說明用戶近期又回購了此產品;當這個值越大,說明用戶已經好久沒有再次購買產品了,這個用戶很有可能流失掉了(猜測)。
基於上述分析,我們採用通用的5分制打分法,對RFM進行分類打分。
說明:由於這個數據集時間較早,因此計算出來的最近一次購買時間距離今天的天數,會特別大,但是沒有關係,我們演示這個案例只是為了說明RFM模型的建模過程,實際中,肯定是過幾個月進行一次RFM建模是比較好的,這裡你只需要知道原理就好。
對於R指標來說:我們可以求出,R指標最小值是660天,我們以30天作為時間間隔,660-690天,打5分;690-720,打4分;720-750打3分;750-780打2分;>780,打1分。
對於F指標來說:我們可以求出,F指標最小值是1次,我們以1次作為時間間隔,0-2,打1分;2-3,打2分;3-4,打3分;4-5,打4分;>5,打5分。
對於M指標來說:我們可以求出,M指標最小值是0.005元,我們以500元作為時間間隔,0-50,打1分;50-100,打2分;100-150,打3分;150-200,打4分;>200,打5分。
至此,我們已經建立好了打分標準,下面我們開始對每個用戶進行分類打分。
def func1(x): if x>=660 and x<690: return 5 elif x>=690 and x<720: return 4 elif x>=720 and x<750: return 3 elif x>=750 and x<780: return 2 elif x>=780: return 1 def func2(x): if x>=0 and x<2: return 1 elif x>=2 and x<3: return 2 elif x>=3 and x<4: return 3 elif x>=4 and x<5: return 4 elif x>=5: return 5 def func3(x): if x>=0 and x<50: return 1 elif x>=50 and x<100: return 2 elif x>=100 and x<150: return 3 elif x>=150 and x<200: return 4 elif x>=200: return 5 df2["R-SCORE"] = df2["R"].apply(func1) df2["F-SCORE"] = df2["F"].apply(func2) df2["M-SCORE"] = df2["M"].apply(func3) df2.sample(10)
結果如下:

3)用戶貼標籤
前面的步驟中,我們已經根據業務需求,對RFM指標進行了分類打分,得到了R-SCORE、F-SCORE、M-SCORE三個指標。接下來,我們需要給每個用戶貼標籤,這裡有兩種方式可以進行用戶貼標籤。
第一種:根據業務場景和業務來分配全重,對於RFM這3個指標,你更看重哪個指標,就賦予它相應較大一點的權重,比如說賦予的權重是3:1:2。
第二種:完全根據單獨的RFM標籤來計算,比如說:R-SCORE>avg(R-SCORE)、F-SCORE>avg(F-SCORE)、M-SCORE>avg(M-SCORE),表示一個客戶近期有購買,購買頻率高於所有客戶平均購買頻率,購買金額高於所有客戶的平均購買金額,因此我們貼上一個「重要挽留客戶」的標籤。
下面以第二種方法進行說明。根據上述敘述,每個指標有兩種情況,要麼>avg(),要麼<avg()。由排列組合的知識,共有8種組合情況,當指標>avg(),我們記為1;當指標<avg(),我們記為0。因此可以得到如下的二維表格。

① 第一步
avg_r = df2["R-SCORE"].mean() avg_f = df2["F-SCORE"].mean() avg_m = df2["M-SCORE"].mean() display(avg_r,avg_f,avg_m)
結果如下:

② 第二步
def func1(x): if x>avg_r: return 1 else: return 0 def func2(x): if x>avg_f: return 1 else: return 0 def func3(x): if x>avg_m: return 1 else: return 0 df2["R-SCORE是否大於均值"] = df2["R-SCORE"].apply(func1) df2["F-SCORE是否大於均值"] = df2["F-SCORE"].apply(func1) df2["M-SCORE是否大於均值"] = df2["M-SCORE"].apply(func1) display(df2.sample(10))
結果如下:

③ 第三步
def functions(x): if x.iloc[0]==1 and x.iloc[1]==1 and x.iloc[2]==1: return "重要價值客戶" elif x.iloc[0]==1 and x.iloc[1]==1 and x.iloc[2]==0: return "潛力客戶" elif x.iloc[0]==1 and x.iloc[1]==0 and x.iloc[2]==1: return "重要深耕客戶" elif x.iloc[0]==1 and x.iloc[1]==0 and x.iloc[2]==0: return "新客戶" elif x.iloc[0]==0 and x.iloc[1]==1 and x.iloc[2]==1: return "重要喚回客戶" elif x.iloc[0]==0 and x.iloc[1]==1 and x.iloc[2]==0: return "一般客戶" elif x.iloc[0]==0 and x.iloc[1]==0 and x.iloc[2]==1: return "重要挽回客戶" elif x.iloc[0]==0 and x.iloc[1]==0 and x.iloc[2]==0: return "流失客戶" df2["標籤"] = df2[["R-SCORE是否大於均值","F-SCORE是否大於均值","M-SCORE是否大於均值"]].apply(functions,axis=1) df2.sample(10)
結果如下:

4)可視化展示
① 繪製不同類型客戶的人數對比
df3 = df2.groupby("標籤").agg({"標籤":"count"}) df3["不同客戶的佔比"] = df3["標籤"].apply(lambda x:x/np.sum(df3["標籤"])) df3 = df3.sort_values(by="標籤",ascending=True) plt.figure(figsize=(6,4),dpi=100) x = df3.index y = df3["標籤"] plt.barh(x,height=0.5,width=y,align="center") plt.title("不同類型客戶的人數對比") for x,y in enumerate(y): plt.text(y+450,x,y,ha="center",va="center",fontsize=14) plt.xticks(np.arange(0,10001,2000)) plt.tight_layout() plt.savefig("不同類型客戶的人數對比",dpi=300)
結果如下:

② 繪製不同類型客戶人數佔比圖
df3 = df2.groupby("標籤").agg({"標籤":"count"}) df3["不同客戶的佔比"] = df3["標籤"].apply(lambda x:x/np.sum(df3["標籤"])) df3 = df3.sort_values(by="標籤",ascending=True) plt.figure(figsize=(7,4),dpi=100) x = df3["不同客戶的佔比"] labels = ['潛力客戶', '一般客戶', '重要價值客戶', '重要喚回客戶', '重要深耕客戶', '新客戶', '重要挽回客戶', '流失客戶'] colors = ['#9999ff','#ff9999','#7777aa','#2442aa','#dd5555','deeppink','yellowgreen','lightskyblue'] explode = [0,0,0,0,0,0,0,0] patches,l_text = plt.pie(x,labels=labels,colors=colors, explode=explode,startangle=90,counterclock=False) for t in l_text: t.set_size(0) plt.axis("equal") plt.legend(loc=(0.001,0.001),frameon=False) plt.title("不同類型客戶人數佔比圖") plt.savefig("不同類型客戶人數佔比圖",dpi=300)
結果如下:

③ 繪製不同類型客戶累計消費金額
df3 = df2.groupby("標籤").agg({"M":"sum"}) df3["M"] = df3["M"].apply(lambda x:round(x)) df3["不同客戶的佔比"] = df3["M"].apply(lambda x:x/np.sum(df3["M"])) df3 = df3.sort_values(by="M",ascending=True) plt.figure(figsize=(6,4),dpi=100) x = df3.index y = df3["M"] plt.barh(x,height=0.5,width=y,align="center") plt.title("不同類型客戶累計消費金額") for x,y in enumerate(y): plt.text(y+45000,x,y,ha="center",va="center",fontsize=14) plt.xticks(np.arange(0,700001,100000)) plt.tight_layout() plt.savefig("不同類型客戶累計消費金額",dpi=300)
結果如下:

④ 繪製不同類型客戶金額佔比圖
df3 = df2.groupby("標籤").agg({"M":"sum"}) df3["M"] = df3["M"].apply(lambda x:round(x)) df3["不同客戶的佔比"] = df3["M"].apply(lambda x:x/np.sum(df3["M"])) df3 = df3.sort_values(by="M",ascending=True) plt.figure(figsize=(7,4),dpi=100) x = df3["不同客戶的佔比"] labels = ['潛力客戶', '一般客戶', '重要價值客戶', '重要喚回客戶', '重要深耕客戶', '新客戶', '重要挽回客戶', '流失客戶'] colors = ['#9999ff','#ff9999','#7777aa','#2442aa','#dd5555','deeppink','yellowgreen','lightskyblue'] explode = [0,0,0,0,0,0,0,0] patches,l_text= plt.pie(x,labels=labels,colors=colors, explode=explode,startangle=90,counterclock=False) for t in l_text: t.set_size(0) plt.axis("equal") plt.legend(loc=(0.001,0.001),frameon=False) plt.title("不同類型客戶金額佔比圖") plt.savefig("不同類型客戶金額佔比圖",dpi=300)
結果如下:

結論
① 通過上面的可視化展示可以發現,重要挽回客戶7102人,雖然只佔總人數的28%,但是他們的累計消費金額卻是最高的,達到了639763元。重要挽回客戶指的是「做出最大購買,但很久沒有回來購買」,這一部分人已經瀕臨流失邊緣,極大可能會流失,但是這一部分人對於公司的實際貢獻來說,具有很大價值。因此可以採取重點聯繫或拜訪的形式,調查回購率低的原因,從而提高留存率。
② 通過上面的可視化展示可以發現,新客戶5002人,也佔總人數的20%,他們的累計消費金額也達到了134085元。這一部分人最近有交易,交易頻率不高,金額小,很容易丟失,但是有推廣價值。針對這一部分群體,我們可以採取社區活動這種形式,提供免費試用產品,提高客戶興趣,對於創建品牌知名度很有必要。
③ 通過上面的可視化展示可以發現,流失客戶8601人,具有最大佔比34%,他們的累計消費金額也達到了200854元。流失客戶表示最後一次購買的時間很長,金額小,訂單數少,屬於冬眠客戶。針對上面的新客戶和這裡的流失客戶的處理有兩種辦法,如果說這部分人的消費金額較大,對公司的價值較大,就需要想辦法恢復這部分客戶的興趣;如果說這部分人的消費金額較小,暫時放棄無價值用戶,主要將運營的中心放在如何留住核心收入來源的客戶群體之上,以及通過各種方式召回「重要挽回客戶」。

如果大家覺得文章對你有幫助,歡迎掃描下方二維碼關注黃同學的CSDN部落格