pandas计算群组留存率的核心逻辑是:先按user_id取首次行为时间打上cohort_date标签,再计算每个行为距首次的偏移天数/周数,最后以cohort_date和偏移期为索引聚合并归一化为百分比留存率。

用 pandas 计算群组留存率的核心逻辑
留存率不是简单按日期统计活跃人数,而是要锁定「首次行为发生的用户群组」,再看他们在后续各周/日的回访比例。关键在两步:打上群组标签(cohort_date),再按群组+偏移天数(period_number)聚合。
常见错误是直接用 groupby('date') 算每日留存——这算的是「当天活跃用户的次日留存」,不是标准群组分析。必须先用 min() 找出每个用户的首次行为时间,再用 dt.to_period('M') 或 dt.floor('D') 对齐群组粒度。
实操建议:
- 对用户行为表,先
groupby('user_id')['event_time'].min()得到first_visit - 合并回原表后,计算
(event_time - first_visit).dt.days // 7得周偏移量(或用// 1算日偏移) - 群组列建议用
first_visit.dt.strftime('%Y-%m'),避免PeriodIndex在 pivot 时出错
用 seaborn.heatmap 绘制热图时的坐标对齐问题
热图的 X 轴是群组(如 2023-01、2023-02),Y 轴是偏移期(第 0 周、第 1 周……),但 pivot_table 默认会按字典序排群组(2023-10 排在 2023-02 前)。不处理会导致热图横轴乱序。
立即学习“Python免费学习笔记(深入)”;
必须显式控制行列顺序:
- 用
reindex()按真实时间顺序重排群组列:cohort_list = pd.date_range('2023-01', '2023-12', freq='MS').strftime('%Y-%m').tolist() - Y 轴偏移期用
range(0, max_period + 1)显式指定,避免缺失期被跳过 - 传给
seaborn.heatmap()前,确保 DataFrame 的index和columns是字符串或整数,别混 Period 类型
否则会报错 TypeError: data type not understood 或热图行列颠倒。
留存率数值归一化与热图颜色标尺设置
原始留存数(如“第 0 周 1243 人,第 1 周 382 人”)不能直接画热图——不同群组基数差异大,视觉上无法比较。必须转成百分比,并以群组首周为分母。
正确做法是:对每个群组,用 div(df_cohort.iloc[:, 0], axis=0) 广播除法;错误做法是全局除总用户数或用 normalize=True(那是算频率分布,不是留存)。
热图颜色要突出衰减趋势:
- 设
vmin=0, vmax=1强制标尺范围,避免某群组异常高拉伸整体色阶 - 加
cbar_kws={'label': 'Retention Rate'}明确标注 - 用
cmap='Blues'比默认viridis更符合业务直觉(越蓝留存越高)
导出热图时字体与分辨率的实际坑
直接 plt.savefig('retention.png') 导出的图常出现中文乱码、轴标签截断、数字模糊。这不是代码逻辑问题,而是 matplotlib 后端和字体配置细节。
必须提前设置:
-
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial', 'DejaVu Sans']解决中文显示 -
plt.rcParams['axes.unicode_minus'] = False防止负号变方块 -
fig, ax = plt.subplots(figsize=(10, 8), dpi=150)控制画布大小和清晰度 - 调用
seaborn.heatmap()后加plt.tight_layout()避免右侧 colorbar 被切
如果群组多(比如 24 个月),X 轴文字会挤在一起,这时得用 ax.set_xticklabels(ax.get_xticklabels(), rotation=45, ha='right') 微调,而不是靠 figsize 硬撑。


















