Cohort Analysis是将某一个时期内的用户划分为一个cohort,并将多个cohort进行时间上的某个属性的比较的一种分析方法。Cohort Analysis在有些场景下非常有用。比如一个网站或App,在某个连续的4周里陆续更新或新增了一个功能或设计,想要知道这些功能和设计上的改动对用户的影响,就可以将每周的新注册作为一个cohort,观察这4个cohort在接下来的一段实际里的行为数据,就可以很清楚地观察到4个改动的影响。

最近要做Cohort Analysis,数据都在数据库里,就直接想用Python接数据库直接分析了。Google到一篇讲得很清楚的文章,学着用Python实现一遍。示例数据从这里下载。

1. 读取数据

据说 from ... import ... 的方式在性能上有弊端,一般都推荐直接 import。示例数据是典型的购物数据,按客户的第一次消费时间将客户分为不同的cohort。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt %matplotlib inline
df = pd.read_excel('relay-foods.xlsx', sheet='Purchase Data')
df.head()
OrderId OrderDate UserId TotalCharges CommonId PupId PickupDate
0 262 2009-01-11 47 50.67 TRQKD 2 2009-01-12
1 278 2009-01-20 47 26.60 4HH2S 3 2009-01-20
2 294 2009-02-03 47 38.71 3TRDC 2 2009-02-04
3 301 2009-02-06 47 53.38 NGAZJ 2 2009-02-09
4 302 2009-02-06 47 14.28 FFYHD 2 2009-02-09

2. 确定 OrderDate 的月份,根据 OrderDate 分群

分别创建两个字段 OrderPeriodCohortGroupOrderPeriod是购买日期的月份,CohortGroup是根据购买日期对 UserId 的分群。

df['OrderPeriod'] = df.OrderDate.map(lambda x: x.strftime('%Y-%m'))

df = df.assign(OrderPeriod = df.OrderDate.map(lambda x: x.strftime('%Y-%m'))) \
.set_index('UserId') # goupby(level=0): level 是index的level, 对于multiIndex, 可用level=0或1指定根据那个index来group
df = df.assign(CohortGroup = df.groupby(level=0).OrderDate.min().apply(lambda x: x.strftime('%Y-%m'))) \
.reset_index() df.head()
UserId OrderId OrderDate TotalCharges CommonId PupId PickupDate OrderPeriod CohortGroup
0 47 262 2009-01-11 50.67 TRQKD 2 2009-01-12 2009-01 2009-01
1 47 278 2009-01-20 26.60 4HH2S 3 2009-01-20 2009-01 2009-01
2 47 294 2009-02-03 38.71 3TRDC 2 2009-02-04 2009-02 2009-01
3 47 301 2009-02-06 53.38 NGAZJ 2 2009-02-09 2009-02 2009-01
4 47 302 2009-02-06 14.28 FFYHD 2 2009-02-09 2009-02 2009-01

3. 计算每个CohortGroup在各个OrderPeriod的用户量

# pd.Series.nunique --> Return number of unique elements in the object.
cohorts = df.groupby(['CohortGroup', 'OrderPeriod']) \
.agg({'UserId': pd.Series.nunique,
'OrderId': pd.Series.nunique,
'TotalCharges': 'sum'})
cohorts.rename(columns={'UserId': 'TotalUsers', 'OrderId': 'TotalOrders'}, inplace=True)
cohorts.head()
TotalCharges TotalOrders TotalUsers
CohortGroup OrderPeriod
2009-01 2009-01 1850.255 30 22
2009-02 1351.065 25 8
2009-03 1357.360 26 10
2009-04 1604.500 28 9
2009-05 1575.625 26 10

4. 标记每个CohortGroup的Cohort时期

比如,对于2009-01 cohort,其第一个时期是 2009-01,第二到第五个时期为 2009-02,...,2009-05。这里需要将每个CohortGroup的OrderPeriod对应到其第1,2,...个时期。

def cohort_period(df):
df['CohortPeriod'] = np.arange(len(df)) + 1
return df cohorts = cohorts.groupby(level=0).apply(cohort_period)
cohorts.head()
TotalCharges TotalOrders TotalUsers CohortPeriod
CohortGroup OrderPeriod
2009-01 2009-01 1850.255 30 22 1
2009-02 1351.065 25 8 2
2009-03 1357.360 26 10 3
2009-04 1604.500 28 9 4
2009-05 1575.625 26 10 5

