前言:
存在一份用戶在一家cd網(wǎng)站上的消費(fèi)記錄,對此進(jìn)行消費(fèi)情況及用戶行為分析。
數(shù)據(jù)樣式:

數(shù)據(jù)來源:
鏈接:https://pan.baidu.com/s/1l68MIJil0-kZQg5DAxIcdg
提取碼:w50i
具體步驟:
1.數(shù)據(jù)清洗
2.進(jìn)行用戶行為銷售分析(按月):每月消費(fèi)總額、次數(shù)、產(chǎn)品購買量、人數(shù)
3.用戶個(gè)體消費(fèi)分析:重點(diǎn)關(guān)注消費(fèi)金額和消費(fèi)次數(shù)情況
4.用戶消費(fèi)行為分析:首購、最后一次購買、用戶分層(新老客、RFM)、用戶購買周期、用戶生命周期
5.用戶回購率和復(fù)購率分析
數(shù)據(jù)清洗:
1.導(dǎo)入常用包及數(shù)據(jù)集
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+")

2.檢查數(shù)據(jù)并清洗整理
df.info() #order_dt int64 為整數(shù)型 需改成日期型

df['order_dt']=pd.to_datetime(df.order_dt,format="%Y%m%d")
df['month']=df.order_dt.values.astype("datetime64[M]") #astype 轉(zhuǎn)換格式

按月分析故新列【month】字段
一、進(jìn)行用戶消費(fèi)趨勢分析(按月)
1.每月的消費(fèi)總金額
grouped_month = df.groupby('month')
order_month_amount=grouped_month.order_amount.sum()
order_month_amount.head()

導(dǎo)入可視化包
#加載數(shù)據(jù)可視化包
import matplotlib.pyplot as plt
#可視化顯示在頁面,%代表內(nèi)置命令,inline 顯示圖標(biāo)
%matplotlib inline
# 更改設(shè)計(jì)風(fēng)格
plt.style.use('ggplot')
order_month_amount.plot() #plot為折線圖

由上圖可知,消費(fèi)金額在前三個(gè)月達(dá)到最高分,后續(xù)消費(fèi)金額較為平穩(wěn),有輕微下降趨勢
2.每月的消費(fèi)次數(shù)
grouped_month.user_id.count().plot()

前三個(gè)月消費(fèi)訂單數(shù)在10000筆左右,后續(xù)月份的平均消費(fèi)人數(shù)則在2500人
3.每月的產(chǎn)品購買量
grouped_month.order_products.sum().plot()

前三個(gè)月產(chǎn)品購買數(shù)在20000以上,后續(xù)月份的產(chǎn)品購買量在6000~8000左右 。
4.每月的消費(fèi)人數(shù)
df.groupby("month").user_id.apply(lambda x:len(x.drop_duplicates())).plot()

每月消費(fèi)人數(shù)低于每月消費(fèi)次數(shù),但差異不大 前三個(gè)月每月的消費(fèi)人數(shù)在8000-10000之間,后續(xù)月份平均消費(fèi)人數(shù)在2000人不到
5.直接利用數(shù)據(jù)透視表分析消費(fèi)金額、產(chǎn)品購買量、消費(fèi)次數(shù)
pivot_df=df.pivot_table(index ="month",
values = ["order_products","order_amount","user_id"],
aggfunc = {"order_products":'sum',
"order_amount":"sum",
"user_id":"count"}).head()
#數(shù)據(jù)透視表進(jìn)行去重操作比較麻煩,不建議

pivot_df.plot()

二、用戶個(gè)體消費(fèi)分析
1.用戶消費(fèi)金額、消費(fèi)次數(shù)的描述統(tǒng)計(jì)
grouped_user = df.groupby('user_id')
grouped_user.sum().describe()

用戶平均購買了5.39張CD,但是中位數(shù)只有1,說明小部分用戶購買了大量的CD
用戶平均消費(fèi)3.7元,中位值有7.37,判斷同上,有極值干擾
2.用戶消費(fèi)金額和消費(fèi)的散點(diǎn)圖
grouped_user.sum().plot.scatter(x = "order_amount",y = "order_products")

