用實戰玩轉pandas數據分析(一)——用戶消費行為分析(python)


  CD商品訂單數據的分析總結。根據訂單數據(用戶的消費記錄),從時間維度和用戶維度,分析該網站用戶的消費行為。通過此案例,總結訂單數據的一些共性,能通過用戶的消費記錄挖掘出對業務有用的信息。對其他產品的線上消費數據分析有一定的借鑒價值,能達到舉一反三的效果。

訂單交易類數據分析總結:訂單交易數據分析


一、案例背景

  CDNOW是一家美國在線零售商,主營商品為CD唱片,ToC業務。1998年上市,2000年被貝塔斯曼收購。網上有一份用戶訂單消費數據集:CDNOW訂單數據集


二、案例目的

  這份數據集只包含了四個基本信息字段:用戶ID、購買日期、購買數量、購買金額。本案例的目標:通過這個四個字段,在數據集時間窗口內,分析用戶消費的基本概況;通過用戶分層、周期分析、復購率和回購率分析等,梳理現階段用戶的價值現狀,並嘗試根據業務經驗提一些運營建議。


三、分析框架

image
image


四、分析過程

4.1數據加載和初探

(1)導入相關包、設置風格樣式

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import os
# jupter魔法函數,設置可視化頁內顯示
%matplotlib inline
# 正常顯示中文和負號
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 畫圖的樣式風格設置為:ggplot
plt.style.use('ggplot')

(2)導入數據集
字段信息:

  • user_id:用戶ID
  • order_dt:購買日期
  • order_products:購買數量
  • order_amount:購買金額

源數據為txt文件,通過pd.read_csv()。分割符為空白字符串,用正則表達'\s+',匹配任意空白符。

# 導入數據集
os.chdir(r'D:/數據集/電商零售類/CDNOW數據集/')
columns = ['user_id', 'order_dt', 'order_products', 'order_amount']
df = pd.read_csv('CDNOW_Dataset.txt',delimiter='\s+',names=columns)
df.head()
  從前5行數據中可以發現:同一個用戶在同一天可能購買多次、同一用戶在不同時間購買多次等消費現象。

(3)數據清洗

df.info()
  源數據質量描述:數據不存在缺失值,但購買日期order_dt字段數據類型為int64,需要轉換為日期型datetime64[ns]
# 'order_dt'字段類型轉換
df['order_dt'] = pd.to_datetime(df['order_dt'],format='%Y%m%d')

  按月份分析銷售趨勢,新增month字段。(tip:datetime64[M]:每月的第一天日期,datetime64[Y]:每年的第一天日期。)

# 新增month字段
df['month'] = df['order_dt'].values.astype('datetime64[M]')
# 查看字段的數據類型
df.dtypes
df.head()

(4)數據初探

# 數據描述性統計分析
df.describe()

描述性統計分析:

  • 用戶平均每筆訂單購買2.4件商品,中位數為2.0,數據呈現正偏態(右偏)特征,最大值99,需要關注極值點(為何該用戶同時買了那么多CD);
  • 用戶平均訂單金額為35.89,中位數為25.98,數據同樣呈現正偏態(右偏)特征,大部分訂單金額都集中在中小額范圍(14.4~43.7),存在極值點。

4.2消費概況分析

4.2.1時間維度分析消費情況(按月)

  按月份降采樣,統計信息。

df_dtindex = df.set_index(['order_dt'])
# 統計每月訂單商品量、每月訂單金額
month_grouped = df_dtindex.resample('m').sum()[['order_products','order_amount']]
# 統計每月消費人數
month_grouped['user_num'] = df_dtindex.resample('m')['user_id'].nunique()
# 統計每月訂單數
month_grouped['order_quantity'] = df_dtindex.resample('m')['user_id'].size()
month_grouped.head()
  每月訂單商品量、每月訂單金額、每月消費人數、每月訂單數趨勢圖。
month_grouped.plot(kind='line',subplots=True,figsize=(10,8))

從折線趨勢圖可以看出:

  • 1-3月份訂單商品量和訂單金額都較高,4月份急速下降后趨於平緩;
  • 1-3月份月均消費人數在7800~9600之間,4月份開始月均消費人數開始下降,在2000人次附近波動,反應用戶粘性不高,留存率低。

