項目分享原因:在學習完Numpy,Pandas,matplotlib后,熟練運用它們的最好方法就是實踐並總結。在下面的分享中,我會將每一步進行分析與代碼展示,
希望能對大家有所幫助。
項目名稱:CD用戶消費行為分析
項目概述:本項目主要利用上面提到的三個工具進行數據的處理,來分析用戶消費行為。數據來源與CDNow網站的用戶購買明細。
數據鏈接:鏈接:https://pan.baidu.com/s/1Cs36oeH0xgblzULmgX75-g 密碼:oeam
分析步驟:
第一部分:數據清洗
1. 數據類型的轉換
2. 空值處理
3. 異常值處理
第二部分:按月數據分析
1. 每月的消費總金額
2. 每月的消費次數
3. 每月的產品購買量
4. 每月的消費人數
第三部分:用戶個體消費數據分析
1. 用戶消費金額和消費次數的描述統計
2. 用戶消費金額和消費次數的散點圖
3. 用戶消費金額的分布圖
4. 用戶消費次數的分布圖
5. 用戶累計消費金額的占比
第四部分:用戶消費行為分析
1. 用戶第一次消費時間
2. 用戶最后一次消費時間
3. 新老客戶消費比
4. 用戶分層
5. 用戶購買周期
6. 用戶生命周期
導包

