使用Python進(jìn)行同期群分析(Cohort?Analysis)
同期群分析
同期群分析概念
同期群(Cohort)的字面意思(有共同特點(diǎn)或舉止類同的)一群人,比如不同性別,不同年齡。
在《精益數(shù)據(jù)分析》中的第2章 創(chuàng)業(yè)的記分牌 中介紹了三種分析方法(市場(chǎng)細(xì)分、同期群分析以及A/B測(cè)試),其中關(guān)于同期群分析的討論可以幫助我們快速了解它的應(yīng)用場(chǎng)景。
同期群分析:比較的是相似群體隨時(shí)間的變化。
產(chǎn)品會(huì)隨著你的開發(fā)和測(cè)試而不斷迭代,這就導(dǎo)致在產(chǎn)品發(fā)布第一周就加入的用戶和后來才加入的用戶有著不同的體驗(yàn)。比如,每個(gè)用戶都會(huì)經(jīng)歷一個(gè)生命周期:從免費(fèi)試用,到付費(fèi)使用,最后停止使用。同時(shí),在這期間里,你還在不停地對(duì)商業(yè)模式進(jìn)行調(diào)整。于是,在產(chǎn)品上線第一個(gè)月就“吃螃蟹”的用戶勢(shì)必與四個(gè)月后才加入的用戶有著不同的上手體驗(yàn)。這對(duì)用戶流失率會(huì)有什么影響?我們用同期群分析來尋找答案。
每一組用戶構(gòu)成一個(gè)同期群,參與整個(gè)試驗(yàn)過程。通過比較不同的同期群,你可以獲知:從總體上看,關(guān)鍵指標(biāo)的表現(xiàn)是否越來越好了。
結(jié)合到用戶分析層面,比如不同月份獲取的用戶,不同渠道新增用戶,具備不同特征的用戶(比如微信里每天至少和10個(gè)以上朋友微信的用戶)。
同期群分析(Cohort Analysis),將這些具有不同特征的人群進(jìn)行對(duì)比分析,以發(fā)現(xiàn)他們?cè)跁r(shí)間維度下的行為差異。
因此,同期群分析主要用于以下2點(diǎn):
- 對(duì)比 不同 同期群群體同一體驗(yàn)周期的數(shù)據(jù)指標(biāo),驗(yàn)證產(chǎn)品迭代優(yōu)化的效果
- 對(duì)比 同一 同期群群體不同體驗(yàn)周期(生命周期)的數(shù)據(jù)指標(biāo),發(fā)現(xiàn)長(zhǎng)線體驗(yàn)的問題
我們?cè)谶M(jìn)行同期群分析的時(shí)候,大致可以劃分為2個(gè)流程:確定同期群分組邏輯和確定同期群分析的關(guān)鍵數(shù)據(jù)指標(biāo)。
- 具有相似行為特征的群體
- 具有相同時(shí)間周期的群體
例如:
- 按獲客月份(按周甚至按天分組)
- 按獲客渠道
- 按照用戶完成的特定行為,比如用戶訪問網(wǎng)站的次數(shù)或者購(gòu)買次數(shù)來分類。
關(guān)于關(guān)鍵數(shù)據(jù)指標(biāo),需要是基于時(shí)間維度下的比如留存、營(yíng)收、自傳播系數(shù)等等。
下面是以留存率作為指標(biāo)的案例示例:

下面是某電商的運(yùn)營(yíng)數(shù)據(jù),我們將以該數(shù)據(jù)演示用python進(jìn)行同期群分析。
同期群分析案例詳解:
數(shù)據(jù)是某電商用戶付費(fèi)日志,日志字段包含日期、付費(fèi)金額和用戶id,已脫敏處理。
數(shù)據(jù)讀取
import pandas as pd
df = pd.read_csv('日志.csv', encoding="gb18030")
df.head()
分析方向
分組邏輯:
這里只按照用戶的初始購(gòu)買月份進(jìn)行分組,如果日志包含的分類字段更多(比如 渠道、性別或者年齡等),可以考慮更多種分組邏輯。
關(guān)鍵數(shù)據(jù)指標(biāo):
針對(duì)此份數(shù)據(jù),至少有3個(gè)數(shù)據(jù)指標(biāo)可以進(jìn)行分析:
- 留存率
- 人均付款金額
- 人均購(gòu)買次數(shù)
數(shù)據(jù)預(yù)處理
因?yàn)槲覀兪前凑赵路葸M(jìn)行分組,所以需要先將日期重采樣為月份:
df['購(gòu)買月份'] = pd.to_datetime(df.日期).dt.to_period("M")
df.head()

計(jì)算每個(gè)用戶在每個(gè)月的付費(fèi)總額:
order = df.groupby(["uid", "購(gòu)買月份"], as_index=False).agg(
月付費(fèi)總額=("付費(fèi)金額","sum"),
月付費(fèi)次數(shù)=("uid","count"),
)
order.head()

計(jì)算每個(gè)用戶的首單購(gòu)買月份作為同期群分組,并將其對(duì)應(yīng)到原始數(shù)據(jù)上:
order["首單月份"] = order.groupby("uid")['購(gòu)買月份'].transform("min")
order.head()

計(jì)算每條購(gòu)買記錄的時(shí)間與首單購(gòu)買時(shí)間的月份差,并重置月份差標(biāo)簽:
order["標(biāo)簽"] = (order.購(gòu)買月份-order.首單月份).apply(lambda x:"同期群人數(shù)" if x.n==0 else f"+{x.n}月")
order.head()

兩個(gè)月份均為時(shí)期類型,相減后得到object類型的列,而該列每個(gè)元素的類型是pandas._libs.tslibs.offsets.MonthEnd
MonthEnd類型具有屬性n能返回具體差值整數(shù)。
同期群分析
前面我們說了至少有3個(gè)數(shù)據(jù)指標(biāo)可以進(jìn)行分析:
- 留存率
- 人均付款金額
- 人均購(gòu)買次數(shù)
從留存率角度進(jìn)行同期群分析
通過數(shù)據(jù)透視表可以一次性計(jì)算所需的數(shù)據(jù):
cohort_number = order.pivot_table(index="首單月份", columns="標(biāo)簽",
values="uid", aggfunc="count",
fill_value=0).rename_axis(columns="留存率")
cohort_number

注意:rename_axis(columns=None)用于刪除列標(biāo)簽的軸名稱。rename_axis(columns=“留存率”)則設(shè)置軸名稱為留存率。
將 本月新增 列移動(dòng)到第一列:
cohort_number.insert(0, "同期群人數(shù)", cohort_number.pop("同期群人數(shù)"))
cohort_number
具體過程是先通過pop刪除該列,然后插入到0位置,并命名為指定的列名。
在本次的分析中,留存率的具體計(jì)算方式為:+N月留存率=+N月付款用戶數(shù)/首月付款用戶數(shù)
cohort_number.iloc[:, 1:] = cohort_number.iloc[:, 1:].divide(cohort_number.本月新增, axis=0) cohort_number

以百分比形式顯示,并設(shè)置顏色:
out1 = (cohort_number.style
.format("{:.2%}", subset=cohort_number.columns[1:])
.bar(subset="同期群人數(shù)", color="green")
.background_gradient("Reds", subset=cohort_number.columns[1:], high=1, axis=None)
)
out1
至此計(jì)算完畢。
從人均付款金額角度進(jìn)行同期群分析
要從從人均付款金額角度考慮,需要考慮同期群基期這個(gè)整體。具體計(jì)算方式是先計(jì)算各月的付款總額,然后除以基期的總?cè)藬?shù):
cohort_amount = order.pivot_table(index="首單月份", columns="標(biāo)簽",
values="月付費(fèi)總額", aggfunc="sum",
fill_value=0).rename_axis(columns="人均付款金額")
cohort_amount.insert(0, "首月人均付費(fèi)", cohort_amount.pop("同期群人數(shù)"))
cohort_amount.insert(0, "同期群人數(shù)", cohort_number.同期群人數(shù))
cohort_amount.iloc[:, 1:] = cohort_amount.iloc[:, 1:].divide(cohort_amount.同期群人數(shù), 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="同期群人數(shù)", color="green")
)
out2

