基于RFM模型的客戶分群和K-Means聚類分析

項(xiàng)目描述

數(shù)據(jù)源是來自Kaggle的一個(gè)跨國數(shù)據(jù)集,其中包含2010年12月12日至2011年12月9日期間發(fā)生的所有在英國注冊(cè)的非商店在線零售業(yè)務(wù)的交易。該公司主要銷售獨(dú)特的全場(chǎng)禮品,并且大部分客戶是批發(fā)商。分析目的是按照RFM模型對(duì)客戶進(jìn)行分級(jí),以用戶的實(shí)際購買行為數(shù)據(jù)作為基礎(chǔ),進(jìn)行用戶群體的劃分,再基于不同分類信息,分解成不同群體針對(duì)運(yùn)營(yíng),從而使企業(yè)能更有效的獲取客戶、使客戶更加滿意、留住客戶成為高價(jià)值客戶、避免客戶流失。
數(shù)據(jù)一覽
數(shù)據(jù)形狀為:542k 行x 8列,8個(gè)字段分別為發(fā)票號(hào),發(fā)票日期,商品碼,商品描述,數(shù)量,單價(jià),顧客ID,國家。

數(shù)據(jù)

分析思路

R(Recency): 表示客戶最近一次購買的時(shí)間距離現(xiàn)在有多遠(yuǎn)
F(Frequency): 表示用戶在定義時(shí)間段內(nèi)購買產(chǎn)品或服務(wù)的次數(shù)
M(Monetary): 表示用戶在定義時(shí)間段內(nèi)購買產(chǎn)品或服務(wù)的金額
按照每個(gè)指標(biāo)取值不同分為八類客戶,包括重要價(jià)值客戶、重要發(fā)展客戶、重要保持客戶、重要挽留客戶、一般價(jià)值客戶、一般發(fā)展客戶、一般保持客戶、一般挽留客戶等八類用戶

數(shù)據(jù)清洗

0.計(jì)算每單的總價(jià),添加Amount列。查看整體數(shù)據(jù)情況,發(fā)現(xiàn)描述和顧客ID列有缺失值,顧客ID類型為浮點(diǎn)型不符合業(yè)務(wù)邏輯

整體的數(shù)據(jù)信息

1.把顧客ID類型轉(zhuǎn)為字符串
df['CustomerID']=df['CustomerID'].astype(str)
2.已知訂單號(hào)中有“c”的是取消訂單,需要?jiǎng)h除這部分
df=df.drop(df[df['InvoiceNo'].str.contains('C')].index)
3.給顧客ID缺失的行加上NULL值
df['CustomerID'] = df['CustomerID'].fillna('NULL')
4.將訂單時(shí)間數(shù)據(jù)類型轉(zhuǎn)為日期
df['InvoiceDate']=pd.to_datetime(df['InvoiceDate'])
5.計(jì)算訂單日期距離所有訂單日期最大值的天數(shù)
df['Diff']=max(df['InvoiceDate'])-df['InvoiceDate']
df['Diff']=df['Diff'].dt.days
6.刪除不需要的商品描述特征
df.drop(['Description'],axis=1,inplace=True)
7.以客戶ID分組,求最近消費(fèi)時(shí)間距離時(shí)間基線的最小值,得到R值

df_r=df.groupby('CustomerID')['Diff'].min()
df_r=df_r.reset_index()
df_r=df_r.drop(df_r[df_r['CustomerID']=='NULL'].index)

8.求出每位顧客在時(shí)間周期內(nèi)消費(fèi)次數(shù),得到F值,(數(shù)據(jù)源中一個(gè)訂單會(huì)包含多種產(chǎn)品,但是每種產(chǎn)品訂單都會(huì)產(chǎn)生一條記錄,注意去重)