1 import numpy as np 2 import pandas as pd 3 from pandas import Series,DataFrame 4 import matplotlib.pyplot as plt 5 %matplotlib inline 6 from datetime import datetime
數據載入
1 # 因為原始數據中不包含表頭,在這里定義好賦值 2 columns = ['user_id','order_dt','order_products','order_amount'] 3 4 # 參數 sep='\s+',用於匹配任意空白符 5 data = pd.read_csv('./CDNOW_master.txt',names=columns,sep='\s+')
''' 字段含義: user_id:用戶ID order_dt:購買日期 order_products:購買產品數 order_amount:購買金額 '''
'''
消費行業或者是電商行業一般是通過訂單數、訂單額、購買日期,用戶ID這四個字段來分析的,基本上這四個字段就能緱進行很豐富的分析。
'''
查看數據概況
display(data.head(),data.info()) ''' 結果如下: <class 'pandas.core.frame.DataFrame'> RangeIndex: 69659 entries, 0 to 69658 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 user_id 69659 non-null int64 1 order_dt 69659 non-null int64 2 order_products 69659 non-null int64 3 order_amount 69659 non-null float64 dtypes: float64(1), int64(3) memory usage: 2.1 MB user_id order_dt order_products order_amount 0 1 19970101 1 11.77 1 2 19970112 1 12.00 2 2 19970112 5 77.00 3 3 19970102 2 20.76 4 3 19970330 2 20.76 '''
分析:
1. 數據完整,沒有空數據
2. order_dt是 int類型,需要將其轉換為時間類型
3. 用戶可能在同一天內重復購買(如:ID為2的顧客在1月12日這一天內購買了兩次)
4. 因為后面要按月分析,所以需要添加一列 month
數據清洗
1 # order_dt 數據類型轉換 2 3 df = data.copy() 4 # format='%Y%m%d' 這里要指明格式,否則可能出錯 5 df['order_dt'] = pd.to_datetime(df['order_dt'],format='%Y%m%d') 6 7 # 增加一列 month 8 df['month'] = df['order_dt'].values.astype('datetime64[M]') 9 10 display(df.head(),df.info(),df.describe()) 11 12 ''' 13 結果如下: 14 <class 'pandas.core.frame.DataFrame'> 15 RangeIndex: 69659 entries, 0 to 69658 16 Data columns (total 5 columns): 17 # Column Non-Null Count Dtype 18 --- ------ -------------- ----- 19 0 user_id 69659 non-null int64 20 1 order_dt 69659 non-null datetime64[ns] 21 2 order_products 69659 non-null int64 22 3 order_amount 69659 non-null float64 23 4 month 69659 non-null datetime64[ns] 24 dtypes: datetime64[ns](2), float64(1), int64(2) 25 memory usage: 2.7 MB 26 user_id order_dt order_products order_amount month 27 0 1 1997-01-01 1 11.77 1997-01-01 28 1 2 1997-01-12 1 12.00 1997-01-01 29 2 2 1997-01-12 5 77.00 1997-01-01 30 3 3 1997-01-02 2 20.76 1997-01-01 31 4 3 1997-03-30 2 20.76 1997-03-01 32 user_id order_products order_amount 33 count 69659.000000 69659.000000 69659.000000 34 mean 11470.854592 2.410040 35.893648 35 std 6819.904848 2.333924 36.281942 36 min 1.000000 1.000000 0.000000 37 25% 5506.000000 1.000000 14.490000 38 50% 11410.000000 2.000000 25.980000 39 75% 17273.000000 3.000000 43.700000 40 max 23570.000000 99.000000 1286.010000 41 '''
分析:
describe()是描述統計:
1. 大部分的訂單只消費了少量的商品(平均2.4),有一定的極值干擾
2. 用戶的消費金額比較穩定,平均消費在35.8元,中位數在25.9元,有一定的極值干擾。
3. 用戶平均每筆訂單購買2.4個商品,標准差在2.3,稍具波動性。中位數2個商品,75分位數3個商品,
說明絕大部分訂單的購買量都不多。最大值在99個,數字比較高。購買金額的情況差不多,大部分
訂單集中在小額
4. 一般而言,消費類的數據分布都是長尾形。大部分用戶都是小額,然而小部分用戶貢獻了收入的大頭,
俗稱二八。
第二部分:按月數據分析
分析方向:用戶、訂單、消費趨勢
消費趨勢的分析
1 # 查看一下df的列,方便操作 2 df.columns
1. 每月的消費總金額
# 根據 month分組統計購買金額總和 order_amt_mon = df.groupby('month')['order_amount'].sum() order_amt_mon.head() ''' 結果: month 1997-01-01 299060.17 1997-02-01 379590.03 1997-03-01 393155.27 1997-04-01 142824.49 1997-05-01 107933.30 Name: order_amount, dtype: float64 '''
# 繪圖 order_amt_mon.plot(c='red')
分析:
可以看到,97年1,2,3月銷量很高,每月平局約3.6萬,后期銷量趨於平穩,每月在1萬左右波動
2. 每月的消費次數
df.groupby('month')['user_id'].count().plot(c='red')
分析:前三個月平均消費訂單在1萬左右,后續月份趨於平穩,約在2500單每月
3. 每月的產品購買量
df.groupby('month')['order_products'].sum().plot(c='red',figsize=(9,6))
分析:前3個月產品購買數量平均在24000左右,后期下降趨於平穩,約6000每月。原因猜想:
1.用戶層面,早期用戶中有異常值;
2. 公司層面,在搞促銷等。因為只有銷售數據,所以暫時無法判斷具體原因。
4. 每月的消費人數(去重)
# 去重的原因:一個人可能在一個月內多次消費 df.groupby('month')['user_id'].nunique().plot()
分析:每個月的消費人數小於每月的消費次數,但是區別不大。前3個月月均消費人數在9000左右,后續月均2000
不到,一樣是前期消費人多,后期平穩的趨勢。
5. 將上述趨勢分析用透視表展示(pivot_table)
df.pivot_table(index='month', values=['user_id','order_amount','order_products'], aggfunc={'user_id':'count','order_amount':'sum','order_products':'sum'})
分析:(解決一個需求可能會有很多種方法,具體看哪個更方便,更簡單)
數據透視表是更簡單的方法,有了透視表,用里面的數據繪圖也是狠方便的。
第三部分:用戶個體消費數據分析
上面是通過維度月,來看總體趨勢。下面對個體進行分析,看消費能力如何。 大致分為以下五個方向:
1. 用戶消費金額和消費次數的描述統計;
2. 用戶消費金額和消費次數的散點圖;
3. 用戶消費金額的分布圖(二八法則);
4. 用戶消費次數的分布圖;
5. 用戶累計消費金額的占比(百分之多少的用戶占了百分之多少的消費額)
1. 用戶消費金額和消費次數的描述統計
1 group_user = df.groupby('user_id') 2 3 group_user.sum().describe()
分析:
1. 從用戶角度看,每位用戶平均購買7件商品,最多的用戶買了1033件。
2. 用戶平均消費額(客單價)100元,標准差是240,結合分位數和最大值看,平均值和75分位接近。
結論:肯定存在小部分高額消費用戶,小部分的用戶占了消費的大頭,符合二八法則。
2.用戶消費金額和消費次數的散點圖
group_user.sum().query('order_amount < 4000').plot(kind='scatter',x='order_amount',y='order_products')
分析:
通過繪制用戶的散點圖,用戶比較健康而且規律性很強。因為這是CD網站的銷售數據,商品比較單一,金額和商品質量的關系 也呈線性,沒幾個離群點。
3. 用戶消費金額的分布圖(二八法則)
group_user.sum()['order_amount'].plot(kind='hist',bins=20)
分析:
1. 從直方圖可知,大部分用戶的消費能力確實不高,絕大部分集中在很低的消費檔次。高消費用戶在圖上幾乎看不到。這也確實
符合消費行為的行業規律。
2. 雖然有極值干擾了我們的數據,但是大部分用戶還是集中在比較低的消費檔次。
4. 用戶消費次數的分布圖(二八法則)
group_user.sum().query('order_products < 100')['order_products'].plot(kind='hist',bins=40)
5. 用戶累計消費金額的占比(百分之多少的用戶占了百分之多少的消費額)
1 # cumsum() 滾動累加求和 2 user_cumsum = (group_user.sum().sort_values('order_amount').cumsum())/2500315.63 3 user_cumsum
user_cumsum.reset_index().order_amount.plot()
分析:
按用戶消費金額進行升序排序,由圖可知50%的用戶僅貢獻了15%的銷售額。而排名前5000的用戶就貢獻了60%的消費額
也就是說,只要維護好這5000個客戶,就可以完成業績KPI的60%,如果能把5000個用戶運營的更好就可以占比70%-80%甚至更高。
第四部分:用戶消費行為分析
用戶第一次消費(首購)
在很多行業中首購是一個很重要的維度,它和渠道信息息息相關,尤其針對客單價比較高客戶留存率比較低的行業,
第一次客戶從哪里來可以拓展出很多運營方式。
用戶最后一次消費
新老客戶消費比
多少客戶僅消費了一次
每月新客占比
用戶分層
RFM
新、老、活躍、流失
用戶購買周期(按訂單)
用戶消費周期描述
用戶消費周期分布
用戶生命周期(按第一次&最后一次消費)
用戶生命周期描述
用戶生命周期分布
1.用戶第一次消費(首購)
# 求月份的最小值,即用戶消費行為中的第一次消費時間 group_user = df.groupby('user_id') group_user['month'].min().value_counts() # 通過統計結果發現:所有用戶第一次消費都集中在前3個月 df['user_id'].unique().size---查看用戶總數 ''' 結果: 1997-02-01 8476 1997-01-01 7846 1997-03-01 7248 Name: month, dtype: int64 '''
2. 用戶最后一次消費
group_user['month'].max().value_counts() ''' 結果: 1997-02-01 4912 1997-03-01 4478 1997-01-01 4192 1998-06-01 1506 1998-05-01 1042 1998-03-01 993 1998-04-01 769 1997-04-01 677 1997-12-01 620 1997-11-01 609 1998-02-01 550 1998-01-01 514 1997-06-01 499 1997-07-01 493 1997-05-01 480 1997-10-01 455 1997-09-01 397 1997-08-01 384 Name: month, dtype: int64 '''
group_user['order_dt'].max().value_counts().plot()
group_user['order_dt'].agg(['min','max'])
分析:
觀察用戶最后一次購買時間發現,用戶最后一次消費比第一次消費分布廣,大部分最后一次消費集中在前三個月,
說明很多客戶購買一次后就不再購買。隨着時間的增長,最后一次購買數也在遞增,消費呈現流失上升的情況,用戶忠誠度在慢慢下降。
3. 新老客戶的消費比
user_life = group_user['order_dt'].agg(['min','max']) user_life.head()
分析:user_id為1的用戶第一次消費時間和最后一次消費時間相同,說明他只消費了一次
4. 用戶的購買周期
(user_life['min'] == user_life['max']).value_counts() '''
結果:
True 12054 False 11516 dtype: int64 '''
分析:可以看到,有一半的用戶只消費了一次
5. 用戶分層(使用透視表)
1 rfm = df.pivot_table(index='user_id', 2 values=['order_products','order_amount','order_dt'], 3 aggfunc={'order_products':'sum','order_amount':'sum','order_dt':'max'}) 4 5 rfm.head() 6 7 # order_products--求消費產品總數、order_amount---求消費總金額、order_dt--求最近一次消費時間
1 # rfm 距今天數 增加一列 2 3 #-(rfm.order_dt - rfm.order_dt.max())結果為時間類型,將時間格式轉化為整數或者浮點數的形式, 4 # 可以除以單位‘D’,也可以用astype轉化 5 rfm['R'] = -(rfm['order_dt'] - rfm['order_dt'].max()) / np.timedelta64(1,'D') 6 7 rfm.rename(columns ={'order_products':'F', 'order_amount':'M'},inplace = True ) 8 9 rfm.head()
分析:
R表示客戶最近一次交易的時間間隔,M表示客戶在最近一段時間內交易的金額。F表示客戶在最近一段時間內交易的次數。
F值越大,表示客戶交易越頻繁,反之則表示客戶交易不夠活躍。M表示客戶在最近一段時間內交易的金額。
M值越大,表示客戶價值越高,反之則表示客戶價值越低。
5. 用戶分層二:RFM
1 def rfm_func(x): 2 level = x.apply(lambda x : '1' if x >= 0 else '0') 3 label = level.R + level.F + level.M 4 5 dict = { 6 '111':'重要價值客戶', 7 '011':'重要保持客戶', 8 '101':'重要挽留客戶', 9 '001':'重要發展客戶', 10 '110':'一般價值客戶', 11 '010':'一般保持客戶', 12 '100':'一般挽留客戶', 13 '000':'一般發展客戶' 14 } 15 16 result = dict[label] 17 return result 18 19 # 用戶分層,這里使用平均數 20 rfm['label'] = rfm[['R','F','M']].apply(lambda x : x - x.mean()).apply(rfm_func,axis=1) 21 22 rfm.head()
5. 用戶分層三:求和
rfm.groupby('label').sum()
分析:M表示不同層次客戶累計消費金額,重要保持客戶最高
5. 用戶分層四:計數
rfm.groupby('label').count()
分析:不同層次用戶的消費人數,之前重要保持客戶的累計消費金額最高,這里人數排第2,但與一般挽留用戶人數差距比較大
5. 用戶分層五:給不同層次客戶用顏色區分設置
1 rfm.loc[rfm.label == '重要價值客戶','color'] = 'g' 2 3 # ~:表示求非 4 rfm.loc[~(rfm.label == '重要價值客戶'),'color'] = 'r' 5 6 rfm.plot('F','R',kind='scatter',c=rfm.color)
分析:
1. 從RFM分層可知,大部分用戶為重要保持客戶,但是這是由於極值的影響,所以RFM的划分應該盡量以業務為准。
盡量用小部分的用戶覆蓋大部分的額度,不能為了數據好看划分等級。
2. RFM是人工使用象限法把數據划分為幾個立方體,立方體對應相應的標簽,我們可以把標簽運用到業務層面上。
比如重要保持客戶貢獻金額最多159203.62,我們如何與業務方配合把數據提高或者維護;而重要發展客戶
和重要挽留客戶他們有一段時間沒消費了,我們如何把他們拉回來。
5. 用戶分層六:用戶生命周期
1 pivoted_counts = df.pivot_table(index='user_id', 2 columns='month', 3 values='order_dt', 4 aggfunc='count', 5 fill_value=0) 6 pivoted_counts.head()
分析:
用戶每個月的消費次數,對於生命周期的划分只需要知道用戶本月是否消費,消費次數在這里並不重要,需要將模型進行簡化
注:使用數據透視表時,要明確獲得什么結果。
# 簡化 df_purchase = pivoted_counts.applymap(lambda x: 1 if x>0 else 0) df_purchase.tail()
分析:對於尾部數據,user_id 2w+的數據是有一些問題的,因為從實際業務場景來說,一月二月他們都沒有注冊,三月份才是
第一次消費。這里需要進行判斷將第一次消費作為生命周期的起始,不能從一月份開始就粗略的計算。
1 # 用戶生命周期狀態變化 2 3 def active_status(data): 4 5 ur = 'unreg' #未注冊 6 ua = 'unactive' #不活躍 7 n = 'new' #新用戶 8 a = 'active' #活躍 9 r = 'return' #回流用戶:上個月不活躍,這個月活躍 10 status = [] 11 for i in range(18): 12 #若本月沒有消費 13 if data[i] == 0: 14 if len(status) > 0: 15 if n not in status: 16 status.append(ur) 17 else: 18 status.append(ua) 19 else: 20 status.append(ur) 21 22 #若本月消費 23 else: 24 if len(status) == 0: 25 status.append(n) 26 else: 27 if n not in status: 28 status.append(n) 29 elif status[-1] == ua: 30 status.append(r) 31 else: 32 status.append(a) 33 # 不能直接返回 status,否則會失去表頭 ---重點 34 return pd.Series(status, index = df_purchase.columns) 35 36 pivoted_status = df_purchase.apply(active_status,axis = 1) 37 38 pivoted_status.head()
每月不同活躍用戶的計數
purchase_status_ct = pivoted_status.replace('unreg',np.NaN).apply(lambda x: pd.value_counts(x)) purchase_status_ct
1 purchase_status_ct.fillna(0,inplace=True) 2 3 # 浮點數轉換為整數 4 purchase_status_ct.astype(np.int) 5 6 # 繪面積圖 (purchase_status_ct要求一下轉置矩陣) 7 purchase_status_ct.T.plot(kind='area')
每月不同活躍用戶占比
消費用戶構成:活躍 + 新增 + 回流
purchase_status_ct.T.apply(lambda x: x/x.sum(),axis=1)
分析報告:
由上表可以看到每月用戶的消費狀態變化。
1. 活躍用戶,持續消費用戶對應的是---消費運營質量;
2. 回流用戶(上月不消費本月消費)對應的是---喚回運營情況;
3. 不活躍的用戶對應的是---用戶流失情況。
分析結果:流失用戶增加,回流用戶正在減少。
5. 用戶分層七:用戶購買周期(按訂單)
# 將用戶分組后,每個用戶的訂單購買時間進行錯位相減 shift():下一行減上一行的值 order_diff = group_user.apply(lambda x:x.order_dt - x.order_dt.shift()) order_diff.head(10) ''' 結果: user_id 1 0 NaT 2 1 NaT 2 0 days 3 3 NaT 4 87 days 5 3 days 6 227 days 7 10 days 8 184 days 4 9 NaT Name: order_dt, dtype: timedelta64[ns] '''
分析:
1. 可以看到:user_id 1為空值,說明用戶只購買過一個訂單
2. user_id 2 的用戶第一筆訂單與第二筆訂單在同一天購買
用戶消費周期分布
(order_diff / np.timedelta64(1,'D')).hist(bins = 20)
分析:
訂單周期呈指數分布,用戶的平均購買周期是68天,絕大部分用戶的購買周期都低於100天。
用戶生命周期(第一筆訂單時間 & 最后一筆訂單時間)
user_life = group_user['order_dt'].agg(['min','max']) user_life.head()
user_life['life_period'] = user_life['max'] - user_life['min'] user_life.head()
user_life['life_period'].describe() ''' 結果: count 23570 mean 134 days 20:55:36.987696 std 180 days 13:46:43.039788 min 0 days 00:00:00 25% 0 days 00:00:00 50% 0 days 00:00:00 75% 294 days 00:00:00 max 544 days 00:00:00 Name: life_period, dtype: object '''
分析:
可以看到,數據偏移較大,中位數是0天,意味着超過50%的用戶生命周期是0天,即只購買了1次。
(user_life['life_period'] / np.timedelta64(1,'D')).plot(kind='hist',bins=40)
分析:
可以看出,用戶生命周期受只購買一次的用戶影響比較大(因此可以排除生命周期為0天的用戶再觀察)
# 用戶生命周期大於0天的分布圖 cond = user_life['life_period'] / np.timedelta64(1,'D') cond[cond>0].hist(bins=40)
分析:
1. 有不少用戶生命周期靠攏在0天,部分質量差的用戶雖然消費了兩次,但是仍然無法持續,
用戶首次消費30天以內應該盡量引導;
2. 少部分用戶集中在50-300天,屬於普通型的生命周期;
3. 高質量用戶的生命周期,集中在400天以后,這屬於忠誠用戶。
6. 復購率和回購率的分析
復購率:自然月內,購買多次的用戶占比
回購率:曾經購買過的用戶在某一時期內的再次購買占比
# applymap()針對DataFrame里的所有數據。使用lambda函數,因為設計了多個結果,所以要用兩個if else 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))
分析:
1. 用sum和count相除即可計算出復購率。這兩個函數都會忽略掉NaN,而NaN是沒有消費的用戶,count不論是0或1都會統計,
所以是總的消費用戶數。而sum求和計算了消費兩次及以上的用戶。這里比較巧妙的用了替代法計算復購率。sql中也可以用。
2. 圖上可以看出復購率在早期,因為大量新用戶加入的關系,新客的復購率並不高,譬如1月新客們的復購率只有6%左右。
而在后期,這時的用戶都是大浪淘沙剩下的老客戶,復購率比較穩定,在20%左右。單看新客和老客,復購率有三倍左右的差距。
回購率:回購率是某一個時間窗口內消費的用戶,在下個時間窗口仍舊消費的占比。
1 # 消費金額進行透視 2 3 pivoted_amount = df.pivot_table(index='user_id', 4 columns='month', 5 values='order_amount', 6 aggfunc='mean') 7 pivoted_amount.fillna(0,inplace=True) 8 columns_month = df['month'].sort_values().astype('str').unique() 9 pivoted_amount.columns = columns_month 10 11 # pivoted_amount.head() 12 pivoted_amount.head()
pivoted_purchase = pivoted_amount.applymap(lambda x : 1 if x>0 else 0) pivoted_purchase.head()
1 # 0代表當月消費過次月沒有消費過,1代表當月消費過次月依然消費 2 3 def purchase_return(data): 4 status = [] 5 for i in range(17): 6 if data[i] == 1: 7 if data[i+1] ==1: 8 status.append(1) 9 if data[i+1] == 0: 10 status.append(0) 11 else: 12 status.append(np.NaN) 13 status.append(np.NaN) 14 return pd.Series(status, index = pivoted_purchase.columns) 15 16 pivoted_purchase_return = pivoted_purchase.apply(purchase_return,axis = 1) 17 18 pivoted_purchase_return.head()
# 回購率,計算方法和復購率類似,同樣的邏輯 (pivoted_purchase_return.sum()/pivoted_purchase_return.count()).plot(figsize=(10,4))
分析:
1. 從上圖看出,用戶的回購率高於復購率,約在30%左右,和老客戶差異不大。
2. 從回購率和復購率綜合分析,新客的整體質量低於老客,老客的忠誠度(回購率)很好,消費頻次稍次