出現以上的原因,可能是該網站1-3月份做大推廣;或者明星新CD專輯集中發布,大量粉絲涌入網站購買。


4.2.2用戶維度分析消費情況

4.2.2.1個體消費分析

(1)用戶消費次數、消費金額、訂單商品量描述性統計

# 對用戶分組,並統計每個用戶的消費次數、消費金額、訂單商品量
user_grouped = df.groupby('user_id').agg({'user_id': 'count','order_amount': 'sum','order_products': 'sum'})
# 列字段重命名consumptions:消費次數
user_grouped.rename(columns={'user_id':'consumptions'},inplace=True)
# 描述性統計
user_grouped.describe()

用戶維度消費情況描述性統計分析:

  • 用戶平均消費次數為2.96次,中位數1,用戶消費次數數據右偏,反映時間段內大部分用戶只在該網站消費1次。
  • 訂單金額和訂單商品量的均值與75%分位值接近,反映訂單金額和訂單商品量主要由少部分用戶貢獻,要關注極值點影響。

(2)用戶消費次數、消費金額、訂單商品量分布直方圖

plt.figure(figsize=(20,4))
plt.subplot(1,3,1)
user_grouped['consumptions'].plot.hist(bins=15,title='consumptions')
plt.subplot(1,3,2)
user_grouped['order_amount'].plot.hist(bins=30,title='order_amount')
plt.subplot(1,3,3)
user_grouped['order_products'].plot.hist(bins=30,title='order_products')

  數據分布存在一定的極值干擾,需要對極值進行過濾處理再觀察數據分布特征。對於非正態分布數據集,根據切比雪夫定理:所有數據,至少有24/25(或96%)的數據位於均值±5個標准差范圍內。根據描述性統計,分別計算過濾條件:

  • consumptions:mean + 5std = 2.95+5*4.73=26.6,選擇27作為過濾閾值
  • order_amount:160.08+5*240.93=1310.73,選擇1310作為過濾閾值
  • order_products:7.12+5*16.98=92.02,選擇95作為過濾閾值
# 過濾極值,畫分布直方圖
plt.figure(figsize=(20,4))
plt.subplot(1,3,1)
user_grouped.query('consumptions<27')['consumptions'].plot.hist(bins=15,title='consumptions',xlim=(0,27))
plt.subplot(1,3,2)
user_grouped.query('order_amount<1312')['order_amount'].plot.hist(bins=30,title='order_amount')
plt.subplot(1,3,3)
user_grouped.query('order_products<95')['order_products'].plot.hist(bins=30,title='order_products')

image

  過濾掉極值之后,從用戶消費次數、消費金額、訂單商品量分布直方圖可以明顯看出:數據大多數集中在低值區,反映大部分用戶消費水平一般,貢獻價值一般。符合“長尾型”消費類數據分布特征。

(3)用戶人數累計消費金額占比

user_cumsum = user_grouped.sort_values(['order_amount']).apply(lambda x:x.cumsum()/x.sum())
user_cumsum.reset_index()['order_amount'].plot(xticks=(range(0,25000,2500)),title='用戶人數累計消費金額占比')
plt.xlabel('人數')
plt.ylabel('占比')

由上圖可知:
  50%的用戶(總共有23570名用戶)僅貢獻了15%的消費額度,70%的用戶僅貢獻了20%的消費額度,85%的用戶僅貢獻了40%的消費額度,而前3570(占比15%)多用戶就貢獻了60%的消費額度。可以重點關注一下這15%客戶更多相關數據,總結用戶特征,挖掘更多消費機會。

(4)用戶消費金額和訂單商品量之間相關性分析

user_grouped.query('order_amount<8000').plot.scatter(x='order_products',y='order_amount',title='用戶消費金額和商品購買量散點分布')

由散點分布圖可以看出:
  用戶的消費金額和訂單商品量幾乎成線性關系:購買的商品越多,消費金額越大。反映該網站的商品比較單一,或者單價比較穩定。在不影響主商品的前提下,可以考慮一些輔助商品鋪設。

4.2.2.2 用戶消費行為分析

(1)用戶第一次消費(首購)和最后一次消費時間分布(按月)

