數據來源 CDNow 網站的用戶購買明細,通過各個指標對用戶行為進行分析,可以更清楚了解用戶行為習慣,為進一步制定營銷策略提供依據。
具體指標包括:
- 戶消費趨勢分析
- 用戶個體消費分析
- 用戶消費行為分析
- 復購率和回購率分析
- 留存率分析
一、理解數據
本數據集共有 6 萬條左右數據,數據為 CDNow 網站 1997年1月至1998年6月的用戶行為數據,共計 4 列字段,分別是:
- user_id: 用戶ID
- order_dt: 購買日期
- order_products: 購買產品數
- order_amount: 購買金額
二、清洗數據
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
plt.style.use('ggplot')
1. 數據加載
columns = ['user_id', 'order_dt', 'order_products', 'order_amount']
# 將 order_dt 轉化為日期數據類型格式
dateparse = lambda dates: pd.datetime.strptime(dates,'%Y%m%d')
df = pd.read_table(r'C:\Users\86134\Desktop\CDNOW_master.txt',
names = columns,parse_dates=['order_dt'],
sep = '\s+',date_parser=dateparse
)
3.數據探索
print(df.info())
print('-'*30)
print(df.isnull().sum())
print('-'*30)
print(df.describe())
print('-'*30)
print(df.head())
print('-'*30)
print(df.tail())
print('-'*30)
輸出
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 69659 entries, 0 to 69658
Data columns (total 4 columns):
user_id 69659 non-null int64
order_dt 69659 non-null datetime64[ns]
order_products 69659 non-null int64
order_amount 69659 non-null float64
dtypes: datetime64[ns](1), float64(1), int64(2)
memory usage: 2.1 MB
None
------------------------------
user_id 0
order_dt 0
order_products 0
order_amount 0
dtype: int64
------------------------------
user_id order_products order_amount
count 69659.000000 69659.000000 69659.000000
mean 11470.854592 2.410040 35.893648
std 6819.904848 2.333924 36.281942
min 1.000000 1.000000 0.000000
25% 5506.000000 1.000000 14.490000
50% 11410.000000 2.000000 25.980000
75% 17273.000000 3.000000 43.700000
max 23570.000000 99.000000 1286.010000
------------------------------
user_id order_dt order_products order_amount
0 1 1997-01-01 1 11.77
1 2 1997-01-12 1 12.00
2 2 1997-01-12 5 77.00
3 3 1997-01-02 2 20.76
4 3 1997-03-30 2 20.76
------------------------------
user_id order_dt order_products order_amount
69654 23568 1997-04-05 4 83.74
69655 23568 1997-04-22 1 14.99
69656 23569 1997-03-25 2 25.74
69657 23570 1997-03-25 3 51.12
69658 23570 1997-03-26 2 42.96
------------------------------
通過數據探索我們發現 - 大部分訂單只購買了少量商品 (平均 2.4), 有一定極值干擾; - 用戶消費的金額比較穩定, 平均消費 35 元, 中位數在 35 元, 有一定極值干擾; - 數據呈右偏分布; - 沒有空缺數據,可以直接分析,但考慮到是按月分析,所以將日期進行解析(實際業務是否按月分析,取決於消費頻率) 。
# 解析日期
df['oreder_dt'] = pd.to_datetime(df.order_dt, format='%Y%m%d')
df['month'] = df.order_dt.values.astype('datetime64[M]')
三、用戶消費趨勢分析(按月)
- 每月的產品購買數量
- 每月的消費總金額
- 每月的消費次數
- 每月的消費人數
1.每月的產品購買數量
grouped_momnth = df.groupby('month')
plt.figure(1, figsize=(10, 4))
plt.title('每月用戶購買張數')
plt.ylabel('CD碟數(張)')
grouped_momnth.order_products.sum().plot()
plt.show()

從圖中可以看到,銷量在前幾個月異常高漲,並在3月達到最高峰,后續銷量較為穩定,且有輕微下降趨勢。
2.每月的消費金額
plt.figure(1, figsize=(10, 4))
plt.title('每月銷售額')
plt.ylabel('銷售額')
grouped_momnth.order_amount.sum().plot()
plt.show()

由圖可以看到,消費金額也一樣呈現早期銷售多,后期平穩下降趨勢,而且前三月數據都呈現出異常狀態,為什么呈現這樣的原因呢?我們可以假設前三個月有促銷活動,或者用戶本身出了問題,早期用戶有異常值。但這里只有消費數據,因此不能做出判斷。
3.每月消費次數
plt.figure(1, figsize=(10, 4))
plt.title('每月訂單數')
plt.ylabel('訂單數')
grouped_momnth.user_id.count().plot()
plt.show()

前三個月訂單數在 10000 筆左右,后續月份的平均消費訂單數則在 2500 筆左右。
4.每月消費人數
plt.figure(1, figsize=(10, 4))
plt.title('每月消費人數')
plt.ylabel('人數')
grouped_momnth.user_id.apply(lambda x: len(x.drop_duplicates())).plot()
plt.show()

每月消費人數低於每月消費次數,但差異不大。
前三個月每月消費人數在 8000~10000 之間,后續月份,平均消費人數在 2000 人不到。
四、用戶個體消費分析
- 用戶消費金額,消費次數的描述統計
- 用戶消費金額和購買數量散點圖
- 用戶消費分布圖
- 用戶累計消費金額的占比(百分之多少的用戶占了百分之多少的消費)
1.用戶消費金額,消費次數的描述統計
grouped_user = df.groupby('user_id')

從用戶角度
- 用戶數量:23570,每位用戶平均購買 7 張 CD,但是中位數值只有3,且有狂熱用戶購買了1033 張。平均值大於中位數,是右偏分布,說明小部分用戶購買了大量的 CD。
從消費金額角度
- 用戶平均消費 106 元,中位值只有 43,且有土豪用戶消費 13990,結合分位數和最大值看,平均數僅和 75 分位接近,肯定存在小部分的高頻消費用戶。
plt.figure(figsize=(12,4))
plt.subplot(121)
plt.scatter(x = 'order_amount', y = 'order_products',data=df)
plt.xlabel('每筆訂單消費金額')
plt.ylabel('每筆訂單購買數量')
plt.subplot(122)
plt.scatter(x = 'order_amount',y = 'order_products',
data = grouped_user.sum())
plt.xlabel('每位用戶消費金額')
plt.ylabel('每位用戶購買數量')
plt.show()

從每筆訂單的散點圖觀察
- 訂單消費金額和訂單商品有規律性,每個商品均價 10元,且訂單極值較少,超出 1000 的不多,所以不是造成異常波動的原因。
從每位用戶的消費散點圖觀察
- 用戶比較健康,且線性關系比訂單更強,由於這是 CD 網站的銷售數據,商品較為單一,金額和商品的關系因此呈線性關系,離群點不多。
3.用戶消費分布圖
為了更好的觀察消費能力極強的用戶,因為數量不多,所以用直方圖
plt.figure(figsize=(12, 4))
plt.subplot(121)
ax = grouped_user.order_amount.sum().hist(bins=50)
ax.set_xlabel('金額(美元)')
ax.set_ylabel('用戶人數')
ax.set_xlim(0, 2000)
ax.set_title('用戶消費金額分布圖')
plt.subplot(122)
ax1 = grouped_user.order_products.sum().hist(bins = 50)
ax1.set_xlabel('CD 數(張)')
ax1.set_ylabel('用戶人數')
ax1.set_xlim(0, 150)
ax1.set_title('購買 CD 數分布圖')
plt.show()

從直方圖可知,用戶消費金額,絕大部分呈現集中趨勢,大部分購買CD數20張內,高消費用戶在圖上幾乎看不到,這是符合消費行為的行業規律。
4.用戶累計消費金額的占比
user_cumsum = grouped_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum())
user_cumsum
user_cumsum = grouped_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum() / x.sum())
user_cumsum.reset_index().order_amount.tail()
# 輸出
23565 0.985405
23566 0.988025
23567 0.990814
23568 0.994404
23569 1.000000
Name: order_amount, dtype: float64
user_cumsum = grouped_user.sum().sort_values('order_amount').apply(lambda x:(x.cumsum() / x.sum())*100)
user_cumsum.reset_index().order_amount.plot()
plt.title('用戶累計消費金額占比')
plt.xlabel('人數')
plt.ylabel('百分比 %')
plt.show()

通過分析累計銷售額占比,從圖中不難看出用戶消費行為基本符合二八定律,80% 的用戶貢獻了 25% 的消費金額,而 60% 的消費由前 5000 名用戶貢獻。所以只要維護了這5000 名用戶,就能完成 60% 的KPI。
五、用戶消費行為
- 用戶第一次消費(首購)時間
- 用戶最后一次消費時間
- 用戶分層
- RFM (RFM模型是衡量客戶價值和客戶創利能力的重要工具和手段)
- 新、老、活躍、回流、流失
- 用戶購買周期(按訂單)
- 用戶消費周期描述
- 用戶消費周期分布
- 用戶生命周期(按第一次&最后一次消費)
- 用戶生命周期描述
- 用戶生命周期分布
1.用戶首購時間
grouped_user.min().month.value_counts()
# 輸出
1997-02-01 8476
1997-01-01 7846
1997-03-01 7248
Name: month, dtype: int64
grouped_user.min().order_dt.value_counts().plot() # 首購
plt.show()

2.用戶最后一次購買時間
grouped_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
grouped_user.max().order_dt.value_counts().plot() # 最后一次消費
plt.show()

通過以上兩個維度觀察,可以看出
- 用戶第一次購買分布,集中在前三個月,其中,在 2 月 11 日至 2 月 25 日有一次劇烈波動。
- 用戶最后一次購買的分布比第一次分布廣,但是大部分最后一次購買也集中在前三個月,說明忠誠用戶較少,隨着時間的遞增,最后一次購買數在遞增,消費呈現流失上升的趨勢,所以可以推測,這份數據選擇的是前三個月消費的用戶在后面18個月的跟蹤記錄數據,前三個月消費金額和購買數量的異常趨勢獲得解釋。
3.用戶分層
3.1 構建RFM 模型
- Recency Frequency Monetary
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['R'] = -(rfm.order_dt - rfm.order_dt.max()) / np.timedelta64(1, 'D')
rfm.rename(columns = {'order_products': 'F', 'order_amount':'M'},
inplace=True)
rfm.head()

def rfm_func(x):
level = x.apply(lambda x:'1' if x>=1 else '0')
label = level.R + level.F + level.M
d = {
'111':'重要價值客戶',
'011':'重要保持客戶',
'101':'重要挽留客戶',
'001':'重要發展客戶',
'110':'一般價值客戶',
'010':'一般保持客戶',
'100':'一般挽留客戶',
'000':'一般發展客戶'
}
result = d[label]
return result
rfm['label'] = rfm[['R', 'F', 'M']].apply(lambda x:x-x.mean()).apply(rfm_func,axis=1)
rfm.head()

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

for label,gropued in rfm.groupby('label'):
x= gropued['F']
y = gropued['R']
plt.scatter(x,y,label = label) # 利用循環繪制函數
plt.legend(loc='best') # 圖例位置
plt.xlabel('Frequency')
plt.ylabel('Recency')
plt.show()

從 RFM 分層可知,大部分用戶為重要保持客戶,但這是因為極值存在,所以 FRM 的划分應按照業務為准划分
- 盡量用小部分的用戶覆蓋大部分的額度
- 不要為了數據好看而划分等級
3.2 按新、活躍、回流、流失分層用戶
# 通過每月是否消費來划分用戶
pivoted_counts = df.pivot_table(index = 'user_id',
columns = 'month',
values = 'order_dt',
aggfunc = 'count').fillna(0)
pivoted_counts.columns = df.month.sort_values().astype('str').unique()
pivoted_counts.head()

df_purchase = pivoted_counts.applymap(lambda x: 1 if x> 0 else 0)
df_purchase.tail()

def active_status(data):
status = []
for i in range(18):
if data[i] == 0:
if len(status) > 0:
if status[i-1] == 'unreg':
status.append('unreg')
else:
status.append('unactive')
else:
status.append('unreg')
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 pd.Series(status,df_purchase.columns)
若本月沒有消費
- 若之前未注冊,則依舊未注冊
- 若之前有消費,則為流失/為活躍
- 其他情況,未注冊
若本月消費
- 若是第一次消費,則為新用戶
- 如果之前有過消費,上個月為不活躍,則為回流
- 如果上個月未注冊,則為新用戶
- 除此之外,為活躍
purchase_states = df_purchase.apply(active_status,axis = 1)
purchase_states.tail()

purchase_states_ct = purchase_states.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x))
purchase_states_ct

unreg 狀態排除掉,是未來才成為新用戶,作為不同分呈用戶每月統計量。
# 轉置后方便觀察
purchase_states_ct.fillna(0).T

# 繪制面積圖
purchase_states_ct.fillna(0).T.plot.area(figsize = (12, 6))
plt.show()