grouped_user.sum().query("order_amount<4000000").plot.scatter(x = "order_amount",y = "order_products")

3.用戶消費(fèi)金額的分布圖
grouped_user.sum().order_amount.plot.hist(bins = 20) #bins是分組,分20個(gè)組

從直方圖可知,用戶消費(fèi)金額,絕大部分呈現(xiàn)幾種趨勢,部分異常值干擾了判斷??梢允褂眠^濾操作排除異常
grouped_user.sum().query("order_products < 92").order_amount.plot.hist(bins = 40)

使用切比雪夫定理過濾異常值,計(jì)算95%的數(shù)據(jù)的分布情況。 mean ± 5std (order_products 7+17*5=92)
4.用戶累計(jì)消費(fèi)金額占比(百分之多少的用戶占了百分值多少的消費(fèi)額)
user_cumsum = grouped_user.sum().sort_values("order_amount").apply(lambda x:x.cumsum()/x.sum())
user_cumsum.reset_index().order_amount.plot(figsize = (10,2)) #reset_index()去掉索引 方便作圖

按用戶消費(fèi)金額進(jìn)行升序排列,由圖可知 50% 的用戶 僅貢獻(xiàn)了15%的消費(fèi)額度。而排名前5000的用戶貢獻(xiàn)了60%的消費(fèi)額度。
三、用戶消費(fèi)行為
1.用戶第一次消費(fèi)(首購)
grouped_user.min().order_dt.value_counts().plot() #最小時(shí)間,距離用戶最遙遠(yuǎn),就是第一次購買的時(shí)間,value_counts()計(jì)數(shù)每天的個(gè)數(shù)

用戶第一次購買分布,集中在前三個(gè)月
其中,在2月11日至2月25日有一次劇烈波動(dòng)
2.用戶最后一次消費(fèi)
grouped_user.max().order_dt.value_counts().plot()

用戶最后一次購買的分布比第一次分布廣
大部分最后一次購買,集中在前三個(gè)月,說明很多用戶購買了一次后就不再進(jìn)行購買
3.新老客消費(fèi)比
3.1多少用戶僅消費(fèi)了一次?
user_life = grouped_user.order_dt.agg(["min","max"])
user_life.head()

(user_life["min"] == user_life["max"]).value_counts()

有一半用戶,就消費(fèi)了一次
3.2每月新客戶占比?
grouped_um = df.groupby(['month','user_id']).order_dt.agg(["min","max"]) #按月分組下的userid分組,求每月的最早購買日期和最晚消費(fèi)日期
grouped_um["new"] = (grouped_um["min"] == grouped_um["max"] ) # 新增列 True 為 新用戶
grouped_um.reset_index().groupby("month").new.value_counts()

grouped_um.reset_index().groupby("month").new.count()

grouped_um1 = grouped_um.reset_index().groupby("month") #重新按月分組
grouped_um2 = grouped_um1["new"].apply(lambda x : x.value_counts()/x.count()).reset_index() #求新老用戶占比
grouped_um2

grouped_um2[grouped_um2["level_1"]].plot(y = 'new', x ="month") #利用布爾值篩選True 作圖

97年3至4月新用戶數(shù)量由90%跌落80%,后幾個(gè)月新用戶量保持在80~82%區(qū)間,1998年6月后再無新用戶
4.用戶分層-RFM

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()

-(rfm.order_dt - rfm.order_dt.max()) # 最早時(shí)間與最晚時(shí)間差

rfm["R"] = -(rfm.order_dt - rfm.order_dt.max()) / np.timedelta64(1,"D") #/np.timedelta64(1,"D") 換成浮點(diǎn)數(shù)
rfm.rename(columns = {"order_products":"F","order_amount":"M"},inplace = True) #inplace 代表 是否覆蓋原始二維表
rfm[["R","F","M"]].apply(lambda x : x-x.mean())

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":"一般發(fā)展客戶",
"000":"一般挽留客戶"
}
result = d[label]
return result
rfm['label'] = rfm [["R","F","M"]].apply(lambda x: x-x.mean()).apply(rfm_func,axis = 1) #axis = 1是逐行應(yīng)用

