數據分析項目之:用戶消費行爲分析

項目分享緣由:在學習完Numpy,Pandas,matplotlib後,熟練運用它們的最好方法就是實踐並總結。在下面的分享中,我會將每一步進行分析與代碼展現,html

       但願能對你們有所幫助。sql

項目名稱:CD用戶消費行爲分析app

項目概述:本項目主要利用上面提到的三個工具進行數據的處理,來分析用戶消費行爲。數據來源與CDNow網站的用戶購買明細。ide

數據連接:連接:https://pan.baidu.com/s/1Cs36oeH0xgblzULmgX75-g  密碼:oeam函數

 

分析步驟:工具

    第一部分:數據清洗學習

    1. 數據類型的轉換
    2. 空值處理
    3. 異常值處理

    第二部分:按月數據分析網站

    1. 每個月的消費總金額
    2. 每個月的消費次數
    3. 每個月的產品購買量
    4. 每個月的消費人數

    第三部分:用戶個體消費數據分析spa

    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
View Code

 

數據載入

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. 從回購率和復購率綜合分析,新客的總體質量低於老客,老客的忠誠度(回購率)很好,消費頻次稍次
相關文章
相關標籤/搜索