『數據分析』使用 python 進行同期羣分析
作者:小小明 & 才哥
大家好,我是才哥。
五一以迅雷不及掩耳盜鈴兒響叮噹仁不讓之勢結束,這不馬上又週末了,我們又可以愉快的學習啦,本次節後第一篇來自小小明大哥主筆。
上次我們介紹過 [數據分析] 使用 Python 簡單玩玩 RFM 用戶價值模型,今天我們再介紹一個同期羣分析模型,並且會用一個實際案例進行詳細講解。
技術作者:小小明
理論修訂:才哥
原文鏈接:
https://blog.csdn.net/as604049322/article/details/116505941
目錄:
-
同期羣分析概念
-
數據讀取
-
分析方向
-
數據預處理
-
同期羣分析
-
從留存率角度進行同期羣分析
-
從人均付款金額角度進行同期羣分析
-
從人均購買次數角度進行同期羣分析
-
每月總體付費情況
-
將結果導出網頁或截圖
-
整體完整代碼
同期羣分析概念
同期羣 (Cohort) 的字面意思 (有共同特點或舉止類同的) 一羣人,比如不同性別,不同年齡。
在《精益數據分析》中的第2章 創業的記分牌
中介紹了三種分析方法(市場細分、同期羣分析以及 A/B 測試),其中關於同期羣分析的討論可以幫助我們快速瞭解它的應用場景。
同期羣分析:比較的是相似羣體隨時間的變化。
產品會隨着你的開發和測試而不斷迭代,這就導致在產品發佈第一週就加入的用戶和後來才加入的用戶有着不同的體驗。比如,每個用戶都會經歷一個生命週期:從免費試用,到付費使用,最後停止使用。同時,在這期間裏,你還在不停地對商業模式進行調整。於是,在產品上線第一個月就 “喫螃蟹” 的用戶勢必與四個月後才加入的用戶有着不同的上手體驗。這對用戶流失率會有什麼影響?我們用同期羣分析來尋找答案。
每一組用戶構成一個同期羣,參與整個試驗過程。通過比較不同的同期羣,你可以獲知:從總體上看,關鍵指標的表現是否越來越好了。
結合到用戶分析層面,比如不同月份獲取的用戶,不同渠道新增用戶,具備不同特徵的用戶(比如微信裏每天至少和 10 個以上朋友微信的用戶)。
同期羣分析 (Cohort Analysis
),將這些具有不同特徵的人羣進行對比分析,以發現他們在時間維度下的行爲差異。
因此,同期羣分析主要用於以下 2 點:
-
對比 不同 同期羣羣體同一體驗週期的數據指標,驗證產品迭代優化的效果
-
對比 同一 同期羣羣體不同體驗週期(生命週期)的數據指標,發現長線體驗的問題
我們在進行同期羣分析的時候,大致可以劃分爲 2 個流程:確定同期羣分組邏輯和確定同期羣分析的關鍵數據指標。
關於分組邏輯,需要遵循以下 2 個準則:
-
具有相似行爲特徵的羣體
-
具有相同時間週期的羣體
例如:
-
按獲客月份(按周甚至按天分組)
-
按獲客渠道
-
按照用戶完成的特定行爲,比如用戶訪問網站的次數或者購買次數來分類。
關於關鍵數據指標,需要是基於時間維度
下的比如留存、營收、自傳播係數等等。
下面是以留存率作爲指標的案例示例:
下面是某電商的運營數據,我們將以該數據演示用 python 進行同期羣分析。
同期羣分析案例詳解:
數據是某電商用戶付費日誌,日誌字段包含日期、付費金額和用戶 id,已脫敏處理。
數據讀取
import pandas as pd
df = pd.read_csv('日誌.csv', encoding="gb18030")
df.head()
分析方向
分組邏輯:
這裏只按照用戶的初始購買月份進行分組,如果日誌包含的分類字段更多(比如 渠道、性別或者年齡等),可以考慮更多種分組邏輯。
關鍵數據指標:
針對此份數據,至少有 3 個數據指標可以進行分析:
-
留存率
-
人均付款金額
-
人均購買次數
數據預處理
因爲我們是按照月份進行分組,所以需要先將日期重採樣爲月份:
df['購買月份'] = pd.to_datetime(df.日期).dt.to_period("M")
df.head()
計算每個用戶在每個月的付費總額:
order = df.groupby(["uid", "購買月份"], as_index=False).agg(
月付費總額=("付費金額","sum"),
月付費次數=("uid","count"),
)
order.head()
計算每個用戶的首單購買月份作爲同期羣分組,並將其對應到原始數據上:
order["首單月份"] = order.groupby("uid")['購買月份'].transform("min")
order.head()
計算每條購買記錄的時間與首單購買時間的月份差,並重置月份差標籤:
order["標籤"] = (order.購買月份-order.首單月份).apply(lambda x:"同期羣人數" if x.n==0 else f"+{x.n}月")
order.head()
兩個月份均爲時期類型,相減後得到 object 類型的列,而該列每個元素的類型是 pandas._libs.tslibs.offsets.MonthEnd
MonthEnd 類型具有屬性 n 能返回具體差值整數。
同期羣分析
前面我們說了至少有 3 個數據指標可以進行分析:
-
留存率
-
人均付款金額
-
人均購買次數
從留存率角度進行同期羣分析
通過數據透視表可以一次性計算所需的數據:
cohort_number = order.pivot_table(index="首單月份", columns="標籤",
values="uid", aggfunc="count",
fill_value=0).rename_axis(columns="留存率")
cohort_number
注意:rename_axis(columns=None) 用於刪除列標籤的軸名稱。rename_axis(columns="留存率") 則設置軸名稱爲留存率。
將 本月新增 列移動到第一列:
cohort_number.insert(0, "同期羣人數", cohort_number.pop("同期羣人數"))
cohort_number
具體過程是先通過 pop 刪除該列,然後插入到 0 位置,並命名爲指定的列名。
在本次的分析中,留存率的具體計算方式爲:+N 月留存率 =+N 月付款用戶數 / 首月付款用戶數
cohort_number.iloc[:, 1:] = cohort_number.iloc[:, 1:].divide(cohort_number.本月新增, axis=0)
cohort_number
以百分比形式顯示,並設置顏色:
out1 = (cohort_number.style
.format("{:.2%}", subset=cohort_number.columns[1:])
.bar(subset="同期羣人數", color="green")
.background_gradient("Reds", subset=cohort_number.columns[1:], high=1, axis=None)
)
out1
至此計算完畢。
從人均付款金額角度進行同期羣分析
要從從人均付款金額角度考慮,需要考慮同期羣基期這個整體。具體計算方式是先計算各月的付款總額,然後除以基期的總人數:
cohort_amount = order.pivot_table(index="首單月份", columns="標籤",
values="月付費總額", aggfunc="sum",
fill_value=0).rename_axis(columns="人均付款金額")
cohort_amount.insert(0, "首月人均付費", cohort_amount.pop("同期羣人數"))
cohort_amount.insert(0, "同期羣人數", cohort_number.同期羣人數)
cohort_amount.iloc[:, 1:] = cohort_amount.iloc[:, 1:].divide(cohort_amount.同期羣人數, axis=0)
out2 = (cohort_amount.style
.format("{:.2f}", subset=cohort_amount.columns[1:])
.background_gradient("Reds", subset=cohort_amount.columns[1:], axis=None)
.bar(subset="同期羣人數", color="green")
)
out2
可以看到,12 月份的同期羣首月新用戶人均消費爲703.43
元,然後逐月遞減,到 + 4 月後這些用戶人均消費僅11.41
元。而隨着版本的迭代發展,新增用戶的首月消費並沒有較大提升,且接下來的消費趨勢反而不如 12 月份。由此可見產品的發展受到了一定的瓶頸,需要思考增長營收的出路了。
一般來說, 通過同期羣分析可以比較好指導我們後續更深入細緻的數據分析,爲產品優化提供參考。
從人均購買次數角度進行同期羣分析
依然按照上面一樣的套路:
cohort_count = order.pivot_table(index="首單月份", columns="標籤",
values="月付費次數", aggfunc="sum",
fill_value=0).rename_axis(columns="人均購買次數")
cohort_count.insert(0, "首月人均頻次", cohort_count.pop("同期羣人數"))
cohort_count.insert(0, "同期羣人數", cohort_number.同期羣人數)
cohort_count.iloc[:, 1:] = cohort_count.iloc[:,
1:].divide(cohort_count.同期羣人數, axis=0)
out3 = (cohort_count.style
.format("{:.2f}", subset=cohort_count.columns[1:])
.background_gradient("Reds", subset=cohort_count.columns[1:], axis=None)
.bar(subset="同期羣人數", color="green")
)
out3
可以得到類似上述一致的結論。
每月總體付費情況
下面我們看看每個月的總體消費情況:
order.groupby("購買月份").agg(
付費人數=("uid", "count"),
人均付款金額=("月付費總額", "mean"),
月付費總額=("月付費總額", "sum")
)
可以看到總體付費人數和付費金額都在逐月下降。
將結果導出網頁或截圖
對於 Styler 類型,我們可以調用 render 方法轉化爲網頁源代碼,通過以下方式即可將其導入到一個網頁文件中:
with open("out.html", "w") as f:
f.write(out1.render())
f.write(out2.render())
f.write(out3.render())
如果你的電腦安裝了谷歌遊覽器,還可以安裝 dataframe_image,將這個表格導出爲圖片。
安裝:pip install dataframe_image
import dataframe_image as dfi
dfi.export(obj=out1, filename='留存率.jpg')
dfi.export(obj=out2, filename='人均付款金額.jpg')
dfi.export(obj=out3, filename='人均購買次數.jpg')
dfi.export 的參數:
-
obj : 被導出的 Datafream 對象
-
filename : 文件保存位置
-
fontsize : 字體大小
-
max_rows : 最大行數
-
max_cols : 最大列數
-
table_conversion : 使用谷歌遊覽器或原生'matplotlib', 只要寫非'chrome'的值就會使用原生'matplotlib'
-
chrome_path : 指定谷歌遊覽器位置
整體完整代碼
import pandas as pd
import dataframe_image as dfi
df = pd.read_csv('日誌.csv', encoding="gb18030")
df['購買月份'] = pd.to_datetime(df.日期).dt.to_period("M")
order = df.groupby(["uid", "購買月份"], as_index=False).agg(
月付費總額=("付費金額", "sum"),
月付費次數=("uid", "count"),
)
order["首單月份"] = order.groupby("uid")['購買月份'].transform("min")
order["標籤"] = (
order.購買月份-order.首單月份).apply(lambda x: "同期羣人數" if x.n == 0 else f"+{x.n}月")
cohort_number = order.pivot_table(index="首單月份", columns="標籤",
values="uid", aggfunc="count",
fill_value=0).rename_axis(columns="留存率")
cohort_number.insert(0, "同期羣人數", cohort_number.pop("同期羣人數"))
cohort_number.iloc[:, 1:] = cohort_number.iloc[:,1:].divide(cohort_number.同期羣人數, axis=0)
out1 = (cohort_number.style
.format("{:.2%}", subset=cohort_number.columns[1:])
.bar(subset="同期羣人數", color="green")
.background_gradient("Reds", subset=cohort_number.columns[1:], high=1, axis=None)
)
cohort_amount = order.pivot_table(index="首單月份", columns="標籤",
values="月付費總額", aggfunc="sum",
fill_value=0).rename_axis(columns="人均付款金額")
cohort_amount.insert(0, "首月人均付費", cohort_amount.pop("同期羣人數"))
cohort_amount.insert(0, "同期羣人數", cohort_number.同期羣人數)
cohort_amount.iloc[:, 1:] = cohort_amount.iloc[:, 1:].divide(cohort_amount.同期羣人數, axis=0)
out2 = (cohort_amount.style
.format("{:.2f}", subset=cohort_amount.columns[1:])
.background_gradient("Reds", subset=cohort_amount.columns[1:], axis=None)
.bar(subset="同期羣人數", color="green")
)
cohort_count = order.pivot_table(index="首單月份", columns="標籤",
values="月付費次數", aggfunc="sum",
fill_value=0).rename_axis(columns="人均購買次數")
cohort_count.insert(0, "首月人均頻次", cohort_count.pop("同期羣人數"))
cohort_count.insert(0, "同期羣人數", cohort_number.同期羣人數)
cohort_count.iloc[:, 1:] = cohort_count.iloc[:,
1:].divide(cohort_count.同期羣人數, axis=0)
out3 = (cohort_count.style
.format("{:.2f}", subset=cohort_count.columns[1:])
.background_gradient("Reds", subset=cohort_count.columns[1:], axis=None)
.bar(subset="同期羣人數", color="green")
)
outs = [out1, out2, out3]
with open("out.html", "w") as f:
for out in outs:
f.write(out.render())
display(out)
dfi.export(obj=out1, filename='留存率.jpg')
dfi.export(obj=out2, filename='人均付款金額.jpg')
dfi.export(obj=out3, filename='人均購買次數.jpg')
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/whx4lYpZVdeZTIQSvZkm2A