本文主要是主要是根據(jù)用戶(hù)消費(fèi)記錄,分析用戶(hù)消費(fèi)行為,建立RFM模型,分析復(fù)購(gòu)率、回購(gòu)率等關(guān)鍵指標(biāo)。希望對(duì)其他產(chǎn)品的線上消費(fèi)數(shù)據(jù)分析有一定的借鑒價(jià)值。
一、項(xiàng)目背景
CDNOW是美國(guó)的一家網(wǎng)上唱片公司,成立于1994年,后來(lái)被貝塔斯曼音樂(lè)集團(tuán)收購(gòu)。
二、分析目標(biāo)
本次分析報(bào)告的數(shù)據(jù)來(lái)源于這家CD網(wǎng)站上的用戶(hù)消費(fèi)記錄,旨在分析用戶(hù)消費(fèi)行為,建立RFM模型,分析復(fù)購(gòu)率、回購(gòu)率等關(guān)鍵指標(biāo)。
三、主要分析框架

四、分析過(guò)程
準(zhǔn)備工作:
# 導(dǎo)入數(shù)據(jù)包
import pandas as pd
import numpy as np
# 導(dǎo)入數(shù)據(jù)
columns = ['user_id','order_dt','order_products','order_amount']
df = pd.read_table('bicycle_master.txt',names = columns,sep = '\s+',engine='python')
df.head()
# user_id:用戶(hù)ID
# order_dt:購(gòu)買(mǎi)日期
# order_products:購(gòu)買(mǎi)產(chǎn)品數(shù)
# order_amount:購(gòu)買(mǎi)金額

# 查看數(shù)據(jù)類(lèi)型及數(shù)據(jù)完整情況
df.info()

通過(guò)查看數(shù)據(jù)類(lèi)型發(fā)現(xiàn),order_dt的數(shù)據(jù)類(lèi)型是int64型,而非日期型,需要將其轉(zhuǎn)化為日期型
# 轉(zhuǎn)化日期格式
df['order_dt'] = pd.to_datetime(df.order_dt,format = "%Y%m%d")
# 新增一列記錄月,方便后續(xù)按月統(tǒng)計(jì)
df['month'] = df['order_dt'].values.astype('datetime64[M]')
df.head(1)

# 數(shù)據(jù)描述性統(tǒng)計(jì)
df.describe()

從描述性統(tǒng)計(jì)可知:
- 大部分訂單只消費(fèi)了少量商品(平均2.4),有一定值干擾
- 用戶(hù)的消費(fèi)金額比較穩(wěn)定,平均消費(fèi)35元,中位數(shù)25元,有一定極值的干擾,導(dǎo)致平均消費(fèi)被拉高,絕大多數(shù)訂單的消費(fèi)金額都小于35元
1. 用戶(hù)消費(fèi)趨勢(shì)分析
根據(jù)月份分組,分別對(duì)消費(fèi)額,購(gòu)買(mǎi)人數(shù),購(gòu)買(mǎi)量,消費(fèi)人數(shù)聚合
# 根據(jù)月份分組
grouped_month = df.groupby('month')
# 聚合獲得月消費(fèi)次數(shù)
order_month_amount = grouped_month.order_amount.sum()
# 聚合獲得月消費(fèi)金額,月消費(fèi)次數(shù),月產(chǎn)品購(gòu)買(mǎi)量
grouped_month_info = grouped_month[['order_amount','user_id','order_products']].agg({'order_amount':'sum','user_id':'count','order_products':'sum'})
# 重命名
grouped_month_info.rename(columns = {'order_amount':'月消費(fèi)金額','user_id':'月消費(fèi)次數(shù)','order_products':'月產(chǎn)品購(gòu)買(mǎi)量'},inplace = True)
# 求消費(fèi)次數(shù)
grouped_month_info['消費(fèi)次數(shù)'] = grouped_month['user_id'].unique().map(len)
# 重置索引
grouped_month_info = grouped_month_info.reset_index()
grouped_month_info.head()

導(dǎo)出數(shù)據(jù),PowerBI作圖
# 如果是日期類(lèi)型,導(dǎo)入Power BI會(huì)是時(shí)間戳格式,所以這里轉(zhuǎn)成字符串
grouped_month_info['month'] = grouped_month_info['month'].astype('str')
grouped_month_info.to_excel(r'group_month_infor.xlsx')

