
原创内容
No.769
数分人必知必会 | 分析方法:同期群分析Cohort Analysis
本周份的分析干货,供大家参考~

图片由夸克AI绘制
同期群分析是一种比较常见的分析方法,在互联网行业、信贷风控、产品优化、市场营销等领域都有着非常普遍的应用,在数分人必知必会的分析方法中,绝对要算一个必知必会的席位。
官方一点的对同期群分析的介绍是这样的:Cohort Analysis是一种数据分析方法,它将具有共同特征(通常指在同一时间段内首次发生某行为)的用户或对象划分为一个群体(即“同期群”),然后追踪并比较这些群体随时间变化的行为表现,从而揭示不同群体间的差异和演化规律。
所谓的同期群,其实就是按共同特征(如注册月份、首次购买时间、出生年份等)划分的用户群体,说得俗一点就是:具有可比性的群体。
绝大多数的分析方法说到底都是最朴素的逻辑,具有可比性。从逻辑学的角度来说,当且仅当两个(或多个)对象处于同一个“比较框架”之下,并且在该框架所规定的比较维度上,它们都确实“落在同一把尺子上”,我们才说它们“具有可比性”,缺了这把尺子,就谈不上比较真假、好坏、大小、优劣。只有同尺度、同维度、同标准的两个比较个体,才叫做具有可比性。
为什么要使用同期群分析呢,主要是处于下面愿意i你的考虑:
同期群分析通常以 “时间” + “行为指标” 组合观察,比如:不同月份注册用户的月度留存率、不同渠道用户的首次购买后复购率等。通过细分用户生命周期,我们可以识别到增长机会、验证策略有效性,并避开整体数据的认知陷阱。尤其适用于产品优化、运营活动复盘和用户价值深度挖掘场景。
接下来我们用一个Python案例来介绍cohort分析:
在正式开始之前,我们需要生成一个虚拟的数据集,大概这样:

生成虚拟数据集的代码如下:
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
from operator import attrgetter
rng = np.random.default_rng(42)
n_users = 3000
# 随机注册日期:2023-01-01 到 2023-06-30
reg_dates = pd.to_datetime(rng.choice(pd.date_range('2023-01-01', '2023-06-30'), n_users))
# 每个用户随机在注册后 90 天内购买 1~10 次
records = []
for uid, reg inenumerate(reg_dates, start=1):
n_orders = rng.integers(1, 11)
order_dates = reg + pd.to_timedelta(rng.integers(0, 90, n_orders), unit='D')
for d in order_dates:
records.append({'user_id': uid,
'reg_date': reg.normalize(),
'order_date': d.normalize()})
df = pd.DataFrame(records)然后构造一些需要的辅助列,对于这个虚拟数据集,需要这么几个辅助列:注册月份、下单月份、第N个月后下单:

辅助列构造的代码如下:
df['reg_period'] = df['reg_date'].dt.to_period('M') # 注册月份
df['order_period'] = df['order_date'].dt.to_period('M') # 下单月份
df['cohort_index'] = (df['order_period'] - df['reg_period']).apply(attrgetter('n')) # 第 N 个月基于上面的辅助列,我们可以得到一个cohort表:也就是基于注册月份来看,当月下单、1个月后下单、2个月后下单的订单数:

实现的代码也比较简单:
cohort_table = (df.groupby(['reg_period', 'cohort_index'])
.agg(n_customers=('user_id', 'nunique'))
.reset_index())接下来把首月人数合并进来做分母:

cohort_size = cohort_table[cohort_table['cohort_index'] == 0][['reg_period', 'n_customers']]
cohort_table = cohort_table.merge(cohort_size.rename(columns={'n_customers': 'cohort_size'}),
on='reg_period')
cohort_table['retention_rate'] = cohort_table['n_customers'] / cohort_table['cohort_size']然后把表格在做一次透视,形成一个比较方便作图的形式:

retention_pivot = cohort_table.pivot(index='reg_period',
columns='cohort_index',
values='retention_rate')至此,数据处理的部分就完成了,接下来是数据可视化的部分工作了:
plt.rcParams['font.sans-serif'] = ['SimHei'] # 黑体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
plt.figure(figsize=(10, 6))
sns.heatmap(retention_pivot,
annot=True,
fmt='.1%',
cmap='Reds',
cbar_kws={'label': '留存率'})
plt.title('Cohort Analysis:按月注册用户的月度留存率')
plt.xlabel('生命周期月份')
plt.ylabel('注册月份')
plt.tight_layout()
plt.show()最终的图大概是这样:

同样的,我们也可以用SQL写这个案例。
首先是创建一个表,写入一些随机数据作为案例数据集:
-- 生成 2023-01 ~ 2023-06 的 3 000 条模拟订单
INSERTINTO orders(user_id, order_dt)
SELECT
u,
d
FROM generate_series(1, 3000) AS u,
generate_series(
'2023-01-01'::date+ (random()*180)::int,
'2023-01-01'::date+ (random()*180)::int+ (random()*60)::int,
interval'1 day') AS d;其他的处理方案和用Python也是比较类似的:
WITH
-- 计算首单日期(注册日期)
first_orders AS (
SELECT
user_id,
MIN(order_dt) AS reg_date
FROM orders
GROUPBY user_id
),
-- 给每条订单打「注册月份」和「生命周期月份」
order_period AS (
SELECT
o.user_id,
fo.reg_date,
date_trunc('month', fo.reg_date) AS reg_month,
date_trunc('month', o.order_dt) AS order_month,
(EXTRACT(year FROM o.order_dt) -EXTRACT(year FROM fo.reg_date))*12
+EXTRACT(monthFROM o.order_dt) -EXTRACT(monthFROM fo.reg_date) AS cohort_index
FROM orders o
JOIN first_orders fo ON fo.user_id = o.user_id
),
-- 统计每个注册月份的「总人数」与「次月留存人数」
cohort_size AS (
SELECT reg_month, COUNT(DISTINCT user_id) AS users_total
FROM order_period
WHERE cohort_index =0
GROUPBY reg_month
)
, cohort_retained AS (
SELECT reg_month, COUNT(DISTINCT user_id) AS users_retained
FROM order_period
WHERE cohort_index =1 -- 次月
GROUPBY reg_month
)
-- 计算留存率
SELECT
cs.reg_month,
cs.users_total,
COALESCE(cr.users_retained, 0) AS retained_1m,
ROUND(
COALESCE(cr.users_retained, 0)::numeric
/ cs.users_total, 4) AS retention_rate_1m
FROM cohort_size cs
LEFTJOIN cohort_retained cr
ON cr.reg_month = cs.reg_month
ORDERBY cs.reg_month;和Python版本的案例类似,到这里数据本身就处理完了,接下来就是根据这些数据作图,作图本身还是Python作图会容易一些。
总的来说,不管是Python代码实现还是Excel实现,Cohort分析涉及的代码都比较简单。
Cohort分析真正的核心还是在于对于同期群,也就是对于可比群体的定义。Cohort定义必须稳定,不能中途变更,否则会导致数据不可比。
此外,也需要注意避免小样本Cohort的结论偏差(如某Cohort仅100人)。同一用户可能在多个Cohort中出现,需根据业务逻辑去重或合并。