# 用戶初次消費時間分布:統計每個用戶消費的時間最小值,再計算相同日期的頻數
df.groupby('user_id')['order_dt'].min().value_counts().plot(title='用戶初次消費時間分布圖')

由初次消費分布圖可知:
  用戶初次購買時間集中在前三個月,其中2月9日到2月25日之間有波動。可能是該網站做了推廣或者明星集中發專輯,導致大量新用戶涌入網站購買CD。

# 用戶最后一次消費時間分布
df.groupby('user_id')['order_dt'].max().value_counts().plot(title='用戶最后一次消費時間分布圖')

由末次消費分布圖上圖可知:

  • 用戶最后一次消費也集中在前三個月,反映有大量用戶在三個月內就流失掉了;
  • 首購和最后一次消費都集中前三個月,可能是這部分新增用戶在前三月就流失掉了。可能是著名明星集中發專輯,新增用戶對其他CD專輯不感興趣。(這部分新用戶是某類明星的粉絲)
  • 隨時間遞增,最后一次消費人數也在緩慢遞增,用戶流失呈上升趨勢。可能是運營沒跟上,老用戶忠誠度也下降了。

從這里簡要看出,要提高網站的銷售金額,有兩個關鍵點:

  • 第一,分析新增用戶的購物喜好,推送同類或類似風格產品,確保在用戶流失之前留住用戶;
  • 第二,分配合理資源維護老用戶,給老用戶做合理的引導消費通知。

(2)新老用戶占比分析
  僅消費一次的用戶占比

# 僅消費一次的用戶占比=消費一次的用戶人數/總用戶數
user_grouped[user_grouped['consumptions']==1]['consumptions'].count()/user_grouped['consumptions'].count()
結果表明:有一半用戶只消費了一次。

注意:這里不能用第一次和最后一次消費日期相同,來判斷該用戶只消費了一次,因為有可能同一天該用戶消費了兩次。這樣算出來的指標應該叫‘僅消費一天的用戶占比’,不過兩者結果相差應該不是很大。
  每月新用戶占比


4.3用戶分層分析

4.3.1FRM模型對用戶分層

R(Recency,近度/最近一次消費)F(Frequency,消費頻次)M(Monetary,消費金額)

# 首先,數據透視表求出用戶的最近一次消費時間、消費次數,消費總金額
rfm = df.pivot_table(index='user_id',
                    values=['order_dt','order_products','order_amount'],
                    aggfunc={'order_dt': 'max',
                            'order_products': 'count',
                            'order_amount': 'sum'})
rfm.head()
# 其次,計算R值:最近一次消費(距今天數),數據比較久遠,這里選擇order_dt的最大值為‘今天’
# 時間格式相減,得到xxx days,需要除以一個單位'D'
rfm['R'] = (rfm.order_dt-rfm.order_dt.max())/np.timedelta64(1,'D')
rfm = rfm.rename(columns={'order_amount': 'M', 'order_products': 'F'})[['R','F','M']]
rfm.head()
# 根據判斷閾值(這里選擇每列均值作為閾值),添加客戶分層標簽字段
# 定義用戶分層函數
def rfm_level(data):
    # 和閾值(均值)做對比:高於閾值賦值為1,低於閾值賦值為0
    level = data.apply(lambda x:'1' if x>=0 else '0')
    # 構建分類字典
    d = {
        '111': '重要價值客戶',
        '011': '重要保持客戶',
        '101': '重要發展客戶',
        '001': '重要挽留客戶',
        '110': '一般價值客戶',
        '010': '一般保持用戶',
        '100': '一般發展客戶',
        '000': '一般挽留客戶'
    }
    # 拼接字符串
    label = level.R + level.F + level.M
    # 各行根據拼接字符串的結果(鍵),返回客戶類型(值)
    return d[label]
# 逐行傳入
rfm['label'] = (rfm-rfm.mean()).apply(rfm_level,axis=1)
rfm.head()
plt.figure(figsize=(12,6),dpi=80)
plt.subplot(1,2,1)
rfm.label.value_counts().plot.pie(autopct='%3.1f%%',title='RFM模型分層用戶人數占比圖')
plt.subplot(1,2,2)
rfm.groupby('label').M.sum().plot.pie(autopct='%3.1f%%',title='RFM模型分層用戶消費金額占比圖')