由上圖可知
- 消費(fèi)金額在前三個(gè)月達(dá)到最高峰,后續(xù)消費(fèi)較為穩(wěn)定,有輕微下降趨勢(shì)
- 產(chǎn)品購(gòu)買(mǎi)量在前三個(gè)月達(dá)到最高峰,后續(xù)消費(fèi)較為穩(wěn)定,有輕微下降趨勢(shì)
- 前三個(gè)月消費(fèi)訂單人數(shù)在10000筆左右,后續(xù)月份的平均消費(fèi)人數(shù)則在2500人
2. 用戶(hù)個(gè)體消費(fèi)分析
2.1 用戶(hù)消費(fèi)金額,消費(fèi)次數(shù)的描述統(tǒng)計(jì)
對(duì)用戶(hù)分組聚合
grouped_user = df.groupby('user_id')
grouped_user.sum().describe()

根據(jù)用戶(hù)購(gòu)買(mǎi)數(shù)量看,max是1033,但是中位數(shù)只有3,而均值為7,所以可知有少部分的用戶(hù)購(gòu)買(mǎi)了大量的產(chǎn)品
2.2 用戶(hù)消費(fèi)金額和消費(fèi)次數(shù)的散點(diǎn)圖
對(duì)用戶(hù)分組,對(duì)訂單數(shù)及訂單金額求和求得用戶(hù)消費(fèi)次數(shù)和消費(fèi)金額
grouped_user_info = grouped_user.sum()
grouped_user_info.to_excel(r'.\臨時(shí)表\用戶(hù)個(gè)體消費(fèi)行為分析.xlsx')

消費(fèi)金額與產(chǎn)品消費(fèi)數(shù)量成正相關(guān)
2.3 用戶(hù)消費(fèi)金額的分布圖
# 獲取用戶(hù)消費(fèi)金額
grouped_user_sum_order_amount = grouped_user.sum()['order_amount']
# 消費(fèi)金額按照50分箱
grouped_user_sum_order_amount_lst = [i for i in range(0,int(grouped_user_sum_order_amount.max())+50,50)]
# 按照分箱規(guī)則對(duì)用戶(hù)消費(fèi)金額進(jìn)行劃分
grouped_user_sum_order_amount = pd.cut(grouped_user_sum_order_amount,bins = grouped_user_sum_order_amount_lst,labels = grouped_user_sum_order_amount_lst[1:])
# 導(dǎo)出數(shù)據(jù)
grouped_user_sum_order_amount.to_excel(r'grouped_user_sum_order_amount.xlsx')

從直方圖可知,用戶(hù)消費(fèi)金額,絕大部分呈現(xiàn)集中趨勢(shì),小部分異常值干擾了判斷,可以使用過(guò)濾操作排除異常
2.4 用戶(hù)消費(fèi)次數(shù)的分布圖
與消費(fèi)金額分布情況相同,對(duì)消費(fèi)次數(shù)進(jìn)行分區(qū)
#4. 用戶(hù)消費(fèi)次數(shù)的分布圖
grouped_user_sum_order_products = grouped_user.sum()['order_products']
grouped_user_sum_order_products_lst = [i for i in range(0,int(grouped_user_sum_order_products.max())+50,50)]
grouped_user_sum_order_products = pd.cut(grouped_user_sum_order_products,bins = grouped_user_sum_order_products_lst,labels = grouped_user_sum_order_products_lst[1:])
grouped_user_sum_order_products.to_excel(r'grouped_user_sum_order_products.xlsx')

2.5 用戶(hù)累計(jì)消費(fèi)金額占比情況
對(duì)用戶(hù)分組,對(duì)消費(fèi)金額排序后累加即可
# 排序后使用cumsum對(duì)消費(fèi)金額累加
user_cumsum = grouped_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum()/x.sum())
user_cumsum.reset_index().order_amount.to_excel(r'user_cumsum_order_amount.xlsx')

3. 用戶(hù)消費(fèi)行為
3.1 用戶(hù)第一次消費(fèi)(首購(gòu))
# 按用戶(hù)分組,取每個(gè)用戶(hù)的最小日期,然后對(duì)最小日期計(jì)數(shù)并按照次數(shù)由大到小排序value_counts
grouped_user_dt_min = grouped_user.min()['order_dt'].value_counts().reset_index().rename(columns = {'index':'first_date'})
grouped_user_dt_min['first_date'] = grouped_user_dt_min['first_date'].astype('str')
grouped_user_dt_min.to_excel(r'grouped_user_dt_min.xlsx')