上面 level=0 实际上就是对 group by CohortGroup,然后对每个group结果 apply cohort_periodgroupby 后的结果是这样的:

[(k, v) for k, v in cohorts.head(5).groupby(level=0)]
[('2009-01',
TotalCharges TotalOrders TotalUsers CohortPeriod
CohortGroup OrderPeriod
2009-01 2009-01 1850.255 30 22 1
2009-02 1351.065 25 8 2
2009-03 1357.360 26 10 3
2009-04 1604.500 28 9 4
2009-05 1575.625 26 10 5)]

5. 确定分群后的结果正确

x = df[(df.CohortGroup=='2009-01') & (df.OrderPeriod=='2009-01')]
y = cohorts.ix[('2009-01', '2009-01')] assert(x.UserId.nunique()==y.TotalUsers)
assert(x.OrderId.nunique()==y.TotalOrders)
assert(x.TotalCharges.sum()==y.TotalCharges) x = df[(df.CohortGroup=='2009-03') & (df.OrderPeriod=='2009-05')]
y = cohorts.ix[('2009-03', '2009-05')] assert(x.UserId.nunique()==y.TotalUsers)
assert(x.OrderId.nunique()==y.TotalOrders)
assert(x.TotalCharges.sum()==y.TotalCharges)

每个CohortGroup的留存

6. 计算每个CohortGroup在第一个CohortPeriod的用户数量

cohorts = cohorts.reset_index() \
.set_index(['CohortGroup', 'CohortPeriod']) cohort_group_size = cohorts.TotalUsers.groupby(level=0).first()
cohort_group_size
CohortGroup
2009-01 22
2009-02 15
2009-03 13
2009-04 39
2009-05 50
2009-06 32
2009-07 50
2009-08 31
2009-09 37
2009-10 54
2009-11 130
2009-12 65
2010-01 95
2010-02 100
2010-03 24
Name: TotalUsers, dtype: int64

7. 计算每个CohortPeriod的留存率

user_retention = cohorts.TotalUsers.unstack(0).divide(cohort_group_size, axis=1)
user_retention.head()
CohortGroup 2009-01 2009-02 2009-03 2009-04 2009-05 2009-06 2009-07 2009-08 2009-09 2009-10 2009-11 2009-12 2010-01 2010-02 2010-03
CohortPeriod
1 1.000000 1.000000 1.000000 1.000000 1.00 1.00000 1.00 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.00 1.0
2 0.363636 0.200000 0.307692 0.333333 0.26 0.46875 0.46 0.354839 0.405405 0.314815 0.246154 0.261538 0.526316 0.19 NaN
3 0.454545 0.333333 0.384615 0.256410 0.24 0.28125 0.26 0.290323 0.378378 0.222222 0.200000 0.276923 0.273684 NaN NaN
4 0.409091 0.066667 0.307692 0.333333 0.10 0.18750 0.20 0.225806 0.216216 0.240741 0.223077 0.107692 NaN NaN NaN
5 0.454545 0.266667 0.076923 0.153846 0.08 0.21875 0.22 0.193548 0.351351 0.240741 0.100000 NaN NaN NaN NaN

留存率曲线

user_retention[['2009-01', '2009-05', '2009-08']] \
.plot(figsize=(11, 6), color=['#4285f4', '#EA4335', '#A60628'])
plt.title("Cohorts: User Retention")
plt.xticks(np.arange(1, len(user_retention)+1, 1))
plt.xlim(1, len(user_retention))
plt.ylabel('% of Cohort Purchasing', fontsize=16)

留存率热力图

import seaborn as sns
sns.set(style='white') plt.figure(figsize=(16, 8))
plt.title('Cohorts: User Retetion', fontsize=14)
sns.heatmap(user_retention.T,
mask=user_retention.T.isnull(),
annot=True, fmt='.0%')

