一、簡(jiǎn)介
對(duì)國(guó)外某一CD網(wǎng)站的用戶購(gòu)買數(shù)據(jù)進(jìn)行分析,主要圍繞整體消費(fèi)情況和用戶消費(fèi)行為進(jìn)行分析,找出高價(jià)值用戶人群亭引,了解用戶留存以及流失等情況,為平臺(tái)指定策略提供數(shù)據(jù)方面的支持和建議皮获。
二焙蚓、流程
1、數(shù)據(jù)清洗
2洒宝、用戶消費(fèi)趨勢(shì)分析:觀察用戶月消費(fèi)額购公、月消費(fèi)趨勢(shì)以及總體消費(fèi)人數(shù)的變動(dòng)情況
3、用戶個(gè)體消費(fèi)分析:關(guān)注用戶消費(fèi)金額以及消費(fèi)次數(shù)的情況
4雁歌、用戶消費(fèi)行為分析:新老客比例宏浩,首購(gòu)及末購(gòu)情況,RFM用戶分層等
5靠瞎、復(fù)購(gòu)率比庄、回購(gòu)率分析
三、具體分析過(guò)程
1乏盐、數(shù)據(jù)清洗
導(dǎo)入數(shù)據(jù)包佳窑,同時(shí)設(shè)置好列標(biāo)簽
import pandas as pd
import numpy as np
columns = ['user_id', 'order_dt', 'order_products', 'order_amount']
df = pd.read_table('CDNOW_master.txt', names = columns, sep='\s+')
df
可以發(fā)現(xiàn)該數(shù)據(jù)集總共有69659行*4列數(shù)據(jù),4張列標(biāo)簽分別是
user_id:用戶id
order_dt:下單日期
order_products:購(gòu)買商品數(shù)量
order_amount:購(gòu)買金額
df.info()
order_dt字段為數(shù)值型父能,需要將其更改為日期型,同時(shí)加入month列神凑,作為備用
# 轉(zhuǎn)換為日期格式
df['order_dt'] = pd.to_datetime(df.order_dt, format="%Y%m%d")
df['month'] = df.order_dt.values.astype('datetime64[M]')
df.info()
pd.to_datetime可以將特定字符串或數(shù)字轉(zhuǎn)換為時(shí)間格式,其中format參數(shù)用于匹配
例如19970701何吝,%Y匹配前面4位數(shù)字溉委,如果y小寫就只匹配2位數(shù)字97
同理鹃唯,%m匹配月份07,%d匹配日期01薛躬。另外俯渤,小時(shí)是%h呆细,分鐘是%M
df.describe()
再來(lái)簡(jiǎn)單觀察數(shù)據(jù)集統(tǒng)計(jì)情況型宝,可以發(fā)現(xiàn)大部分訂單只消費(fèi)了少量商品(平均2.4個(gè)),同時(shí)用戶消費(fèi)金額比較穩(wěn)定絮爷,平均消費(fèi)35.89元趴酣,但是中位數(shù)在25.98元,最大值有1286元坑夯,說(shuō)明存在極值干擾的情況
2岖寞、用戶消費(fèi)趨勢(shì)的分析
1)月消費(fèi)金額
先對(duì)消費(fèi)金額按月進(jìn)行統(tǒng)計(jì)
grouped_month = df.groupby('month')
order_month_amount = grouped_month.order_amount.sum()
order_month_amount.head()
利用matplotlib進(jìn)行圖表展示
# 加載數(shù)據(jù)可視化包
import matplotlib.pyplot as plt
# 可視化結(jié)果顯示在頁(yè)面
%matplotlib inline
# 更改設(shè)計(jì)風(fēng)格
plt.style.use('ggplot')
order_month_amount.plot()
可以發(fā)現(xiàn),顧客消費(fèi)金額在前三月達(dá)到了頂峰柜蜈,之后出現(xiàn)了明顯的回落仗谆,但是能保持在較穩(wěn)定的水平
2)月訂單數(shù)量
grouped_month.user_id.count().plot()
在訂單數(shù)量方面,其走勢(shì)也和消費(fèi)金額大體一致淑履,前三月在11000左右隶垮,后期回落到了2500左右的水平
3)月消費(fèi)人數(shù)
df.groupby('month').user_id.apply(lambda x: len(x.drop_duplicates())).plot()
用drop duplicates對(duì)user_id字段進(jìn)行去重,從而觀察每月消費(fèi)人數(shù)情況秘噪,可以發(fā)現(xiàn)前三月消費(fèi)人數(shù)在8000到10000之間狸吞,后續(xù)月份消費(fèi)人數(shù)大幅回落,平均消費(fèi)人數(shù)不足2000
3指煎、用戶個(gè)體消費(fèi)分析
1)用戶消費(fèi)描述統(tǒng)計(jì)
grouped_user = df.groupby('user_id')
grouped_user.sum().describe()
用戶平均購(gòu)買了7張CD蹋偏,但是中位數(shù)只有3,說(shuō)明有小部分用戶購(gòu)買了大量CD
用戶平均消費(fèi)金額106元至壤,中位數(shù)為43威始,同樣存在極值的干擾
grouped_user.sum().query('order_amount < 4000').plot.scatter(x = 'order_amount', y = 'order_products')
利用散點(diǎn)圖對(duì)極值敏感的特點(diǎn),可以直觀發(fā)現(xiàn)極值的存在
2)用戶消費(fèi)金額分布情況
# hist為直方圖像街,bins為分組黎棠,此處分為20組
grouped_user.sum().order_amount.plot.hist(bins = 20)
從直方圖可知,用戶消費(fèi)金額絕大部分呈現(xiàn)集中趨勢(shì)宅广,小部分異常值干擾了判斷葫掉,可以使用過(guò)濾操作來(lái)排除異常
# 加入過(guò)濾條件
grouped_user.sum().query('order_products < 92').order_amount.hist(bins = 20)
使用切比雪夫定理來(lái)過(guò)濾異常值,計(jì)算95%數(shù)字的情況(order_products平均值為7跟狱,標(biāo)準(zhǔn)差為17俭厚,7+5*17=92)
3)用戶累計(jì)消費(fèi)占比情況
# cumsum求累加值
user_cumsum = grouped_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum() / x.sum())
user_cumsum.reset_index().order_amount.plot()
按用戶消費(fèi)金額進(jìn)行升序排列,由圖可知50%的用戶僅貢獻(xiàn)了15%的消費(fèi)額度驶臊,而排名前5000的用戶就貢獻(xiàn)了60%的消費(fèi)額
4挪挤、用戶消費(fèi)行為分析
1)用戶首購(gòu)及末購(gòu)情況
# 得到最小日期善茎,再統(tǒng)計(jì)各個(gè)日期的個(gè)數(shù)
grouped_user.min().order_dt.value_counts().plot()
可以發(fā)現(xiàn),用戶第一次購(gòu)買集中分布在前三個(gè)月肥照,其中在2月11日至2月25日有一次劇烈波動(dòng)
grouped_user.max().order_dt.value_counts().plot()
而在最后一次購(gòu)買方面嚷闭,日期分布明顯要比首購(gòu)寬廣,但是大量最后一次購(gòu)買行為集中在前三個(gè)月论寨,說(shuō)明有大量用戶只購(gòu)買了一次之后便不再購(gòu)買星立。
隨著時(shí)間遞增,最后一次購(gòu)買數(shù)量也在遞增葬凳,消費(fèi)呈現(xiàn)流失上升的狀況
2)用戶生命周期
user_life = grouped_user.order_dt.agg(['min', 'max'])
user_life.head()
(user_life['min'] == user_life['max']).value_counts()
可以發(fā)現(xiàn)有一半用戶只消費(fèi)了一次
(user_life['max'] - user_life['min']).describe()
((user_life['max'] - user_life['min']) /np.timedelta64(1, 'D')).hist(bins = 20)
可以看到用戶生命周期受極值的影響非常厲害绰垂,中位數(shù)僅有0天,但是平均首購(gòu)與末購(gòu)相隔天數(shù)為134天火焰,而且圖表中數(shù)據(jù)也集中于0天上
u_1 = ((user_life['max'] - user_life['min']) /np.timedelta64(1, 'D'))
u_1[u_1 > 0].hist(bins = 40)
將首購(gòu)與末購(gòu)相隔天數(shù)大于0的單獨(dú)挑出來(lái)進(jìn)行觀察劲装,可以發(fā)現(xiàn)有接近1200名用戶兩次消費(fèi)間隔天數(shù)約在25天內(nèi),間隔400到500天的也有很大一部分
3)用戶分層——RFM模型
# 先對(duì)原始數(shù)據(jù)進(jìn)行透視
rfm = df.pivot_table(index='user_id',
values=['order_products', 'order_amount', 'order_dt'],
aggfunc={'order_products':'sum',
'order_amount':'sum',
'order_dt':'max'
})
rfm.head()
# 將最早時(shí)間與最晚時(shí)間差轉(zhuǎn)換為浮點(diǎn)數(shù)
rfm['R'] = -(rfm.order_dt - rfm.order_dt.max()) / np.timedelta64(1,'D')
rfm.rename(columns = {'order_products':'F', 'order_amount':'M'}, inplace = True)
#定義RFM函數(shù)
def rfm_func(x):
level = x.apply(lambda x:'1' if x>=0 else '0')
label = level.R + level.F + level.M
d = {
'111' : '重要價(jià)值客戶',
'011' : '重要保持客戶',
'101' : '重要發(fā)展客戶',
'001' : '重要挽留客戶',
'110' : '一般價(jià)值客戶',
'010' : '一般保持客戶',
'100' : '一般挽留客戶',
'000' : '一般發(fā)展客戶',
}
result = d[label]
return result
rfm['label'] = rfm[['R', 'F', 'M']].apply(lambda x: x-x.mean()).apply(rfm_func,axis=1)
rfm.groupby('label').sum()
從RFM分層可知昌简,大量用戶為重要保持客戶占业,但這是受到極值的影響,RFM劃分標(biāo)準(zhǔn)應(yīng)以業(yè)務(wù)為準(zhǔn)
- 盡量用小部分用戶覆蓋大部分額度
- 不用為了數(shù)據(jù)好看劃分等級(jí)
rfm.loc[rfm.label == '重要價(jià)值客戶', 'color'] = 'b'
rfm.loc[~(rfm.label == '重要價(jià)值客戶'), 'color'] = 'r'
rfm.plot.scatter('F', 'R', c = rfm.color)
4)用戶分層——新老客纯赎、活躍度
# 數(shù)據(jù)透視谦疾,user_id為索引,月為列址否,求每月消費(fèi)次數(shù)
pivoted_counts = df.pivot_table(index = 'user_id',
columns = 'month',
values = 'order_dt',
aggfunc = 'count').fillna(0)
pivoted_counts.head()
# 轉(zhuǎn)變維度餐蔬,有消費(fèi)記錄為1,沒(méi)有消費(fèi)記錄為0
df_purchase = pivoted_counts.applymap(lambda x: 1 if x > 0 else 0)
df_purchase.tail()
# 由于進(jìn)行數(shù)據(jù)透視佑附,將一些null值填充為0樊诺,而實(shí)際有可能該用戶當(dāng)月還沒(méi)有注冊(cè),
# 這樣會(huì)讓第一次消費(fèi)統(tǒng)計(jì)數(shù)據(jù)出錯(cuò)音同,因此定義一個(gè)函數(shù)來(lái)進(jìn)行處理
def active_status(data):
status = []
for i in range(18):
#若本月沒(méi)有消費(fèi)
if data[i] == 0:
if len(status) > 0:
if status[i-1] == 'unreg':
status.append('unreg')
else:
status.append('unactive')
else:
status.append('unreg')
#若本月有消費(fèi)
else:
if len(status) == 0:
status.append('new')
else:
if status[i-1] == 'unactive':
status.append('return')
elif status[i-1] == 'unreg':
status.append('new')
else:
status.append('active')
return status
若本月沒(méi)有消費(fèi)
- 若之前是未注冊(cè)词爬,則依舊為未注冊(cè)
- 若之前有消費(fèi),則為流失/不活躍
- 其他权均,為未注冊(cè)
若本月有消費(fèi)
- 若是第一次消費(fèi)顿膨,則為新用戶
- 若之前有消費(fèi),上個(gè)月為不活躍叽赊,則為回流
- 若上個(gè)月未注冊(cè)恋沃,則為新用戶
- 其他,為活躍用戶
indexs=df['month'].sort_values().astype('str').unique() #astype 的區(qū)別
purchase_stats = df_purchase.apply(lambda x:pd.Series(active_status(x),index=indexs),axis=1)
purchase_stats.head(5)
# 把未注冊(cè)的記錄替換為空值必指,這樣count計(jì)算時(shí)不會(huì)被計(jì)算到
purchase_stats_ct = purchase_stats.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x))
purchase_stats_ct
可以發(fā)現(xiàn)活躍用戶在減少囊咏,非活躍用戶不斷增加
# T轉(zhuǎn)置,同時(shí)求出所有用戶占比
purchase_stats_ct.fillna(0).T.apply(lambda x:x/x.sum(),axis = 1)
# 繪制面積圖
purchase_stats_ct.fillna(0).T.plot.area()
由上圖可知每月不同消費(fèi)狀態(tài)的人群變化情況
5)用戶購(gòu)買周期
# 計(jì)算兩個(gè)訂單的時(shí)間間隔
order_diff=grouped_user.apply(lambda x:x.order_dt-x.order_dt.shift())
order_diff.head(10)
order_diff.describe()
# 去除單元值并作圖
(order_diff/np.timedelta64(1,'D')).hist(bins=20)
可以發(fā)現(xiàn),用戶的購(gòu)買周期具有以下幾個(gè)特點(diǎn):
- 訂單周期呈指數(shù)型分布
- 用戶平均購(gòu)買周期是68天
- 大部分用戶購(gòu)買周期低于100天
5梅割、復(fù)購(gòu)率和回購(gòu)率分析
- 復(fù)購(gòu)率:自然月內(nèi)購(gòu)買兩次以上的用戶占比
- 回購(gòu)率:某一時(shí)期內(nèi)曾經(jīng)購(gòu)買過(guò)的用戶再次購(gòu)買的占比
# 復(fù)購(gòu)率
pivoted_counts.head(10)
# 區(qū)分一次和一次以上的情況霜第,以便計(jì)算復(fù)購(gòu)率(大于1為1,等于1為0户辞,等于0為NaN)
purchase_r=pivoted_counts.applymap(lambda x:1 if x>1 else np.NaN if x==0 else 0)
purchase_r.head()
# 復(fù)購(gòu)人數(shù)/總消費(fèi)人數(shù)
(purchase_r.sum()/purchase_r.count()).plot(figsize=(10,4))
可以發(fā)現(xiàn)泌类,復(fù)購(gòu)率大體穩(wěn)定在20%左右,前三個(gè)月大量只購(gòu)買一次的新用戶的涌入底燎,導(dǎo)致復(fù)購(gòu)率較低
# 回購(gòu)率刃榨,知道本月是否有消費(fèi)記錄即可
df_purchase.head()
# 使用函數(shù)來(lái)定義回購(gòu)率,當(dāng)月已消費(fèi)的用戶下月也有消費(fèi)記錄的計(jì)入回購(gòu)
def purchase_back(data):
status=[]
"""判斷每個(gè)月是否有回購(gòu)书蚪,根據(jù)上個(gè)月是否有購(gòu)買記錄進(jìn)行判斷喇澡,上個(gè)月有消費(fèi)本月沒(méi)有消費(fèi)就不是回購(gòu)"""
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ǔ)充NaN
"""語(yǔ)句中的是 第一位與第二位相比 輸出結(jié)構(gòu)放第一位, 循環(huán)17次殊校,17/18的判斷結(jié)束后,輸出的status僅17個(gè)读存,需補(bǔ)上最后一個(gè)"""
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()
# 求得回購(gòu)率
(purchase_b.sum()/purchase_b.count()).plot(figsize=(10,4))
可以發(fā)現(xiàn)絕大部分用戶購(gòu)買一次后不再購(gòu)買为流,老用戶回購(gòu)率在30%左右
四、總結(jié)
1让簿、cd網(wǎng)站在前三個(gè)月涌入了絕大多數(shù)的新用戶敬察,月訂單數(shù)量在11000左右,月消費(fèi)人數(shù)在8000到10000之間尔当,之后均回落至2000左右的水平
2莲祸、50%的用戶僅貢獻(xiàn)了15%的消費(fèi)額度,消費(fèi)排名前5000的用戶就貢獻(xiàn)了60%的消費(fèi)額椭迎,符合二八法則锐帜。同時(shí)該網(wǎng)站用戶平均購(gòu)買了7張cd,中位數(shù)只有3畜号,說(shuō)明有小部分用戶購(gòu)買了大量CD缴阎,同樣也符合二八法則
3、根據(jù)用戶首購(gòu)简软、末購(gòu)情況蛮拔,可以發(fā)現(xiàn)用戶的受次購(gòu)買集中分布在前三個(gè)月,且購(gòu)買一次后便不再購(gòu)買痹升,該部分用戶占比50%(符合2中提及的50%用戶僅貢獻(xiàn)15%的消費(fèi)額度)
4建炫、根據(jù)用戶分層RFM模型以及新老客、活躍度情況的分析疼蛾,大量用戶為重要保持客戶肛跌,絕大多數(shù)新用戶集中在前三個(gè)月,之后大部分轉(zhuǎn)為非活躍用戶
5、按用戶購(gòu)買周期分析惋砂,用戶平均購(gòu)買周期為68天妒挎,大部分用戶購(gòu)買周期集中在100天內(nèi)
6、從復(fù)購(gòu)率和回購(gòu)率的角度來(lái)看西饵,該cd網(wǎng)站的復(fù)購(gòu)率答題穩(wěn)定在20%左右酝掩,回購(gòu)率在30%左右