3.2 用戶(hù)最后一次消費(fèi)
與用戶(hù)首購(gòu)分布同理,獲取用戶(hù)最后一次購(gòu)買(mǎi)日期的分布
grouped_user_dt_max = grouped_user.max()['order_dt'].value_counts().reset_index().rename(columns = {'index':'last_date'})
grouped_user_dt_max['last_date'] = grouped_user_dt_max['last_date'].astype('str')
grouped_user_dt_max.to_excel(r'grouped_user_dt_max.xlsx')

大部分最后一次購(gòu)買(mǎi),集中在前三個(gè)月,大量用戶(hù)購(gòu)買(mǎi)了一次后就不再進(jìn)行購(gòu)買(mǎi)。
3.3 新老客戶(hù)消費(fèi)占比
3.3.1 多少用戶(hù)只消費(fèi)一次
通過(guò)判斷起始購(gòu)買(mǎi)日期與最后購(gòu)買(mǎi)日期,如果相等則任務(wù)是新客戶(hù),如果不等則為老客戶(hù)
user_life=grouped_user.order_dt.agg(['min','max'])
# 統(tǒng)計(jì)只消費(fèi)了一次的用戶(hù)
(user_life['min']==user_life['max']).value_counts()

可知,新客戶(hù)占比51.1%
3.3.2 每月新客占比
分別對(duì)月與客戶(hù)分組,獲取每月每個(gè)客戶(hù)第一次與最后一次購(gòu)買(mǎi)日期,然后判斷是否為新客,通過(guò)月份分組獲得新老客戶(hù)數(shù)量
# 按月與用戶(hù)分組,獲得首購(gòu)及最后一次購(gòu)買(mǎi)日期
group_um = df.groupby(['month','user_id'])['order_dt'].agg(['min','max'])
# 新老客判斷
group_um['new'] = (group_um['min'] == group_um['max'])
# 按月分組對(duì)新老客計(jì)數(shù)
group_um.reset_index().groupby('month')['new'].value_counts()

3.4 用戶(hù)分層RFM模型
RFM模型介紹:
RFM模型是衡量客戶(hù)價(jià)值和客戶(hù)創(chuàng)利能力的重要工具和手段。
R:最近一次消費(fèi) (Recency)
F:消費(fèi)頻率 (Frequency)
M:消費(fèi)金額 (Monetary)
將以上三個(gè)指標(biāo)分別拆分為二,這樣就產(chǎn)生了8個(gè)維度用戶(hù)客戶(hù)的分組。R為1 表示比均值大,離最早時(shí)間近,F(xiàn)為1 表示消費(fèi)金額比較多,M為1 表示消費(fèi)頻次比較多
'111':'重要價(jià)值客戶(hù)',
'011':'重要保持客戶(hù)',
'101':'重要發(fā)展客戶(hù)',
'001':'重要挽留客戶(hù)',
'110':'一般價(jià)值客戶(hù)',
'010':'一般保持客戶(hù)',
'100':'一般發(fā)展客戶(hù)',
'000':'一般挽留客戶(hù)',
# 將消費(fèi)記錄表轉(zhuǎn)透視表,user_id為行索引,分別對(duì)訂單日期取最大值,訂單金額匯總,訂單產(chǎn)品數(shù)求和。
rfm=df.pivot_table(index='user_id',
values=['order_products','order_amount','order_dt'],
aggfunc={'order_dt':'max',
'order_amount':'sum',
'order_products':'sum'})
rfm.head()

# 因?yàn)閿?shù)據(jù)日期距今天比較久遠(yuǎn),所以這里令表中日期的最大值為當(dāng)前日期
# 求R值
RFM['R'] = (RFM.order_dt.max() - RFM.order_dt)/np.timedelta64(1,'D')
# 重命名
RFM = RFM.rename(columns = {'order_amount':'M','order_products':'F'})
RFM.head()