rfm.loc[rfm.label == '重要價(jià)值客戶','color'] = 'g' #green
rfm.loc[~(rfm.label == '重要價(jià)值客戶'),'color'] = 'r' #red
rfm.plot.scatter("F","R",c=rfm.color)

rfm.groupby('label').sum()

從RFM分層可知,大部分用戶為重要保持客戶,但是這是由于極值的影響,所以RFM的劃分標(biāo)準(zhǔn)應(yīng)該以業(yè)務(wù)為準(zhǔn)
盡量用小部分的用戶覆蓋大部分的額度
不要為了數(shù)據(jù)好看劃分等級
5.用戶分層-新、活躍、回流、消失
pivoted_counts=df.pivot_table(index = "user_id",
columns = "month",
values = "order_dt",
aggfunc = "count").fillna(0)
pivoted_counts.head()

df_purchase = pivoted_counts.applymap(lambda x: 1 if x>0 else 0) #簡化模型,只需判斷是否存在 即 1與0
df_purchase.tail () #判斷尾部數(shù)據(jù) 是從 3月份才開始第一次購買

def active_status(data):
status = []
for i in range(18): #12+6個(gè)月
#若本月沒有消費(fèi)
if data[i] == 0:
if len(status) > 0: #判斷 存在記錄的話時(shí)
if status[i-1] == "unreg": #unreg 未注冊
status.append("unreg")
else:
status.append("unactive") # 如果前一個(gè)為 unreg 未注冊,則后一個(gè)應(yīng)判斷為不活躍
else:
status.append("unreg") #不存在記錄時(shí),加入 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

若本月沒有消費(fèi)
- 若之前是未注冊,則依舊為未注冊
- 若之前有消費(fèi),則為流失/不活躍
- 其他情況,為未注冊
若本月有消費(fèi)
- 若是第一次消費(fèi),則為新用戶
- 如果之前有過消費(fèi),則上個(gè)月為不活躍,則為回流
- 如果上個(gè)月為未注冊,則為新用戶
- 除此之外,為活躍
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)

purchase_stats.tail(5)

purchase_stats_ct1 = purchase_stats.apply(lambda x:pd.value_counts(x)) #注意unreg區(qū)別
purchase_stats_ct1

流失用戶(unactive)正在增加
新用戶暫停
活躍用戶越來越少
運(yùn)營差
purchase_stats_ct.fillna(0).T.head() #fillna將空值填充為0, .T轉(zhuǎn)置

purchase_stats_ct.fillna(0).T.plot.area() #.plot.area()面積圖

purchase_stats_ct.fillna(0).T.apply(lambda x:x/x.sum(),axis =1)

由上表可知,每月的用戶消費(fèi)狀態(tài)變化
- 活躍用戶,持續(xù)消費(fèi)的用戶,對應(yīng)的是消費(fèi)運(yùn)營的質(zhì)量
- 回流用戶,之前不消費(fèi)本月才消費(fèi),對應(yīng)的是喚回運(yùn)營
- 不活躍用戶,對應(yīng)的是流失
6.用戶購買周期(按訂單)
- 用戶消費(fèi)周期描述
- 用戶消費(fèi)周期分布
df.head(10)

order_diff = grouped_user.apply(lambda x : x.order_dt -x.order_dt.shift()) #grouped_user = df.groupby('user_id'),shift() 為 兩個(gè)日期錯(cuò)行相減
order_diff.head(10) #目的是求 時(shí)間差值

order_diff.describe() #會(huì)自動(dòng)過濾空值,對時(shí)間差值進(jìn)行描述統(tǒng)計(jì)分析

(order_diff / np.timedelta64(1,"D")).hist(bins = 20)