#選出求消費(fèi)金額需要的列
df_f_1=df.loc[:,['InvoiceNo','CustomerID','Amount']]
#刪除客戶ID為空的記錄
df_f_1=df_f_1.drop(df_f_1[df_f_1['CustomerID']=='NULL'].index)
#以訂單號(hào)分組,求每筆訂單總金額
df_f_2=df_f_1.groupby('InvoiceNo')['Amount'].sum()
df_f_2=df_f_2.reset_index()
#刪除Amount小于0的
df_f_2=df_f_2.drop(df_f_2[(df_f_2['Amount']<=0)].index)
# InvoiceNo相同的算一次購買頻次,去除InvoiceNo相同的行
df_f_3=df_f_1.loc[:,['InvoiceNo','CustomerID']]
df_f_3=df_f_3.drop_duplicates()
#在整理的時(shí)候看到unique可以進(jìn)行分組后去重統(tǒng)計(jì),這里還可以簡(jiǎn)化一下
df_f=df_f_3.groupby("CustomerID")['InvoiceNo'].count()
df_f=df_f.reset_index()

9.表合并
df_data=pd.merge(df_f_2,df_f_3,on='InvoiceNo',how='left')

訂單號(hào),金額,顧客ID表

10.計(jì)算顧客消費(fèi)金額,得到M值,最后合并得到RFM值表

df_m=df_data.groupby('CustomerID')['Amount'].sum()
df_m=df_m.reset_index()
#合并三張表
df_rf=pd.merge(df_r,df_f,on='CustomerID')
df_rfm=pd.merge(df_rf,df_m,on='CustomerID')
df_rfm.columns = ['CustomerID','R','F','M']
df_rfm.tail()
RFM表

11.對(duì)得到三個(gè)指標(biāo)進(jìn)行分區(qū),映射級(jí)別

# 分級(jí),cut函數(shù)把數(shù)據(jù)離散化,按照指定區(qū)間劃分?jǐn)?shù)據(jù)組,打上標(biāo)簽
R_bins = [0,30,90,180,360,720]
F_bins = [0,10,20,50,100,250]
M_bins = [0,500,5000,10000,30000,300000]
R_score=pd.cut(df_rfm['R'],R_bins,right='False',labels=[5,4,3,2,1],include_lowest=True)
F_score=pd.cut(df_rfm['F'],F_bins,right='False',labels=[1,2,3,4,5])
M_score=pd.cut(df_rfm['M'],M_bins,right='False',labels=[1,2,3,4,5])
#連接數(shù)據(jù)源和標(biāo)簽數(shù)據(jù)
df_rfm_1=pd.concat([df_rfm,R_score,F_score,M_score],axis=1)
df_rfm_1.columns=['CustomerID','R','F','M','R_score','F_score','M_score']
#求每個(gè)指標(biāo)的均值,作為評(píng)判標(biāo)準(zhǔn)
R_mean=df_rfm_1['R_score'].astype(float).mean()
F_mean=df_rfm_1['F_score'].astype(float).mean()
M_mean=df_rfm_1['M_score'].astype(float).mean()
#分類函數(shù),映射
def rank_R(x):
    if x>R_mean:
        return '高'
    else:
        return '低'
df_rfm_1['R_rank']=df_rfm_1['R_score'].apply(rank_R)
#得到最后的客戶分級(jí)
df_rfm['value']=df_rfm['R_rank'].str[:]+df_rfm['F_rank'].str[:]+df_rfm['M_rank'].str[:]
df_rfm.head()
RFM分值

按照RFM分值對(duì)顧客分類

def trans_value(x):
    if x=='高高高':
        return '高價(jià)值客戶'
    elif x=='高低高':
        return '重點(diǎn)深耕客戶'
    elif x=='低高高':
        return '重點(diǎn)喚回客戶'
    elif x=='低低高':
        return '重點(diǎn)挽留客戶'
    elif x=='高高低':
        return '潛力客戶'
    elif x=='高低低':
        return '新客戶'
    elif x=='低高低':
        return '一般保持客戶'
    else:
        return '流失客戶'