由上圖可知:

  • ‘一般挽留客戶’人數占比最多,達到57.7%。但貢獻的消費金額僅占比16.4%
  • ‘重要價值客戶’人數占比為19.6%,貢獻消費占比卻達到了63.7%

此次分析中,R、F、M三個維度的閾值是依據各自的平均值選定,實際業務要根據不同場景設定合適的閾值,切莫不要為了數據好看划分等級。在資源的有限情況下,盡量用小部分的用戶覆蓋大部分的額度。在運營策略上,應結合用戶分層的結果,針對不同的用戶分類用戶制定不同的運營策略:

  • 針對重要價值客戶,應保持好現狀;
  • 針對重要發展客戶,由於其價值高但是頻次低,應采取合適的策略來刺激其消費頻率;
  • 針對重要保持用戶,應采取合適的策略來將其召回,留住該用戶;
  • 針對重要挽留用戶,召回他並刺激其消費;
  • 針對一般價值用戶,可以通過發放大額面值優惠券等方式刺激消費力度;
  • 其余幾類用戶都要在考慮成本、資源的情況下采取相應的策略召回、刺激消費。

4.3.2根據消費狀態對用戶分層

  用戶分層的目的在於區分不同價值的用戶,對不同價值的用戶、不同階段的用戶采用精准、細化的運營方案。在實際業務場景中,常常把用戶分為新客,不活躍用戶,活躍用戶,回流用戶。
指標口徑說明:

  • 新用戶:時間窗口內,首次消費的用戶。
  • 不活躍用戶(流失用戶):時間窗口內,未消費的老客。
  • 活躍用戶:本時間窗口內有消費,上一個時間窗口也有消費的用戶。
  • 回流用戶:上一個時間窗口中沒有消費,而在當前時間窗口內有過消費的老客。
# 首先,以月份為統計窗口,制作透視表:得到每個月用戶的消費次數
# 缺失值NaN(該月份無消費記錄)用0填充
pivoted_counts = df.pivot_table(index='user_id',
                               columns='month',
                               values='order_dt',
                               aggfunc='count').fillna(0)
pivoted_counts.head()

image
  上面的NaN值用0填充:0不一定代表本月沒有消費,也有可能該用戶在當月沒有注冊,為了判斷該用戶在當月是否為新用戶,還需用自定義函數來判斷。

# 然后,自定義用戶分層函數
def user_status(data):
    '''
    unreg:未注冊用戶
    new:新用戶
    unactive:不活躍用戶(流失用戶)
    active:活躍用戶
    return:回流用戶
    '''
    status = []
    # 判斷第一月是否為新用戶
    if data[0] == 0:
        status.append('unreg')
    else:
        status.append('new')
    # 循環判斷每一個月的用戶狀態
    for i in range(17):
        # 若本月未消費
        if data[i+1]==0:
            # 上一個月也未消費,則本月為未注冊用戶
            if status[i] == 'unreg':
                status.append('unreg')
            else:
                status.append('unactive')
        # 本月消費
        else:
            # 上一月為未注冊用戶,則本月為新用戶
            if status[i] == 'unreg':
                status.append('new')
            # 上一月為不活躍用戶,則本月為回流用戶
            elif status[i] == 'unactive':
                status.append('return')
            # 上一月消費,本月也消費,則本月為活躍用戶
            else:
                status.append('active')
    return pd.Series(status,index=data.index) 


pivoted_user_status = pivoted_counts.apply(user_status, axis=1)
pivoted_user_status.head()

image

# 統計每個月不同狀態的用戶數
# 把未注冊unreg替換為NaN值,count不會計數
user_status_counts = pivoted_user_status.replace('unreg',np.NaN).apply(pd.value_counts)
user_status_counts

image

# 繪制不同類型用戶數量隨時間變化的曲線
user_status_counts.T.plot(figsize=(10, 4),title='不同類型用戶數量隨時間變化的曲線圖',grid=False)

由上圖可知:

  • 前三個月新用戶大量涌入,后期沒有新增;
  • 后期不活躍用戶數量非常多,基本每個月20000人以上;
  • 活躍用戶前幾月較多,后期逐漸下降;
  • 回流用戶基本保持1000左右。

