HeoiJin 凹凸數據 html
做者簡介
HeoiJin:立志透過數據看清世界的產品策劃,專一爬蟲、數據分析、產品策劃領域。
萬物皆營銷 | 資本永不眠 | 數據恆真理
CSDN:https://me.csdn.net/weixin_40679090、、python
後互聯網時代,獲客拉新的成本愈來愈高,如何增長客戶的留存,提升客戶的復購次數、購買金額等變得十分重要,同期羣分析即是當中很是重要的分析方法。
關於同期羣分析概念和思路的文章不少,但分享如何實現的文章很是罕見。所以,本文將簡單介紹同期羣分析的概念,並用數據分析師的三板斧ESP(Excel、MySQL、Python)分別實現同期羣分析。面試
數據分析最終目標都是爲了解決業務問題,任何分析方法都只是工具。所以在詳細講解如何實現以前,須要先明晰方法的含義是什麼,能帶來什麼收益,才能在合適的問題上選對分析方法。算法
同期羣(Cohort)即相同時間內具備類似或特定屬性 、行爲的羣體。核心要素爲時間+特定屬性,好比把00後出生的人劃分爲一個羣組。
同期羣分析指將用戶進行同期羣劃分後,對比不一樣同期羣用戶的相同指標。咱們耳熟能詳的留存率就是同期羣分析的其中一種,案例以下圖:
同期羣分析包含了3個重要元素:數據庫
同期羣分析給到更加細緻的衡量指標,幫助咱們實時監控真實的用戶行爲、衡量用戶價值,併爲營銷方案的優化和改進提供支撐:app
4.1 數據狀況梳理
拿到數據的第一步,天然是瞭解數據的狀況。針對本次同期羣分析,咱們可能須要用到的字段有:ide
針對此份數據,有3個分析方向能夠選擇:函數
Excel的實現方式是三個當中門檻最低的,只須要掌握數據透視表和一些基礎函數,但過程相對繁雜。實現思路以下:
實現思路一共分爲4大部分:數據清洗 -> 計算首單時間 -> 計算首單時間與付款時間差 -> 利用透視表計算同期羣留存量和留存率。其中因爲部分版本的office和wps的數據透視表不支持非重複計數,所以須要先計算各月中各用戶出現的次數。
數據清洗部分只須要篩選+刪除即可完成,相信如此簡單的操做難不倒各位看官老爺們,那麼咱們便從第二部分開始詳細講解。工具
首先經過數據透視表求每個用戶首次付款時間。數據透視表,說白了就是經過特定的條件進行分組,並對數據進行求和、求均值、求方差等聚合操做。在製做數據透視表時要注意如下幾點:測試
全選數據 -> 插入 -> 數據透視表 -> 肯定
5.1.2 選擇分組字段和值字段
將「客戶暱稱」拖進「行」,將付款時間拖進「值」,並將值字段設置中的彙總方式設置爲最小值
這裏最小付款時間顯示爲10位的時間戳,只要調整顯示格式即可轉爲咱們常見的xx年xx月xx日。
5.1.3 將首單時間拼接到每一個用戶所在行
此步驟須要使用到vlookup函數進行匹配。VLOOKUP函數是一個縱向查找的函數,包含4個參數,具體語法爲=VLOOKUP(查找的依據,查找的區域,返回的值在查找區域中的列號,是否近似匹配)
注意:
=VLOOKUP(A2,首付時間透視表!A:B,2,0)
利用VLOOKUP拼接以後,首單時間一樣顯示爲10位的時間戳,設置單元格格式後便可顯示爲上圖的形式。
5.2.1 對付款時間和首單時間進行降採樣
如按算法2進行計算,可直接省略此步驟。
可能有看官老爺對重採樣的概念並非很清楚,簡單說下:
=YEAR(B2)&"/"&MONTH(B2)
5.2.2 計算時間差
此步驟中須要用到DATEDIF函數,此公式經常使用於計算兩個日期之間的天數、月份、年數差,語法爲:=DATEDIF(起始時間,結束時間,時間頻率),經常使用的時間頻率參數有['Y','M','D'],分別對應年月日
=DATEDIF(E2,D2,"M")
5.2.3 重置月份差標籤
修改透視表的標籤並不方便,所以先重置月份差標籤,須要用到一個IF函數即可。具體語法:=IF(條件,符合條件時的操做,不符合條件時的操做)
=IF(F2=0,"首月","+"&F2&"月")
5.3 計算同期留存量和留存率
若是是office 2013及以後的版本,以上的數據已經足夠咱們進行留存量的計算,能夠直接跳過計算用戶出現次數環節。
5.3.1 計算每個月中每一個用戶出現的次數
這裏利用COUNTIFS函數,計算出「用戶暱稱」和「付款時間(重採樣)」均相同的次數,並取其倒數,讓當月不管該用戶出現多少次,最終都只會計算爲一次。即假設用戶當月付款5次,倒數後權重變爲1/5,求和後出現次數爲1。
COUNTIFS的語法爲:=COUNTIFS(區域A,條件A,區域B,條件B,....)
=COUNTIFS(A:A,A:A,D:D,D:D,E:E,E:E) =1/H2
5.3.2 建立留存量數據透視表
針對wps及office2013之前的版本,咱們已經計算了出現次數的倒數,只須要仿照前文「計算每一個用戶首單時間」的步驟建立數據透視表,以「首單時間重採樣」做爲行,以「月份差標籤」做爲列,以「出現次數(倒數)」做爲值,並修改值字段設置中的計算類型爲求和便可。
而office 2013及以後的版本,咱們在插入數據透視表時,須要注意勾選「將此數據添加到數據模型」
數據透視表
一樣以「首單時間重採樣」做爲行,以「月份差標籤」做爲列,但不一樣的是,咱們能夠直接以「客戶暱稱」做爲值,並在值字段設置當中,將計算類型設置爲「非重複計數」。
到此,咱們留存量的透視圖便完成了,但格式看上去仍是有點醜,咱們手動拖動下行、列標籤的排序,最終得到以下效果:
5.3.3 計算留存率
在值字段顯示方式當中並無找到咱們想要的效果,所以咱們在數據透視表下方選定一個區域,複製好行標籤和列標籤。經過公式「=C5/$B5」計算出留存率,並向右向下拖動公式即可完成
注:
MySQL的實現路徑與Excel的實現路徑很是相近,具體步驟爲:
目前的數據的保存格式爲xlsx,咱們須要先將數據導入到數據庫當中才能執行查詢。第一步選擇一個庫,右鍵選擇導入嚮導。
第二步選擇導入類型,咱們直接選擇Excel文件便可。
第三步爲選擇數據源的路徑,咱們找到對應的數據後,勾選須要導入的表。
完成前文的操做以後即可以點擊「>>」跳轉至最後的步驟,固然中間還有幾個調整數據的步驟,但這次數據十分工整,不須要進行額外操做。
到達下圖的界面,咱們按照指引直接點擊「開始」便可,如導入成功,會在日誌欄中顯示Finished successfully,以下圖所示。
照舊先篩選出訂單狀態爲交易成功的行,並提取用戶暱稱、付款時間兩個字段。這裏咱們稍微修改了列名,把用戶暱稱
修改爲c_id
,付款時間
修改成paytime
,交易狀態
修改爲了status
。
咱們後續的查詢都是基於篩選後的數據,所以這裏新建一個表sheet2去存儲查詢結果。
-- 步驟一:篩選訂單狀態爲」交易成功「的行,並輸出表sheet2:用戶暱稱[c_id]、付款時間[paytime] CREATE table sheet2 as SELECT c_id,paytime FROM sheet1 WHERE `status`='交易成功';
此步驟只須要對用戶暱稱進行groupby,再求最小值便可,很少贅述。
-- 步驟二:找出每一個用戶的首單時間 SELECT c_id,min(paytime) f_time FROM sheet2 GROUP BY c_id;
6.4 計算月份差,重採樣首付時間
此步驟中會涉及到兩個重要的函數:
-- 步驟三:求出月份差,對首付時間進行重採樣 SELECT a.c_id, b.f_time, TIMESTAMPDIFF(MONTH,b.f_time,a.paytime) m_diff, CONCAT(YEAR(b.f_time),"年",MONTH(b.f_time),"月") y_m FROM sheet2 a LEFT JOIN ( SELECT c_id,min(paytime) f_time FROM sheet2 GROUP BY c_id -- LIMIT測試時用,爲了提高效率 LIMIT 0,7000 ) b on a.c_id=b.c_id -- 一樣是爲了提高效率而使用 WHERE b.f_time is NOT NULL;
咱們只須要將前面的三個步驟做爲子查詢,並以首單時間
以及月份差
做爲條件對數據進行分組,用DISTINCT篩選出惟一的用戶ID
便可求出咱們所需的留存量。這裏建立一個名爲cohort的表儲存查詢結果。
-- 步驟四:經過首付時間和月份差進行分組,求出惟一的用戶id數,並輸出爲表[cohort] CREATE table cohort as SELECT c.y_m "首付月份",c.m_diff"月份差",COUNT(DISTINCT c.c_id) "留存量" FROM ( SELECT a.c_id, b.f_time, TIMESTAMPDIFF(MONTH,b.f_time,a.paytime) m_diff, CONCAT(YEAR(b.f_time),"年",MONTH(b.f_time),"月") y_m from sheet2 a LEFT JOIN ( SELECT c_id,min(paytime) f_time FROM sheet2 GROUP BY c_id ) b on a.c_id=b.c_id -- 爲了提高效率而使用 WHERE b.f_time is NOT NULL ) c GROUP BY c.y_m,c.m_diff;
查詢結果以下。相比於步驟三,咱們這裏刪除了用於分頁查詢的LIMIT語句,但依然保留了WHERE b.f_time is NOT NULL。這裏的where語句並無篩選任何一行,但有無這一句的查詢效率相差很是大,分別爲0.739s和125.649s。這裏涉及到SQL優化的問題,有機會之後專門整理一篇文章分享給各位。
咱們有了留存量的表格,計算留存率便很是容易,只要讓每一期的留存率都除以首月的留存率便可。
-- 步驟五:計算留存率(基礎版) SELECT c.`首付月份`,CONCAT(ROUND((c.`留存量`/m.`留存量`)*100,2),"%") 留存率 FROM cohort c LEFT JOIN ( SELECT 首付月份,留存量 FROM cohort where `月份差`=0 ) m on c.`首付月份`=m.`首付月份`;
留存率結果如上圖,但結果並不利於觀察和分析,所以接下來的進階版將經過case when語句,加入億點細節來優化下展現格式。
-- 步驟五:計算留存率(進階版) SELECT n.`首付月份`, AVG(n.`留存量`) "本月新增", CONCAT(sum(n.`+1月`),"%") "+1月", CONCAT(sum(n.`+2月`),"%") "+2月", CONCAT(sum(n.`+3月`),"%") "+3月", CONCAT(sum(n.`+4月`),"%") "+4月", CONCAT(sum(n.`+5月`),"%") "+5月" FROM( # 一級子查詢:轉置表格,將月份差做爲列名 SELECT a.`首付月份`, a.`留存量`, CASE a.`月份差` when 1 THEN a.`留存率` ELSE 0 END "+1月", CASE a.`月份差` when 2 THEN a.`留存率` ELSE 0 END "+2月", CASE a.`月份差` when 3 THEN a.`留存率` ELSE 0 END "+3月", CASE a.`月份差` when 4 THEN a.`留存率` ELSE 0 END "+4月", CASE a.`月份差` when 5 THEN a.`留存率` ELSE 0 END "+5月" FROM( # 二級子查詢:計算留存率 SELECT a.`首付月份`,b.`留存量`,a.`月份差`,ROUND((a.`留存量`/b.`留存量`)*100,2) 留存率 FROM cohort a LEFT JOIN ( # 三級子查詢:查詢首月用戶量 SELECT `首付月份`,`留存量` FROM cohort WHERE cohort.`月份差`=0 ) b on a.`首付月份`=b.`首付月份` ) a ) n GROUP BY n.`首付月份`;
正如「分析方法肯定」環節中說起,Excel中經過天然月去劃分月份的偏移量,而MySQL中則直接將付款時間和首單時間相減。咱們使用的TIMESTAMPDIFF函數的邏輯爲結束日期的DAY參數大於等於起始日期的DAY參數時,月份差纔會+N。即:
做爲壓軸,確定是路子野、效率高、操做騷的Python。得益於pandas強大的分組功能及很是多的奇技淫巧,Python的實現相比於Excel或MySQL會更加簡單,但實現路徑會比較抽象,須要注入一點想象力。按慣例先盤實現思路:
此步驟只須要調用drop函數便可完成刪除,難度不大,核心是找到訂單狀態爲「交易失敗」的所在行的行索引。
df.drop(index=df[df['訂單狀態'] == '交易失敗'].index, axis=1, inplace=True)
調用分組聚合函數groupby以及數據拼接函數merge便能完成咱們的需求,都是常規操做
df_f = df.groupby(by='客戶暱稱')['付款時間'].min().to_frame(name='首單時間') df_f.reset_index(inplace=True) # 合併新的dataframe,包含客戶暱稱,付款時間,首單時間 df_f = df[['客戶暱稱', '付款時間']].merge(df_f)
接下來就是見證騷操做的時刻了。在pandas的分組聚合當中,對時間戳進行重採樣不要太簡單,只須要修改freq參數便可。核心思路:
# 經過首單時間及付款時間進行分組,得到每一個時間段的不重複客戶數量 df_f = df_f.groupby(by=[pd.Grouper(key='首單時間', freq='m'), pd.Grouper(key='付款時間', freq='m')])['客戶暱稱'].nunique() # 將複合索引的series轉置爲dataframe df_f = df_f.unstack()
得到的結果如上圖。若是有看Excel或MySQL實現方式的看官可能有會有疑問,爲何python不用計算月份差而其餘兩種須要。那是由於這種分組方式,首月用戶量都分佈在表格的對角線上,在Excel的數據透視表或者MySQL當中,等差地移動單元格並非一件容易的事,但對於Python來講,不過是一個for循環。
for i in range(len(df_f.index)): df_f.iloc[i] = df_f.iloc[i].shift(periods=-i) # 重置columns df_f.columns = ['本月新增', '+1月', '+2月', '+3月', '+4月', '+5月']
shift函數經常使用於移動dataframe或series,具體參數以下:
儘管pandas很是強大,但此步驟中,如經過df_f/df_f[‘首月’]計算,結果是全爲NaN的dataframe。不過咱們可使用apply函數遍歷dataframe來實現。
df_1 = df_f.apply(count_per, axis=0, args=(df_f['本月新增'],)) df_1['本月新增']=df_f['本月新增'] def count_per(s, dx): a=[f'{i}%' if str(i)!='nan' else 0 for i in round((s / dx) * 100, 2)] return a
做爲pandas中最好用的函數之一,apply的詳細用法各位參考官方文檔便可,這裏僅提三點注意事項:
先回顧下同期羣分析的重點
那麼本次的分享到這裏便結束了。至於同期羣分析如何應用到實際業務問題中,咱們留到下一篇商業分析實戰再詳細講解。(若是寫出來的話,必定不會鴿,必定不會~鴿!)
我是HeoiJin,不要期待有下篇~