分類完成
  1. 客戶類型占比
C_type = df_rfm['Rank'].value_counts()
c_num=C_type.tolist()
c_type = C_type.index.tolist()
pie = (
    charts.Pie()
    .add("",[list(z) for z in zip(c_type, c_num)],
         rosetype="radius",
         radius=["30%","55%"],
         label_opts=opts.LabelOpts(formatter='占比u0z1t8os%'),
         center=["50%","40%"])
#          label_opts=opts.LabelOpts(is_show=False))
    .set_global_opts(
        title_opts=opts.TitleOpts(title="客戶類型",pos_left="80%",pos_top="5%"),
        legend_opts=opts.LegendOpts(type_="scroll", pos_left="80%", orient="vertical",pos_top="15%"),
    )
    
)
pie.render_notebook()
客戶類型占比情況

2.客戶消費(fèi)情況

bar = (
    charts.Bar()
    .add_xaxis(c_sales['分級(jí)'].tolist())
    .add_yaxis("",c_sales['M'].round(2).tolist())
    .set_global_opts(
        title_opts = opts.TitleOpts(title = '客戶消費(fèi)金額分布',pos_left = '50%',pos_top='5%'),
        xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-20))
    )
    .set_series_opts(label_opts = opts.LabelOpts(formatter = '{c}'))
)
bar.render_notebook()  
客戶消費(fèi)金額
pie2_type = c_sales['分級(jí)'].tolist()
pie2_value = c_sales['M'].round(2).tolist()
pie2_datapair = [list(z) for z in zip(pie2_type,pie2_value)]
pie2 = (
    charts.Pie()
    .add("",pie2_datapair,radius=["30%","55%"],center=["50%","40%"],rosetype="radius",
        label_opts = opts.LabelOpts(formatter='占比u0z1t8os%')
        )
    .set_global_opts(
        title_opts = opts.TitleOpts(title = "客戶消費(fèi)金額分布",pos_left='80%',pos_top='5%'),
        legend_opts=opts.LegendOpts(type_="scroll", pos_left="80%", orient="vertical",pos_top="15%")
    )

)
pie2.render_notebook()
客戶消費(fèi)占比

3.每個(gè)指標(biāo)的分布情況

#刪除R,F,M每列的異常值
q1 = df_rfm['R'].quantile(0.05)
q3 = df_rfm['R'].quantile(0.95)
iqr = q3-q1
df_cluster = df_cluster[(df_cluster["R"]>=q1-1.5*iqr) & (df_cluster["R"]<=q3+1.5*iqr)]
df_cluster.head()
#看指標(biāo)的箱型圖
boxplot4_r = [df_cluster['R'].tolist()]
boxplot4 = (
    charts.Boxplot()
    .add_xaxis(["R"])
    .add_yaxis("R",boxplot4.prepare_data(boxplot4_r))
)
boxplot4.render_notebook()
箱型圖 R

箱型圖 F

箱型圖 M

RFM分析結(jié)論