在實際業務中,活躍用戶和回流用戶是真正意義上消費的用戶,是運營應該重點關注的高價值群體。

#計算每個月各類用戶占比
user_status_counts.T.apply(lambda x: x/x.sum(), axis=1).round(2)

4.4用戶生命周期分析

(1)用戶生命周期分布直方圖
  用戶生命周期定義:第一次消費至最后一次消費的時間差。
  延長用戶的生命周期,在用戶生命周期內獲取更多價值,是用戶運營的目標。

# LTV:生命周期總價值,即生命周期內消費的總金額
user_LTV = df.groupby(['user_id']).agg({'order_amount': 'sum'})
user_LTV['lifetime'] = (df.groupby(['user_id'])['order_dt'].max()-df.groupby(['user_id'])['order_dt'].min())/np.timedelta64(1, 'D')
user_LTV.head()
  前面分析中,有一半以上的用戶只消費1次,再進行用戶生命周期分析時,需要過濾掉這部分用戶再分析才具有實際意義。因為只消費1次的用戶生命周期為0,會把整體數據拉低,而且大部分一次性用戶創造的價值很小,對后期用戶運營沒有顯著的幫助。
# 用戶生命周期描述性統計:排除只消費1次的用戶
user_LTV_query = user_LTV[user_LTV['lifetime'] > 0]
user_LTV_query.describe()
#繪制分布直方圖
user_LTV_query['lifetime'].plot.hist(bins=40,figsize=(10, 6),title='CDNOW用戶生命周期分布直方圖')

由描述性統計和分布直方圖可以看出:

  • 消費2次以上用戶平均生命周期為276天;
  • 分布直方圖呈現雙峰趨勢,大體可以分為三段:
    1. [0-30]天,有部分用戶雖然消費超過兩次,但是無法持續,運營應該在30天內做出對策,延長用戶生命周期;
    2. [50-350]天,屬於普通型生命周期,這部分用戶是有消費欲望的,可以根據其特點推出針對性營銷策略;
    3. [>350]天,屬於網站的忠誠用戶,要用心維護,穩定銷量。

(2)用戶生命周期和用戶生命周期總價值相關性

plt.scatter(x=user_LTV_query['lifetime'],y=user_LTV_query['order_amount'])
user_LTV_query = user_LTV_query.query('order_amount < 4000')
plt.scatter(x=user_LTV_query['lifetime'],y=user_LTV_query['order_amount'])
plt.title('用戶生命周期和用戶生命周期總價值散點圖')

由上散點分布圖可知:
  用戶生命周期和用戶生命周期總價值無線性相關關系,但是有一部分生命周期超過350天的用戶,貢獻的消費金額明顯高於生命周期偏低的用戶。


4.5用戶留存分析

  用戶在某段時間內開始使用應用,經過一段時間后,仍然繼續使用該應用的用戶,被認作是留存用戶。這部分用戶占當時新增用戶的比例即是留存率,會按照每隔1單位時間(例日、周、月)來進行統計。留存用戶和留存率體現了用戶的質量和運營保留用戶的能力。

# 獲取用戶留存數據表

# 獲取用戶的激活日期reg_dt:這里指第一次消費日期
reg_dt = df.groupby('user_id')['order_dt'].min().reset_index().rename(columns={'order_dt': 'reg_dt'})

# 內連接
user_retention = pd.merge(left=df,right=reg_dt,how='inner',on='user_id')[['user_id','order_amount','reg_dt','order_dt']]
user_retention['第n天活躍'] = (user_retention['order_dt'] - user_retention['reg_dt']).apply(lambda x: x.days)
# # 對活躍天數進行分箱
bins = [0, 3, 7, 15, 30, 60, 90, 180, 365]
user_retention['活躍天數區間'] = pd.cut(user_retention['第n天活躍'], bins=bins)
user_retention.head(10)
# 數據透視,獲取用戶維度留存表
user_retention_pivoted = user_retention.pivot_table(index='user_id', columns='活躍天數區間',values='order_amount',aggfunc=sum,dropna=False)
user_retention_pivoted.head(10)
# 統計各個時間段用戶的消費比列:即首次消費后,有多少用戶比例在各個時間段再次消費
retention = user_retention_pivoted.count()/len(user_retention_pivoted)
retention
```python retention.plot.bar(title = '各個時間段用戶的再次消費人數占比') ```

由上柱狀圖可知:

  • 約有2.7%的用戶在第一次消費后,次日至3日內再次消費;
  • 3.6%的用戶在3至7日消費;
  • 約20%的用戶在第一次消費三個月至半年內有過消費記錄;
  • 約26%的用戶在半年后至一年內購買過。

可見,CD商品購買不屬於高頻行為。從運營角度看,在促活新用戶的時,更要注重培養老用戶的忠誠度。

# 統計用戶在第一次消費后,再后續時間段的平均消費
user_retention_pivoted.mean()

4.5用戶購買周期分析

# 計算用戶的相鄰兩個訂單的時間間隔,shift函數是對數據進行錯位,所有數據會往下平移一下
order_dt_diff = user_retention.groupby('user_id').apply(lambda x: x.第n天活躍 - x.第n天活躍.shift(1))
order_dt_diff.head(10)
```python # 用戶消費時間間隔描述性統計 order_dt_diff.describe() ```   用戶平均消費時間間隔為69天(極值影響)。在運營策略中,可以大概理解為,想要召回用戶消費,在60天左右的時間間隔可以做一些好的運營策略或推薦。 ```python # 用戶消費時間間隔分布直方圖 order_dt_diff.plot.hist(bins=50,title='用戶消費時間間隔分布直方圖') ```

由用戶消費時間間隔分布直方圖,可以看出:
  直方圖呈現長尾分布,大部分用戶的消費時間間隔很短,在用戶第一次消費后,將時間召回點設為消費后立即贈送優惠券,消費后15天詢問用戶CD怎么樣,消費后20天提醒優惠券到期,消費后60天短信推送,以實現用戶粘性增加,並不斷刺激用戶消費。


4.6復購率和回購率分析

指標說明:(這里時間窗口為月)

  • 復購率:在單位時間窗口內多次(2次及以上)消費的用戶在總消費用戶的占比。
  • 回購率:在某單位時間窗口內消費的用戶,在下一單位時間窗口仍消費的占比。
# 計算每個月用戶的消費次數
pivoted_counts.head()

image
(1)復購率分析

# 消費次數:>1表示消費兩次以上,>=1表示有消費
month_buy_rate = pd.DataFrame((pivoted_counts>1).sum()/(pivoted_counts>=1).sum(),columns=['復購率'])
month_buy_rate.head()
month_buy_rate.plot(figsize=(10,4),title='復購率月趨勢圖')

由復購率月趨勢圖可知:

  • 前三個月,由於新用戶涌入,導致復購率偏低,新客在短時間內只購買了一次
  • 隨着時間的推移,留下的都是少量忠誠用戶(2000人),復購率穩定在20%左右。

(2)回購率分析

# 定義函數判斷用戶是否回購:本月消費,下月也消費,則表示用戶回購
def func_purchase(data):
    status = []
    # 判斷前17個月
    for i in range(data.count()-1):
        # 本月有消費
        if data[i] > 0:
            # 下個月也消費,則為回購,即為1
            if data[i+1] > 0:
                status.append(1)
            # 下個月不消費,不回購,記為0
            else:
                status.append(0)
        # 本月無消費,賦予為NaN,count不統計
        else:
            status.append(np.nan)
    # 最后一個月補充為NaN,因為沒有下一月數據,無法判斷是否回購
    status.append(np.nan)
    return pd.Series(status,index=data.index)

# axis=1,按行逐用戶傳入
purchase = pivoted_counts.apply(func_purchase,axis=1)
purchase.head()

image

# 計算回購率
purchase_rate = pd.DataFrame(purchase.sum()/purchase.count(),columns=['回購率'])
purchase_rate.head()
purchase_rate.plot(figsize=(10,4),title='回購率月趨勢圖')

由回購率月趨勢圖可知:

  • 前三個月,新用戶涌入,新用戶回購率在16%左右,大部分新客戶只在當月購買了一次,新客戶質量不佳;
  • 后回購率穩定在30%左后,波動較大(10%之間)

五、案例總結


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM