Python資料分析實戰專案| 使用者消費行為的分析報告
資料下載:
連結:
https://
pan。baidu。com/s/1XarNZ_
Qzxfe8xPq_QhCEmg
提取碼: zcph
資料來源
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)
輸出
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 天,遠高於總體,所以如何在使用者首次消費後引導其進行多次消費,可以有效提高使用者生命週期。 六、復購率和回購率分析 復購率 自然月內,購買多次的使用者佔比 回購率 曾經購買的使用者在某一時期內的再次購買的佔比 6。1 復購率 # 消費兩次及以上為 1 ,消費一次為 0 ,沒有消費為空 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)) plt。show() 復購率穩定在 20% 左右,前三個月因為有大量新使用者湧入,而這批使用者只購買了一次,所以導致復購率降低。 6。2 回購率 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) return pd。Series(status,df_purchase。columns) purchase_b = df_purchase。apply(purchase_back,axis = 1) purchase_b。head() 1 為回購使用者, 0 為上月沒購買當月購買過,NaN 為連續兩月都沒購買 plt。figure(figsize=(20,4)) plt。subplot (211) (purchase_b。sum() / purchase_b。count())。plot() plt。title(‘使用者回購率圖’) plt。ylabel(‘百分比%’) plt。subplot(212) plt。plot(purchase_b。sum(),label=‘每月消費人數’) plt。plot(purchase_b。count(),label=‘每月回購人數’) plt。xlabel(‘month’) plt。ylabel(‘人數’) plt。legend() plt。show() 由回購率圖可以看出,使用者回購率高於復購率,約在 30% 左右,波動性較強。新使用者回購率在 15 % 左右,與老使用者相差不大。 由人數分佈圖發現,回購人數在前三月之後趨於穩定,所以波動產生的原因可能由於營銷淡旺季導致,但之前復購使用者的消費行為與會回購使用者的行為大致相同,可能有一部分使用者重合,屬於優質使用者。 結合回購率和復購率分析,可以新客的整體忠誠度低於老客,老客的回購率較好,消費頻率稍低,這是 CDNow 網站的使用者消費特徵。 七、留存率分析 # 每一次消費距第一次消費的時間差值 user_purchase = df[[‘user_id’,‘order_products’,‘order_amount’,‘order_dt’]] user_purchase_retention = pd。merge(left = user_purchase, right = user_life[‘min’]。reset_index(), how = ‘inner’, on = ‘user_id’) user_purchase_retention[‘order_dt_diff’] = user_purchase_retention[‘order_dt’]-user_purchase_retention[‘min’] user_purchase_retention[‘dt_diff’] = user_purchase_retention。order_dt_diff。apply(lambda x: x/ np。timedelta64 (1,‘D’)) user_purchase_retention。head() # 時間差值分桶 bin = [0,30,60,90,120,150,180,365] user_purchase_retention[‘dt_diff_bin’] = pd。cut(user_purchase_retention。dt_diff, bins = bin) user_purchase_retention[‘order_dt_diff’] = user_purchase_retention[‘order_dt’] - user_purchase_retention[‘min’] pivoted_retention = user_purchase_retention。groupby([‘user_id’,‘dt_diff_bin’])。order_amount。sum()。unstack() pivoted_retention_trans = pivoted_retention。fillna(0)。applymap(lambda x: 1 if x >0 else 0) print(pivoted_retention_trans。head()) 這裡將時間差值分桶,代表使用者當前消費時間距第一次消費屬於哪個時間段。這裡dt_diff = 0 並沒有被劃分入 0~30 天,因為計算的是留存率,如果使用者僅消費了一次,留存率應該是 0。此外,如果使用者第一天內消費了多次,但是往後沒有消費,也算作留存率 0 pivoted_retention。mean() 統計各時間段使用者有消費的平均值,雖然後面時段的消費更高,但是其時間跨度也更大。從平均效果看,使用者第一次消費後的 0~3 天,可能消費更多。 但消費多是相對的,我們要看整體中有多少使用者在 0~3 天消費。 ((pivoted_retention_trans。sum()/pivoted_retention_trans。count())*100)。plot。bar() plt。ylabel(‘百分比 %’) plt。title(‘各時間段的使用者留存率’) plt。show() 第一個月的留存率達到 38% ,第二個月就下降到 35% 左右,之後幾個月趨於穩定,說明透過使用者在前三個月的使用中,逐漸開始喜愛本店鋪的業務或者轉換別家的店鋪,有 20% 左右的使用者在第一次購買後的三個月到半年之間有過購買, 27% 左右的使用者在半年後至一年內有過購買。 從運角度讀看,與拉新相比,更應該注重使用者忠誠度的培養,如果有活動,最好放在前三個月。 結合使用者生命週期,應該放長線釣大魚,使用者平均消費時間間隔為 68 天,所以召回使用者最好在 60 左右的時間間隔。 以上就是這個專案的全部過程,如果有任何疑問歡迎在評論區留言~我看到後會及時回覆的~覺得有幫助的話,可以給我點個贊喲~ 最後給大家安利一個Python的小課,對於想學Python,但是不知道怎麼入門的朋友十分適合,而且價格也只需要3.9元,課程涵蓋了Python絕大部分的基礎語法。 而且他有以下優勢,學習起來更輕鬆! 課程特色: 1、專為0 基礎設計,簡單易學; 2、互動式課堂,邊玩邊實操,生動有趣; 3、線上網頁程式碼實操,無需安裝軟體; 4、助教全程答疑輔導+督促,不怕學不會; 5、班級社群交流,收穫志同道合小夥伴 更多 Python 專案實戰: