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,不要期待有下篇~