將個(gè)用戶(hù)分組,按照RFM的均值作為標(biāo)準(zhǔn),比均值大就為1,比均值小就為0
# 設(shè)定函數(shù),如果
def rfm_fumc(x):
level = x.apply(lambda x:'1' if x >= 0 else '0')
label = level.R + level.F + level.M
d = {
'111':'重要價(jià)值客戶(hù)',
'011':'重要保持客戶(hù)',
'101':'重要發(fā)展客戶(hù)',
'001':'重要挽留客戶(hù)',
'110':'一般價(jià)值客戶(hù)',
'010':'一般保持客戶(hù)',
'100':'一般發(fā)展客戶(hù)',
'000':'一般挽留客戶(hù)'}
result = d[label]
return result
# apply 默認(rèn)axis = 0,按列映射
RFM['label'] = RFM[['R','F','M']].apply(lambda x : x - x.mean()).apply(rfm_fumc,axis = 1)
RFM.to_excel(r'RFM.xlsx')
RFM.head()


3.5 用戶(hù)購(gòu)買(mǎi)周期(按訂單)
3.5.1 用戶(hù)消費(fèi)周期描述
購(gòu)買(mǎi)周期,即用戶(hù)每次購(gòu)買(mǎi)的間隔
# 利用shift將用戶(hù)的購(gòu)買(mǎi)日期向下偏移1行,然后相減就可以得到每次購(gòu)買(mǎi)的間隔日期
order_diff = grouped_user.apply(lambda x : x.order_dt - x.order_dt.shift())
# 然后描述性統(tǒng)計(jì)
order_diff.describe()

3.5.2 用戶(hù)消費(fèi)周期分布
對(duì)用戶(hù)的消費(fèi)周期進(jìn)行區(qū)間劃分,然后對(duì)劃分后的組進(jìn)行分類(lèi)計(jì)數(shù)即為消費(fèi)周期的分布
# 去掉相減后的單位days
order_diff_info = (order_diff/np.timedelta64(1,'D'))
# 指定分箱
order_diff_cut_lst = [i for i in range(0,int(order_diff_info.max())+1,10)]
# 按照分箱去分組劃分
order_diff_info_hist = pd.cut(order_diff_info,bins = order_diff_cut_lst,labels = order_diff_cut_lst[1:])
# 用10填充N(xiāo)aN。NaN為第一次購(gòu)買(mǎi),所以沒(méi)有周期
order_diff_info_hist = order_diff_info_hist.fillna(10)
order_diff_info_hist.to_excel(r'order_dt_diff_info.xlsx')

訂單周期呈指數(shù)分布
絕大部分用戶(hù)的購(gòu)買(mǎi)周期都低于100天
3.6 用戶(hù)生命周期分布
用戶(hù)生命周期即為最后一次購(gòu)買(mǎi)的日期減去第一次購(gòu)買(mǎi)的日期。
按照用戶(hù)分組,獲取第一次與最后一次夠嗎的日期,并求間隔
# 分組獲取第一次與最后一次購(gòu)買(mǎi)日期
user_life = grouped_user['order_dt'].agg(['min',max])
# 獲取購(gòu)買(mǎi)周期
user_life['user_life'] = user_life['max'] - user_life['min']
user_life['user_life'] = user_life['user_life']/np.timedelta64(1,'D')
對(duì)購(gòu)買(mǎi)周期進(jìn)行分組劃分,統(tǒng)計(jì)各分組的頻數(shù)可形成用戶(hù)周期的分布圖
user_life_info = user_life['user_life']
# 分箱,然后按照分箱分區(qū)劃分,對(duì)于只購(gòu)買(mǎi)一次的用戶(hù),不進(jìn)行處理,即為NaN,后續(xù)在處理的時(shí)候不會(huì)統(tǒng)計(jì)NaN
user_life_lst = [i for i in range(0,int(user_life_info.max()+10),10)]
user_life_info_hist = pd.cut(user_life_info,bins = user_life_lst,labels = user_life_lst[1:])
user_life_info_hist.to_excel(r'user_life_info_noNaN.xlsx')