可以看到,12月份的同期群首月新用戶人均消費(fèi)為703.43元,然后逐月遞減,到+4月后這些用戶人均消費(fèi)僅11.41元。而隨著版本的迭代發(fā)展,新增用戶的首月消費(fèi)并沒有較大提升,且接下來的消費(fèi)趨勢(shì)反而不如12月份。由此可見產(chǎn)品的發(fā)展受到了一定的瓶頸,需要思考增長(zhǎng)營(yíng)收的出路了。
一般來說, 通過同期群分析可以比較好指導(dǎo)我們后續(xù)更深入細(xì)致的數(shù)據(jù)分析,為產(chǎn)品優(yōu)化提供參考。
從人均購(gòu)買次數(shù)角度進(jìn)行同期群分析
依然按照上面一樣的套路:
cohort_count = order.pivot_table(index="首單月份", columns="標(biāo)簽",
values="月付費(fèi)次數(shù)", aggfunc="sum",
fill_value=0).rename_axis(columns="人均購(gòu)買次數(shù)")
cohort_count.insert(0, "首月人均頻次", cohort_count.pop("同期群人數(shù)"))
cohort_count.insert(0, "同期群人數(shù)", cohort_number.同期群人數(shù))
cohort_count.iloc[:, 1:] = cohort_count.iloc[:,
1:].divide(cohort_count.同期群人數(shù), 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="同期群人數(shù)", color="green")
)
out3

可以得到類似上述一致的結(jié)論。
每月總體付費(fèi)情況
下面我們看看每個(gè)月的總體消費(fèi)情況:
order.groupby("購(gòu)買月份").agg(
付費(fèi)人數(shù)=("uid", "count"),
人均付款金額=("月付費(fèi)總額", "mean"),
月付費(fèi)總額=("月付費(fèi)總額", "sum")
)