- 訂單周期呈指數(shù)分布
- 用戶的平均購買周期是68天
- 絕大部分用戶的購買周期都低于100天
7.用戶生命周期(按第一次&最后一次消費(fèi))
- 用戶生命周期描述
-
用戶生命周期分布
image.png
(user_life["max"] - user_life["min"]).describe()

((user_life["max"] - user_life["min"])/np.timedelta64(1,"D")).hist(bins = 40)

用戶的生命周期受只購買一次的用戶影響比較厲害(可以排除)
用戶均消費(fèi)134天,中位數(shù)僅0天
user_life["差值"]=(user_life["max"] - user_life["min"])
user_life.head()

user_life["差值"]=(user_life["max"] - user_life["min"])/np.timedelta64(1,"D")
user_life.head(5)

user_life.query("差值>0")["差值"].hist(bins = 40)

四、復(fù)購率和回購率分析
復(fù)購率
- 自然月內(nèi),購買多次的用戶占比
回購率 - 曾經(jīng)購買過的用戶在某一時(shí)期內(nèi)的的再次購買的占比
pivoted_counts.head()

purchase_r = pivoted_counts.applymap(lambda x: 1 if x>1 else np.NaN if x== 0 else 0 )
purchase_r.head()

(purchase_r.sum()/purchase_r.count()).plot(figsize = (10,4)) #寬為10 高為4 , sum()對于1的求和,count()過濾掉np.Nan

復(fù)購率穩(wěn)定在20%左右,前三個(gè)月因?yàn)橛写罅啃掠脩粲咳耄@批用戶只購買了一次,所以導(dǎo)致復(fù)購率降低
df_purchase.head()

def purchase_back(data):
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)
status.append(np.NaN) #需注意 語句中的是 第一位與第二位相比 輸出結(jié)構(gòu)放第一位, 循環(huán)17次,17/18的判斷結(jié)束后,輸出的status僅17個(gè),需補(bǔ)上最后一個(gè)
return status
#1代表 本月購買及下個(gè)月購買 ,sum()可計(jì)算 ,0代表 本月購買,下個(gè)月未購買 count()可計(jì)算總?cè)藬?shù), 回購率: 本月及下月購買/本月購買人群
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()

(purchase_b.sum()/purchase_b.count()).plot(figsize = (10,4))

絕大部分用戶購買一次后不再購買,老用戶回購率在30%徘徊
總結(jié)
1.cd網(wǎng)站的用戶在前三個(gè)月內(nèi)涌入絕大多數(shù)新用戶,其前三個(gè)月的平均消費(fèi)次數(shù)為10000筆左右,消費(fèi)人次達(dá)8000~10000左右,后續(xù)的消費(fèi)次數(shù)及人次下跌至2000左右。
2.根據(jù)二八法則,50%的用戶僅貢獻(xiàn)了15%的消費(fèi)額度,而消費(fèi)金額前5000名用戶貢獻(xiàn)了60%消費(fèi)額度,同時(shí)根據(jù)用戶購買次數(shù),平均購買7張CD,而中位數(shù)只有3,說明小部分用戶購買大量的CD,符合上述二八法則的分析。
3.根據(jù)用戶首次購買、最后購買及新老客占比分析,絕大部分用戶購買在前三個(gè)月,且購買一次就不再進(jìn)行購買,這部分客戶占50%(符合2的50%用戶僅貢獻(xiàn)15%消費(fèi)額度)。
4.根據(jù)每月新用戶占比分析,3至4月的新用戶占比由90%跌落80%,后幾個(gè)月新用戶量保持在80%到82%區(qū)間,1998年6月后再無新用戶,前三個(gè)月新用戶平均8000人左右,后續(xù)新用戶在1000至1500人波動(dòng)。
5.根據(jù)用戶分層-RFM分析,重要價(jià)值客戶的消費(fèi)金額高,但近期消費(fèi)下降,大部分為重要保持客戶。
6.按用戶購買時(shí)間分析,用戶平均購買周期68天,絕大部分用戶購買周期都低于100天。
7.根據(jù)復(fù)購率和回購率分析,復(fù)購率穩(wěn)定在20%左右,回購率穩(wěn)定在30%左右。