4. 復(fù)購(gòu)率和回購(gòu)率分析
復(fù)購(gòu)率:自然月內(nèi),購(gòu)買(mǎi)多次的用戶(hù)占比
本分析,定義復(fù)購(gòu)為當(dāng)月多次購(gòu)買(mǎi)為復(fù)購(gòu)
回購(gòu)率:曾經(jīng)購(gòu)買(mǎi)過(guò)的用戶(hù)在某一時(shí)期的再次購(gòu)買(mǎi)的占比
本分析,定義上個(gè)月購(gòu)買(mǎi),這個(gè)月繼續(xù)購(gòu)買(mǎi)為回購(gòu)。即周期為1個(gè)月
4.1 復(fù)購(gòu)率
對(duì)df進(jìn)行用戶(hù)和月份的透視,值為用戶(hù)單月購(gòu)買(mǎi)的次數(shù)
# 以用戶(hù)為索引,月份為列,對(duì)訂單進(jìn)行計(jì)數(shù),對(duì)于空值填充0,得到的值就為用戶(hù)單月購(gòu)買(mǎi)的次數(shù)
pivoted_counts=df.pivot_table(index='user_id',
columns='month',
values='order_dt',
aggfunc='count').fillna(0)
pivoted_counts.head()

為了方便統(tǒng)計(jì),我們把用戶(hù)單月購(gòu)買(mǎi)次數(shù)大于等于2的設(shè)為1,表示復(fù)購(gòu)
只有一次購(gòu)買(mǎi)設(shè)置為0,表示有購(gòu)買(mǎi),沒(méi)有復(fù)購(gòu)
沒(méi)有購(gòu)買(mǎi)的設(shè)置為NaN,表示未購(gòu)買(mǎi),方便后續(xù)算復(fù)購(gòu)率
purchase_r=pivoted_counts.applymap(lambda x: 1 if x>1 else np.NaN if x==0 else 0)
purchase_r.head()

計(jì)算復(fù)購(gòu)率
# 每列的和即為當(dāng)月的復(fù)購(gòu)次數(shù),每列的計(jì)數(shù)即為當(dāng)月購(gòu)買(mǎi)客戶(hù)的數(shù)量
purchase_r_reshop = (purchase_r.sum()/purchase_r.count()).reset_index(name = 'reshop')
purchase_r_reshop['month'] = purchase_r_reshop['month'].astype(str)
purchase_r_reshop.to_excel(r'purchase_reshop.xlsx')

4.2 回購(gòu)率
#在用戶(hù)單月購(gòu)買(mǎi)次數(shù)的表的基礎(chǔ)上,將當(dāng)月購(gòu)買(mǎi)過(guò)標(biāo)記為1,未購(gòu)買(mǎi)過(guò)的標(biāo)記為0
df_purchase = pivoted_counts.applymap(lambda x : 1 if x > 0 else 0)
df_purchase.head()

接下來(lái)判斷是否回購(gòu),通過(guò)apply設(shè)置axis = 1,對(duì)每個(gè)用戶(hù)逐個(gè)映射。
先判斷該用戶(hù)本月是否購(gòu)買(mǎi),如果沒(méi)購(gòu)買(mǎi):填充N(xiāo)aN,進(jìn)行下一個(gè)月份的判斷;如果購(gòu)買(mǎi):則判斷下個(gè)月是否夠購(gòu)買(mǎi),如果購(gòu)買(mǎi)則即為復(fù)購(gòu)1,否則為未復(fù)購(gòu)0
def purchase_back(data):
'''判斷每一個(gè)月是否是回購(gòu),根據(jù)上個(gè)月是否購(gòu)買(mǎi)來(lái)判斷,上個(gè)月消費(fèi)下個(gè)月沒(méi)有購(gòu)買(mǎi)就不是回購(gòu)'''
status=[]
for i in range(17):
if data[i]==1:
if data[i+1]==1:
status.append(1)
if data[i+1]==0:
status.append(0)
else:
status.append(np.NaN)
# 第18個(gè)月補(bǔ)充N(xiāo)aN
status.append(np.NaN)
return status
indexs=df['month'].sort_values().astype('str').unique()
purchase_b = df_purchase.apply(lambda x :pd.Series(purchase_back(x),index = indexs),axis =1)
purchase_b.head()

計(jì)算各月的復(fù)購(gòu)率
# 對(duì)各列求和即為當(dāng)月復(fù)購(gòu)的次數(shù),對(duì)各列計(jì)數(shù)即為當(dāng)月購(gòu)買(mǎi)的次數(shù)
purchase_backshop = purchase_b.sum()/purchase_b.count()
purchase_backshop.index = purchase_backshop.index.astype('str')
purchase_backshop.to_excel(r'purchase_backshop.xlsx')