Cohort Analysis Using Python的更多相关文章

  1. Cohort Analysis and LifeCycle Grids mixed segmentation with R(转)

    This is the third post about LifeCycle Grids. You can find the first post about the sense of LifeCyc ...

  2. Windows学习"Network Analysis in Python"

    原代码仓库的地址为 Network Analysis in Python. 主要按照里面的README.md 进行操作,全部仓库有100MB以上.考虑到数据比较大,再加上我对原笔记文件有修改,建议从我 ...

  3. 同期群分析(Cohort Analysis)

    什么是同期群? 将相同时间段内具有共同行为特征的用户划分为同一个群体,其被称为同期群.“共同行为特征”是指在某个时间段内的行为相似.最常见的是按不同时间的新增用户来划分,然后分析留存率.当然也可以按其 ...

  4. Applied Social Network Analysis in Python 相关笔记3

    如果是option2的话,答案选A. 这里节点s,从左边的选择,节点t从右边选择. 这里计算还是用以前的值,不用更新过的值.

  5. Applied Social Network Analysis in Python 相关笔记

  6. 《利用Python进行数据分析: Python for Data Analysis 》学习随笔

    NoteBook of <Data Analysis with Python> 3.IPython基础 Tab自动补齐 变量名 变量方法 路径 解释 ?解释, ??显示函数源码 ?搜索命名 ...

  7. Python for Data Analysis

    Data Analysis with Python ch02 一些有趣的数据分析结果 Male描述的是美国新生儿男孩纸的名字的最后一个字母的分布 Female描述的是美国新生儿女孩纸的名字的最后一个字 ...

  8. [Python] 机器学习库资料汇总

    声明:以下内容转载自平行宇宙. Python在科学计算领域,有两个重要的扩展模块:Numpy和Scipy.其中Numpy是一个用python实现的科学计算包.包括: 一个强大的N维数组对象Array: ...

  9. 【转帖】Python在大数据分析及机器学习中的兵器谱

    Flask:Python系的轻量级Web框架. 1. 网页爬虫工具集 Scrapy 推荐大牛pluskid早年的一篇文章:<Scrapy 轻松定制网络爬虫> Beautiful Soup ...

随机推荐

  1. 网易云音乐 歌词制作软件 BesLyric

    导读 哈哈,喜欢网易云音乐,又愁于制作歌词的童鞋有福啦! BesLyric 为你排忧解难! 上个周末在用网易云音乐听歌,发现一些喜欢的歌还没有滚动歌词,然而网易云音乐还没有自带的歌词编辑功能,要制作歌 ...

  2. UVA 11551 - Experienced Endeavour(矩阵高速幂)

    UVA 11551 - Experienced Endeavour 题目链接 题意:给定一列数,每一个数相应一个变换.变换为原先数列一些位置相加起来的和,问r次变换后的序列是多少 思路:矩阵高速幂,要 ...

  3. 数据结构与算法/leetcode/lintcode题解

    http://algorithm.yuanbin.me/zh-hans/index.html

  4. hadoop错误ERROR namenode.NameNode (NameNode.javamain(1657)) - Failed to start namenode java.net.BindException:Port in use:host1:50070

    解决方法: 1.通过lsof -i:50070(lsof可以通过yum install lsof安装)查看,发现是mysql被占用了 2.修改mysql端口 从/usr/share/mysql/my- ...

  5. (转载)Eclipse下配置Github环境 .

    总的参考文档:EGit User Guide http://wiki.eclipse.org/EGit/User_Guide Address: http://www.linuxidc.com/Linu ...

  6. 高性能爬虫为什么使用定制DNS客户端?

    DNS 解析是高性能网络爬虫的瓶颈,主要是因为: 1. 由于域名服务的分布式的特性,DNS解析可能需要多次的请求转发,有时需要几秒甚至更长的时间来解析出相应的IP 地址. 2. 现有的标准库对DNS解 ...

  7. RedHat7 SELinux

    SELinux(Security-Enhanced Linux) 是美国国家安全局(NSA)对于强制访问控制的实现,是 Linux历史上最杰出的新安全子系统.NSA是在Linux社区的帮助下开发了一种 ...

  8. FreeBSD更换默认csh为bash

    1.安装bash cd /usr/ports/shells/bash make install 2.切换chsh(change shell) chsh -s /usr/local/bin/bash

  9. Linux终端Ctrl相关快捷键

    快速跳至行首:Ctrl+A 快速跳至行尾:Ctrl+E 向前删除至行首:Ctrl+U 向后删除至行尾:Ctrl+K 向后删一个单词:Ctrl+D 清屏:Crtl+L(clear)

  10. replace()、replaceFirst()和replaceAll()的区别

    1.replace() String str= "mesquite in your cellar" str.replace('e', 'o') returns "mosq ...