可以看到總體付費(fèi)人數(shù)和付費(fèi)金額都在逐月下降。
將結(jié)果導(dǎo)出網(wǎng)頁或截圖
對(duì)于Styler類型,我們可以調(diào)用render方法轉(zhuǎn)化為網(wǎng)頁源代碼,通過以下方式即可將其導(dǎo)入到一個(gè)網(wǎng)頁文件中:
with open("out.html", "w") as f:
f.write(out1.render())
f.write(out2.render())
f.write(out3.render())
如果你的電腦安裝了谷歌游覽器,還可以安裝dataframe_image,將這個(gè)表格導(dǎo)出為圖片。
安裝: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='人均購(gòu)買次數(shù).jpg')
dfi.export的參數(shù):
- obj : 被導(dǎo)出的Datafream對(duì)象
- filename : 文件保存位置
- fontsize : 字體大小
- max_rows : 最大行數(shù)
- max_cols : 最大列數(shù)
- table_conversion : 使用谷歌游覽器或原生’matplotlib’, 只要寫非’chrome’的值就會(huì)使用原生’matplotlib’
- chrome_path : 指定谷歌游覽器位置
整體完整代碼
import pandas as pd
import dataframe_image as dfi
df = pd.read_csv('日志.csv', encoding="gb18030")
df['購(gòu)買月份'] = pd.to_datetime(df.日期).dt.to_period("M")
order = df.groupby(["uid", "購(gòu)買月份"], as_index=False).agg(
月付費(fèi)總額=("付費(fèi)金額", "sum"),
月付費(fèi)次數(shù)=("uid", "count"),
)
order["首單月份"] = order.groupby("uid")['購(gòu)買月份'].transform("min")
order["標(biāo)簽"] = (
order.購(gòu)買月份-order.首單月份).apply(lambda x: "同期群人數(shù)" if x.n == 0 else f"+{x.n}月")
cohort_number = order.pivot_table(index="首單月份", columns="標(biāo)簽",
values="uid", aggfunc="count",
fill_value=0).rename_axis(columns="留存率")
cohort_number.insert(0, "同期群人數(shù)", cohort_number.pop("同期群人數(shù)"))
cohort_number.iloc[:, 1:] = cohort_number.iloc[:,1:].divide(cohort_number.同期群人數(shù), axis=0)
out1 = (cohort_number.style
.format("{:.2%}", subset=cohort_number.columns[1:])
.bar(subset="同期群人數(shù)", color="green")
.background_gradient("Reds", subset=cohort_number.columns[1:], high=1, axis=None)
)
cohort_amount = order.pivot_table(index="首單月份", columns="標(biāo)簽",
values="月付費(fèi)總額", aggfunc="sum",
fill_value=0).rename_axis(columns="人均付款金額")
cohort_amount.insert(0, "首月人均付費(fèi)", cohort_amount.pop("同期群人數(shù)"))
cohort_amount.insert(0, "同期群人數(shù)", cohort_number.同期群人數(shù))
cohort_amount.iloc[:, 1:] = cohort_amount.iloc[:, 1:].divide(cohort_amount.同期群人數(shù), 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="同期群人數(shù)", color="green")
)
cohort_count = order.pivot_table(index="首單月份", columns="標(biāo)簽",
values="月付費(fèi)次數(shù)", aggfunc="sum",
fill_value=0).rename_axis(columns="人均購(gòu)買次數(shù)")
cohort_count.insert(0, "首月人均頻次", cohort_count.pop("同期群人數(shù)"))
cohort_count.insert(0, "同期群人數(shù)", cohort_number.同期群人數(shù))
cohort_count.iloc[:, 1:] = cohort_count.iloc[:,
1:].divide(cohort_count.同期群人數(shù), 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="同期群人數(shù)", 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='人均購(gòu)買次數(shù).jpg')

到此這篇關(guān)于使用Python進(jìn)行同期群分析(Cohort Analysis)的文章就介紹到這了,更多相關(guān)Python同期群分析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python3實(shí)現(xiàn)抓取javascript動(dòng)態(tài)生成的html網(wǎng)頁功能示例
這篇文章主要介紹了Python3實(shí)現(xiàn)抓取javascript動(dòng)態(tài)生成的html網(wǎng)頁功能,結(jié)合實(shí)例形式分析了Python3使用selenium庫針對(duì)javascript動(dòng)態(tài)生成的HTML網(wǎng)頁元素進(jìn)行抓取的相關(guān)操作技巧,需要的朋友可以參考下2017-08-08
基于Python實(shí)現(xiàn)俄羅斯方塊躲閃小游戲
這篇文章主要為大家詳細(xì)介紹了如何基于Python實(shí)現(xiàn)有趣的俄羅斯方塊躲閃小游戲,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-04-04
Jupyter Lab設(shè)置切換虛擬環(huán)境的實(shí)現(xiàn)步驟
本文主要介紹了Jupyter Lab設(shè)置切換虛擬環(huán)境的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
django admin.py 外鍵,反向查詢的實(shí)例
今天小編就為大家分享一篇django admin.py 外鍵,反向查詢的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-07-07
在Python中使用CasperJS獲取JS渲染生成的HTML內(nèi)容的教程
這篇文章主要介紹了在Python中使用CasperJS獲取JS渲染生成的HTML內(nèi)容的教程,需要先用JavaScript創(chuàng)建一個(gè)接口文件,需要的朋友可以參考下2015-04-04
將Python中的數(shù)據(jù)存儲(chǔ)到系統(tǒng)本地的簡(jiǎn)單方法
這篇文章主要介紹了將Python中的數(shù)據(jù)存儲(chǔ)到系統(tǒng)本地的簡(jiǎn)單方法,主要使用了pickle模塊,需要的朋友可以參考下2015-04-04