由面積圖,藍色和灰色區域占大面積,可以不看,因為這只是某段時間消費過的用戶的后續行為。其次紅色代表的活躍用戶非常穩定,是屬於核心用戶,以及紫色的回流用戶,這兩個分層相加,就是消費用戶人數占比(后期沒用新客)
3.3回流用戶占比
plt.figure(figsize=(20, 4))
rate = purchase_states_ct.fillna(0).T.apply(lambda x: x/x.sum())
plt.plot(rate['return'],label='return')
plt.plot(rate['active'],label='active')
plt.legend()
plt.show()

- 回流用戶比:某個時間段內回流用戶在總用戶中的占比
- 由圖可知,用戶每月回流用戶比占 5% ~ 8% 之間,有下降趨勢,說明客戶有流失傾向。
- 回流用戶率:上月有多少不活躍用戶在本月消費
- 由於這份數據的不活躍用戶量基本不變,所以這里的回流率,也近似等於回流比
- 活躍用戶比:某個時間段內活躍用戶在總用戶中的占比。
- 活躍用戶的占比在 3% ~ 5%間,下降趨勢更顯著,活躍用戶可以看作連續消費用戶,忠誠度高於回流用農戶。
結合活躍用戶和回流用戶看,在后期的消費用戶中,60%是回流用戶,40%是活躍用戶,整體用戶質量相對不錯。也進一步說明前面用戶消費行為分析中的二八定律,反應了在消費領域中,狠抓高質量用戶是不變的道理。
4.用戶購買周期
# 訂單時間間隔
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)
plt.show()

- 訂單周期呈指數分布
- 用戶的平均購買周期是 68 天
- 絕大部分用戶的購買周期低於 100 天
- 用戶生命周期圖是典型的長尾圖,大部分用戶的消費間隔確實比較短。不妨將時間召回點設為消費后立即贈送優惠券,消費后10天詢問用戶CD怎么樣,消費后30天提醒優惠券到期,消費后60天短信推送。
5.用戶生命周期
# 最后一次購買的時間減去首購時間
user_life = grouped_user.order_dt.agg(['min', 'max'])
user_life.head()

# 只消費過一次的用戶占比
(user_life['min'] == user_life['max']).value_counts().plot.pie()
plt.show()
(user_life['max'] - user_life['min']).describe()

通過描述可知,用戶平均生命周期 134 天,比預想高,但是平均數不靠譜,中位數 0 天,大部分用戶第一次消費也是最后一次,這批屬於低質量用戶,而最大的是 544 天,幾乎是數據集的總天數,這用戶屬於核心用戶。
因為數據中的用戶都是前三個月第一次消費,所以這里的生命周期代表的是1月~3月用戶的生命周期。因為用戶會持續消費,這段時間過后還會繼續消費,用戶的平均生命周期會增長。
plt.figure(figsize=(20, 4))
plt.subplot(121)
((user_life['max'] - user_life['min']) / np.timedelta64(1, 'D')).hist(bins = 15)
plt.title('二次消費以上用戶的生命周期直方圖')
plt.xlabel('天數')
plt.ylabel('人數')
# 過濾生命周期為0 的
plt.subplot(122)
u_l = ((user_life['max'] - user_life['min']).reset_index()[0] / np.timedelta64(1, 'D'))
u_l[u_l > 0].hist(bins = 40)
plt.title('二次消費以上用戶的生命周期直方圖')
plt.xlabel('天數')
plt.ylabel('人數')
plt.show()

通過兩圖對比看出,過濾掉周期為 0 的用戶后,圖像呈雙峰結構,雖然還是有不少用戶生命周期趨於 0 天,但是相比第一幅圖,靠譜多了。部分低質用戶,雖然消費兩次,但還是不能持續消費,要想提高用戶轉化率,應該用戶首次消費 30 天內盡量引導,少部分用戶集中在 50 - 300 天,屬於普通用戶,忠誠度一般。集中在 400 天以后的,是高質量用戶了,后期人數還在增加,這批用戶已經屬於核心用戶了,忠誠度極高,盡量維護這批用戶的利益。
# 消費兩次以上用戶平均生命周期
u_l[u_l > 0].mean()
# 輸出
276.0448072247308
消費兩次以上的用戶平均生命周期是 276 天,遠高於總體,所以如何在用戶首次消費后引導其進行多次消費,可以有效提高用戶生命周期。