1.客戶類型分析
  • 經(jīng)過第一步的數(shù)據(jù)清理,最終有4000+條記錄,以R,F,M三個(gè)指標(biāo)的不同取值對(duì)客戶分群,其中重點(diǎn)深耕客戶(最近有消費(fèi)記錄且消費(fèi)金額大,但消費(fèi)次數(shù)不多)占比近四成,流失客戶(最近一年無消費(fèi),歷史消費(fèi)金額低,消費(fèi)次數(shù)少)占比兩成,重點(diǎn)關(guān)注這兩類人數(shù)較多的客戶。

  • 在客戶總數(shù)中占比為7.61%的高價(jià)值客戶和占比39.83%的重點(diǎn)深耕客戶貢獻(xiàn)了85.8%的銷售額,距離各行業(yè)都適用的二八法則差距較大,接下來要提高高價(jià)值客戶和重點(diǎn)深耕客戶的消費(fèi)金額,或是把重點(diǎn)深耕客戶轉(zhuǎn)化為高價(jià)值客戶,即提升該客戶群(總計(jì)1728個(gè))的消費(fèi)頻次。

  • 重要挽留客戶(消費(fèi)金額高,但消費(fèi)頻次不高,最近無消費(fèi)記錄),人數(shù)511,貢獻(xiàn)銷售額占比8.75%,首先考慮是否在計(jì)算周期內(nèi)有大型促銷,降價(jià)活動(dòng),如果有,那么要轉(zhuǎn)化這部分客戶的難度會(huì)大一些,如果沒有,則需要分析這類顧客的購買產(chǎn)品特性(是否是日常消費(fèi)品,利潤(rùn)空間,季節(jié)性),首先考慮提高消費(fèi)頻次,培養(yǎng)客戶在本平臺(tái)的消費(fèi)習(xí)慣。

  • 新客戶(最近有消費(fèi),但消費(fèi)金額不大,消費(fèi)次數(shù)少),人數(shù)835,貢獻(xiàn)銷售額2.6%,因?yàn)樾枰劝研驴蛻艮D(zhuǎn)化為留存用戶,在實(shí)際運(yùn)營(yíng)過程中,可以考慮把這部分客戶和重要挽留客戶放在一起轉(zhuǎn)化,提升消費(fèi)次數(shù)。

  • 流失客戶(最近一年無消費(fèi),歷史消費(fèi)金額低,消費(fèi)次數(shù)少),人數(shù)927,貢獻(xiàn)銷售金額占比低,需要繼續(xù)分析這類客戶群體的消費(fèi)商品和消費(fèi)時(shí)間,但人數(shù)較多,分析過程比較耗時(shí)可作為提升銷量的次要考慮因素 。

  • 重要喚回客戶(最近無消費(fèi),但過往消費(fèi)頻次和消費(fèi)金額都很高),人數(shù)7,這類客戶人數(shù)較少,但消費(fèi)意愿和消費(fèi)價(jià)值較高,可作為短期提升銷量的重點(diǎn)關(guān)注對(duì)象,通過運(yùn)營(yíng)活動(dòng)push,優(yōu)惠活動(dòng)刺激消費(fèi)。

2.R,F,M三個(gè)指標(biāo)數(shù)值分布分析
  • F指標(biāo):有一半的數(shù)據(jù)集中在0-5區(qū)間,有25%的數(shù)據(jù)分布在4-30區(qū)間;M指標(biāo):一半的數(shù)據(jù)集中在0-2500區(qū)間,25%的數(shù)據(jù)在2500-14000區(qū)間,兩個(gè)指標(biāo)的中位數(shù)都偏低,極差較大;R指標(biāo)分布相對(duì)較均勻,一半的數(shù)分布在0-150內(nèi);總體的消費(fèi)數(shù)據(jù)可以看出:客戶消費(fèi)頻次整體不高,金額不大,但最近消費(fèi)情況還不錯(cuò),即消費(fèi)意愿高,重點(diǎn)是拉新和提升消費(fèi)次數(shù),在提升客戶消費(fèi)金額方面應(yīng)首先考慮消費(fèi)金額大,但最近無消費(fèi)的客戶。

K-Means聚類

最優(yōu)k值的確定方法-手肘法和輪廓系數(shù)法
核心指標(biāo):SSE(sum of the squared errors,誤差平方和)



Ci是第i個(gè)簇,p是Ci中的樣本點(diǎn),mi是Ci的質(zhì)心(Ci中所有樣本的均值),SSE是所有樣本的聚類誤差,代表了聚類效果的好壞。

  • 隨著聚類數(shù)k的增大,樣本劃分會(huì)更加精細(xì),每個(gè)簇的聚合程度會(huì)逐漸提高,那么誤差平方和SSE自然會(huì)逐漸變小。
  • 當(dāng)k小于真實(shí)聚類數(shù)時(shí),由于k的增大會(huì)大幅增加每個(gè)簇的聚合程度,故SSE的下降幅度會(huì)很大,而當(dāng)k到達(dá)真實(shí)聚類數(shù)時(shí),再增加k所得到的聚合程度回報(bào)會(huì)迅速變小,所以SSE的下降幅度會(huì)驟減,然后隨著k值的繼續(xù)增大而趨于平緩,也就是說SSE和k的關(guān)系圖是一個(gè)手肘的形狀,而這個(gè)肘部對(duì)應(yīng)的k值就是數(shù)據(jù)的真實(shí)聚類數(shù)
#數(shù)據(jù)標(biāo)準(zhǔn)化,這里用的是均值-標(biāo)準(zhǔn)差歸一化,盡量將數(shù)據(jù)轉(zhuǎn)化為均值為零,方差為一的數(shù)據(jù),形如標(biāo)準(zhǔn)正態(tài)分布(高斯分布)
df_cluster_scaled = df_cluster[['R','F','M']]
scaler = StandardScaler()
df_cluster_scaled = scaler.fit_transform(df_cluster_scaled)
df_cluster_scaled = pd.DataFrame(df_cluster_scaled)
df_cluster_scaled.columns = ['R','F','M']
df_cluster_scaled.head()
#測(cè)試應(yīng)該分為幾簇,肘部對(duì)于的k值為3(曲率最高),故對(duì)于這個(gè)數(shù)據(jù)集的聚類而言,最佳聚類數(shù)應(yīng)該選3
ssd = []
range_n_clusters = [2,3,4,5,6,7,8]
for num_clusters in range_n_clusters:
    kmeans = KMeans(n_clusters = num_clusters,max_iter = 50)
    kmeans.fit(df_cluster_scaled)
    ssd.append(kmeans.inertia_)
plt.plot(ssd)
分簇

也使用輪廓系數(shù)(silhouette coefficient)來確定K最優(yōu)值,選擇使系數(shù)較大所對(duì)應(yīng)的k值

  • 輪廓系數(shù)范圍在[-1,1]之間。該值越大,越合理。
    si接近1,則說明樣本i聚類合理;
    si接近-1,則說明樣本i更應(yīng)該分類到另外的簇;
    若si 近似為0,則說明樣本i在兩個(gè)簇的邊界上。
  • 所有樣本的s i 的均值稱為聚類結(jié)果的輪廓系數(shù),是該聚類是否合理、有效的度量。
  • 使用輪廓系數(shù)(silhouette coefficient)來確定,選擇使系數(shù)較大所對(duì)應(yīng)的k值
for num_clusters in range_n_clusters:
    kmeans = KMeans(n_clusters = num_clusters,max_iter = 50)
    kmeans.fit(df_cluster_scaled)
    cluster_labels = kmeans.labels_
    silhouette_avg = silhouette_score(df_cluster_scaled,cluster_labels)
    print('For n_clusters = {0},the silhouette score is {1}'.format(num_clusters,silhouette_avg))
不同k值對(duì)應(yīng)的輪廓系數(shù)

綜上確定聚類K值為3

Kmeans = KMeans(n_clusters=3,max_iter=50)
Kmeans.fit(df_cluster_scaled)
df_cluster['Cluster_ID'] = Kmeans.labels_
cluster = df_cluster[['CustomerID','R','F','M','Rank','Cluster_ID']]
cluster.head()
聚類結(jié)果(tableau)

用K-Means進(jìn)行無監(jiān)督聚類后,可以看出整體分為三類,與RFM模型分類結(jié)果較為相似,可以重點(diǎn)關(guān)注偏離集群的幾個(gè)點(diǎn),以及藍(lán)色類別中出現(xiàn)的幾個(gè)紅色類別數(shù)據(jù),這部分與RFM模型的差異可能是由于RFM模型判斷時(shí)間的主觀性造成的,在實(shí)際建模的過程中需要再考慮一下RFM的分級(jí)條件。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容