客戶RFM模型分類
客戶類型
最近一次交易距離觀測窗口的天數(黏性)
累計交易頻次(忠誠度)
累計交易成交額(消費能力)
對應場景
重要價值客戶
+
+
+
RFM都很大,優質客戶
重要召回客戶
-
+
+
成交量和成交額都很大,但是最近沒有交易,需要召回
重要發展客戶
+
-
+
成交額大,最近有交易,需要重點識別
重要挽留客戶
-
-
+
成交額大,潛在的價值客戶,需要挽留
潛力客戶
+
+
-
成交量大,且最近有交易,需要挖掘
新客戶
+
-
-
最近有交易,是新客戶,需要推廣
一般維持客戶
-
+
-
成交量大,但是貢獻不大,黏性也不高,一般維持
流失客戶
-
-
-
已經流失的客戶
客戶的價值分析:
(1)客戶羣1 —— FM很高,R較低,可以看做是重要召回客戶
(2)客戶羣2 —— RFM都偏低,屬於低價值客戶
(3)客戶羣3 —— R很高,但FM很低,屬於新客戶
客戶價值排名:
客戶羣1 ——1—— 重要召回
客戶羣2 ——2—— 流失客戶/低價值客戶
客戶羣3 ——3—— 新客戶
客戶特點:
(1)重要召回客戶:
成交量和成交額都很大,但是最近沒有交易,需要發掘其潛力方形
促使這類客戶在本公司消費和合作伙伴處消費。通過客戶價值提升,加強滿意度,促使成爲高價值客戶。
(2)流失客戶/低價值客戶:
客戶所乘坐航班乘坐次數F里程M低以及較長時間無乘坐。
他們可能是在公司打折促銷時纔會乘坐本公司航班。
(3)新客戶:
最近有交易,是新客戶,需要推廣。
增加與這類客戶的互動,瞭解情況,採取一定手段,讓客戶生命進一步成長。
實現步驟
數據探索清洗
RFM屬性設計
維度打分
分值計算
客戶分層
描述統計並可視化
import numpy as np
import pandas as pd
import matplotlib. pyplot as plt
import matplotlib
plt. style. use( 'seaborn' )
plt. rcParams[ "font.sans-serif" ] = [ "SimHei" ]
plt. rcParams[ "axes.unicode_minus" ] = False
數據探索和清洗
df = pd. read_excel( 'PYTHON-RFM實戰數據.xlsx' )
df. head( )
品牌名稱
買家暱稱
付款日期
訂單狀態
實付金額
郵費
省份
城市
購買數量
0
wifi365
叫我李2
2019-01-01 00:17:59
交易成功
186
6
上海
上海市
1
1
wifi365
0cyb1992
2019-01-01 00:59:54
交易成功
145
0
廣東省
廣州市
1
2
wifi365
蘿污萌莉
2019-01-01 07:48:48
交易成功
194
8
山東省
東營市
1
3
wifi365
atblovemyy
2019-01-01 09:15:49
付款以後用戶退款成功,交易自動關閉
84
0
江蘇省
鎮江市
1
4
wifi365
小星期魚
2019-01-01 09:59:33
付款以後用戶退款成功,交易自動關閉
74
0
上海
上海市
1
df = df. loc[ df[ '訂單狀態' ] == '交易成功' , : ]
df. shape
(27793, 9)
print ( "列數 : " , df. shape[ 0 ] )
print ( "行數 : " , df. shape[ 1 ] )
print ( "\n特徵數量 : \n" , df. columns. tolist( ) )
print ( "\n缺失值 : " , df. isnull( ) . sum ( ) . values. sum ( ) )
print ( "\n唯一值 : \n" , df. nunique( ) )
列數 : 27793
行數 : 9
特徵數量 :
['品牌名稱', '買家暱稱', '付款日期', '訂單狀態', '實付金額', '郵費', '省份', '城市', '購買數量']
缺失值 : 1
唯一值 :
品牌名稱 1
買家暱稱 25420
付款日期 27283
訂單狀態 1
實付金額 280
郵費 6
省份 31
城市 345
購買數量 22
dtype: int64
df. dropna( inplace= True )
df. drop_duplicates( inplace= True )
df. reset_index( drop= True , inplace= True )
df. head( 2 )
品牌名稱
買家暱稱
付款日期
訂單狀態
實付金額
郵費
省份
城市
購買數量
0
wifi365
叫我李2
2019-01-01 00:17:59
交易成功
186
6
上海
上海市
1
1
wifi365
0cyb1992
2019-01-01 00:59:54
交易成功
145
0
廣東省
廣州市
1
RFM屬性設計
df1 = df[ [ '買家暱稱' , '付款日期' , '實付金額' ] ]
df1[ '付款日期' ] = pd. to_datetime( df1. 付款日期)
C:\Users\Administrator\anaconda3\lib\site-packages\ipykernel_launcher.py:1: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
"""Entry point for launching an IPython kernel.
df1. head( )
買家暱稱
付款日期
實付金額
0
叫我李2
2019-01-01 00:17:59
186
1
0cyb1992
2019-01-01 00:59:54
145
2
蘿污萌莉
2019-01-01 07:48:48
194
3
重碎疊
2019-01-01 10:00:07
197
4
iho_jann
2019-01-01 10:00:08
168
R
r = df1. groupby( '買家暱稱' ) [ '付款日期' ] . max ( ) . reset_index( )
r. head( )
買家暱稱
付款日期
0
.blue_ram
2019-02-04 17:49:34.000
1
.christiny
2019-01-29 14:17:15.000
2
.willn1
2019-01-11 03:46:18.000
3
.託託m
2019-01-11 02:26:33.000
4
0000妮
2019-06-28 16:53:26.458
r[ 'R' ] = ( pd. to_datetime( '2019-7-1' ) - r[ '付款日期' ] ) . dt. days
r = r[ [ '買家暱稱' , 'R' ] ]
r. head( )
買家暱稱
R
0
.blue_ram
146
1
.christiny
152
2
.willn1
170
3
.託託m
170
4
0000妮
2
F
每個用戶累計的購買頻次:引入一個精確到天的日期標籤,依照買家和日期進行分組,將每個用戶一天內的多筆交易進行合併
df[ '日期標籤' ] = df[ '付款日期' ] . astype( str ) . str [ : 10 ]
df. head( )
品牌名稱
買家暱稱
付款日期
訂單狀態
實付金額
郵費
省份
城市
購買數量
日期標籤
0
wifi365
叫我李2
2019-01-01 00:17:59
交易成功
186
6
上海
上海市
1
2019-01-01
1
wifi365
0cyb1992
2019-01-01 00:59:54
交易成功
145
0
廣東省
廣州市
1
2019-01-01
2
wifi365
蘿污萌莉
2019-01-01 07:48:48
交易成功
194
8
山東省
東營市
1
2019-01-01
3
wifi365
重碎疊
2019-01-01 10:00:07
交易成功
197
0
江蘇省
南京市
1
2019-01-01
4
wifi365
iho_jann
2019-01-01 10:00:08
交易成功
168
0
廣東省
廣州市
1
2019-01-01
df. shape
(27791, 10)
dup = df. groupby( [ '買家暱稱' , '日期標籤' ] ) [ '付款日期' ] . count( ) . reset_index( )
dup. head( )
買家暱稱
日期標籤
付款日期
0
.blue_ram
2019-02-04
1
1
.christiny
2019-01-29
1
2
.willn1
2019-01-11
1
3
.託託m
2019-01-11
1
4
0000妮
2019-06-28
1
f = dup. groupby( '買家暱稱' ) [ '付款日期' ] . count( ) . reset_index( )
f. columns= [ '買家暱稱' , 'F' ]
f. head( )
買家暱稱
F
0
.blue_ram
1
1
.christiny
1
2
.willn1
1
3
.託託m
1
4
0000妮
1
M
df. head( )
品牌名稱
買家暱稱
付款日期
訂單狀態
實付金額
郵費
省份
城市
購買數量
日期標籤
0
wifi365
叫我李2
2019-01-01 00:17:59
交易成功
186
6
上海
上海市
1
2019-01-01
1
wifi365
0cyb1992
2019-01-01 00:59:54
交易成功
145
0
廣東省
廣州市
1
2019-01-01
2
wifi365
蘿污萌莉
2019-01-01 07:48:48
交易成功
194
8
山東省
東營市
1
2019-01-01
3
wifi365
重碎疊
2019-01-01 10:00:07
交易成功
197
0
江蘇省
南京市
1
2019-01-01
4
wifi365
iho_jann
2019-01-01 10:00:08
交易成功
168
0
廣東省
廣州市
1
2019-01-01
sum_me = df. groupby( '買家暱稱' ) [ '實付金額' ] . sum ( ) . reset_index( )
sum_me. columns = [ '買家暱稱' , '總支付金額' ]
comb = pd. merge( sum_me, f, how= 'inner' , on= '買家暱稱' )
comb[ 'M' ] = comb[ '總支付金額' ] / comb[ 'F' ]
comb
買家暱稱
總支付金額
F
M
0
.blue_ram
49
1
49.0
1
.christiny
183
1
183.0
2
.willn1
34
1
34.0
3
.託託m
37
1
37.0
4
0000妮
164
1
164.0
...
...
...
...
...
25414
龍火師
175
1
175.0
25415
龍魔鬼女
87
1
87.0
25416
龜mil寶
497
2
248.5
25417
!謝鵬逗逼?
137
1
137.0
25418
~小邱~
185
1
185.0
25419 rows × 4 columns
創建RFM屬性表
rfm = pd. merge( r, comb, on= '買家暱稱' , how= 'inner' )
rfm = rfm[ [ '買家暱稱' , 'R' , 'F' , 'M' ] ]
rfm
買家暱稱
R
F
M
0
.blue_ram
146
1
49.0
1
.christiny
152
1
183.0
2
.willn1
170
1
34.0
3
.託託m
170
1
37.0
4
0000妮
2
1
164.0
...
...
...
...
...
25414
龍火師
84
1
175.0
25415
龍魔鬼女
72
1
87.0
25416
龜mil寶
11
2
248.5
25417
!謝鵬逗逼?
24
1
137.0
25418
~小邱~
158
1
185.0
25419 rows × 4 columns
rfm[ [ 'R' , 'F' , 'M' ] ] . describe( [ 0.01 , 0.25 , 0.5 , 0.9 , 0.99 ] ) . T
count
mean
std
min
1%
25%
50%
90%
99%
max
R
25419.0
97.120225
58.452032
0.0
2.0
37.0
105.0
170.0
170.0
180.0
F
25419.0
1.077580
0.303221
1.0
1.0
1.0
1.0
1.0
2.0
7.0
M
25419.0
128.157942
75.858900
30.0
30.0
73.0
117.0
233.0
272.0
4647.0
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Fiqmb1AF-1589444596669)(attachment:%E5%9B%BE%E7%89%87.png)]
rfm. F. value_counts( )
1 23668
2 1559
3 169
4 19
5 3
7 1
Name: F, dtype: int64
維度打分
rfm[ 'R-score' ] = pd. cut( rfm[ 'R' ] , bins= [ 0 , 30 , 60 , 90 , 120 , 1000000 ] , labels= [ 5 , 4 , 3 , 2 , 1 ] , right= False ) . astype( float )
rfm. head( )
買家暱稱
R
F
M
R-score
0
.blue_ram
146
1
49.0
1.0
1
.christiny
152
1
183.0
1.0
2
.willn1
170
1
34.0
1.0
3
.託託m
170
1
37.0
1.0
4
0000妮
2
1
164.0
5.0
rfm[ 'F-score' ] = pd. cut( rfm[ 'F' ] , bins= [ 1 , 2 , 3 , 4 , 5 , 1000000 ] , labels= [ 1 , 2 , 3 , 4 , 5 ] , right= False ) . astype( float )
rfm[ 'M-score' ] = pd. cut( rfm[ 'M' ] , bins= [ 0 , 50 , 100 , 150 , 200 , 1000000 ] , labels= [ 1 , 2 , 3 , 4 , 5 ] , right= False ) . astype( float )
rfm. head( )
買家暱稱
R
F
M
R-score
F-score
M-score
0
.blue_ram
146
1
49.0
1.0
1.0
1.0
1
.christiny
152
1
183.0
1.0
1.0
4.0
2
.willn1
170
1
34.0
1.0
1.0
1.0
3
.託託m
170
1
37.0
1.0
1.0
1.0
4
0000妮
2
1
164.0
5.0
1.0
4.0
第二輪打分:和平均值做比較
rfm[ 'R是否大於均值' ] = ( rfm[ 'R-score' ] > rfm[ 'R-score' ] . mean( ) ) * 1
rfm[ 'F是否大於均值' ] = ( rfm[ 'F-score' ] > rfm[ 'F-score' ] . mean( ) ) * 1
rfm[ 'M是否大於均值' ] = ( rfm[ 'M-score' ] > rfm[ 'M-score' ] . mean( ) ) * 1
rfm
買家暱稱
R
F
M
R-score
F-score
M-score
R是否大於均值
F是否大於均值
M是否大於均值
0
.blue_ram
146
1
49.0
1.0
1.0
1.0
0
0
0
1
.christiny
152
1
183.0
1.0
1.0
4.0
0
0
1
2
.willn1
170
1
34.0
1.0
1.0
1.0
0
0
0
3
.託託m
170
1
37.0
1.0
1.0
1.0
0
0
0
4
0000妮
2
1
164.0
5.0
1.0
4.0
1
0
1
...
...
...
...
...
...
...
...
...
...
...
25414
龍火師
84
1
175.0
3.0
1.0
4.0
1
0
1
25415
龍魔鬼女
72
1
87.0
3.0
1.0
2.0
1
0
0
25416
龜mil寶
11
2
248.5
5.0
2.0
5.0
1
1
1
25417
!謝鵬逗逼?
24
1
137.0
5.0
1.0
3.0
1
0
1
25418
~小邱~
158
1
185.0
1.0
1.0
4.0
0
0
1
25419 rows × 10 columns
客戶分層
rfm[ '人羣數值' ] = rfm[ 'R是否大於均值' ] * 100 + rfm[ 'F是否大於均值' ] * 10 + rfm[ 'M是否大於均值' ]
rfm
買家暱稱
R
F
M
R-score
F-score
M-score
R是否大於均值
F是否大於均值
M是否大於均值
人羣數值
0
.blue_ram
146
1
49.0
1.0
1.0
1.0
0
0
0
0
1
.christiny
152
1
183.0
1.0
1.0
4.0
0
0
1
1
2
.willn1
170
1
34.0
1.0
1.0
1.0
0
0
0
0
3
.託託m
170
1
37.0
1.0
1.0
1.0
0
0
0
0
4
0000妮
2
1
164.0
5.0
1.0
4.0
1
0
1
101
...
...
...
...
...
...
...
...
...
...
...
...
25414
龍火師
84
1
175.0
3.0
1.0
4.0
1
0
1
101
25415
龍魔鬼女
72
1
87.0
3.0
1.0
2.0
1
0
0
100
25416
龜mil寶
11
2
248.5
5.0
2.0
5.0
1
1
1
111
25417
!謝鵬逗逼?
24
1
137.0
5.0
1.0
3.0
1
0
1
101
25418
~小邱~
158
1
185.0
1.0
1.0
4.0
0
0
1
1
25419 rows × 11 columns
基於人羣數值給客戶打標籤
rfm[ '人羣類型' ] = rfm[ '人羣數值' ] . map ( {
111 : '重要價值客戶' ,
11 : '重要召回客戶' ,
101 : "重要發展客戶" ,
1 : "重要挽留客戶" ,
110 : '潛力客戶' ,
100 : '新客戶' ,
10 : '一般客戶' ,
0 : '流失客戶'
} )
rfm. head( )
買家暱稱
R
F
M
R-score
F-score
M-score
R是否大於均值
F是否大於均值
M是否大於均值
人羣數值
人羣類型
0
.blue_ram
146
1
49.0
1.0
1.0
1.0
0
0
0
0
流失客戶
1
.christiny
152
1
183.0
1.0
1.0
4.0
0
0
1
1
重要挽留客戶
2
.willn1
170
1
34.0
1.0
1.0
1.0
0
0
0
0
流失客戶
3
.託託m
170
1
37.0
1.0
1.0
1.0
0
0
0
0
流失客戶
4
0000妮
2
1
164.0
5.0
1.0
4.0
1
0
1
101
重要發展客戶
統計描述並可視化
描述統計
count = rfm[ '人羣類型' ] . value_counts( ) . reset_index( )
count. columns = [ '客戶類型' , '人數' ]
count[ '人數佔比' ] = count[ '人數' ] / count[ '人數' ] . sum ( )
count
客戶類型
人數
人數佔比
0
重要挽留客戶
7336
0.288603
1
流失客戶
6681
0.262835
2
重要發展客戶
5427
0.213502
3
新客戶
4224
0.166175
4
重要價值客戶
756
0.029742
5
潛力客戶
450
0.017703
6
重要召回客戶
360
0.014163
7
一般客戶
185
0.007278
rfm[ '購買總金額' ] = rfm[ 'F' ] * rfm[ 'M' ]
rfm. head( )
買家暱稱
R
F
M
R-score
F-score
M-score
R是否大於均值
F是否大於均值
M是否大於均值
人羣數值
人羣類型
購買總金額
0
.blue_ram
146
1
49.0
1.0
1.0
1.0
0
0
0
0
流失客戶
49.0
1
.christiny
152
1
183.0
1.0
1.0
4.0
0
0
1
1
重要挽留客戶
183.0
2
.willn1
170
1
34.0
1.0
1.0
1.0
0
0
0
0
流失客戶
34.0
3
.託託m
170
1
37.0
1.0
1.0
1.0
0
0
0
0
流失客戶
37.0
4
0000妮
2
1
164.0
5.0
1.0
4.0
1
0
1
101
重要發展客戶
164.0
m = rfm. groupby( '人羣類型' ) [ '購買總金額' ] . sum ( ) . reset_index( )
m. columns = [ '客戶類型' , '消費金額' ]
m[ '金額佔比' ] = mon[ '消費金額' ] / mon[ '消費金額' ] . sum ( )
m
客戶類型
消費金額
金額佔比
0
一般客戶
25803.0
0.007349
1
新客戶
270869.0
0.077146
2
流失客戶
444679.0
0.126649
3
潛力客戶
64075.0
0.018249
4
重要價值客戶
269230.0
0.076679
5
重要發展客戶
981893.0
0.279652
6
重要召回客戶
116665.0
0.033227
7
重要挽留客戶
1337906.0
0.381048
可視化
result = pd. merge( count, m, on= '客戶類型' )
result. head( )
客戶類型
人數
人數佔比
消費金額
金額佔比
0
重要挽留客戶
7336
0.288603
1337906.0
0.381048
1
流失客戶
6681
0.262835
444679.0
0.126649
2
重要發展客戶
5427
0.213502
981893.0
0.279652
3
新客戶
4224
0.166175
270869.0
0.077146
4
重要價值客戶
756
0.029742
269230.0
0.076679
plt. bar( result[ '客戶類型' ] , result[ '人數佔比' ] )
plt. plot( result[ '客戶類型' ] , result[ '金額佔比' ] , 'yo-' ) ;