用pandas计算各cohort首访周及距首访周数,交叉表统计去重用户数,以首周人数为分母算留存率,再用seaborn.heatmap(cmap='Blues', vmin=0, vmax=1, annot=True, fmt='.1%')绘制热力图。

怎么用 Python 快速画出留存率矩阵热力图
直接用 pandas 做分组统计 + seaborn.heatmap 渲染,别碰 plotly 或自定义颜色映射——前者渲染慢、交互干扰分析节奏,后者容易把时间轴对齐搞错。核心是确保矩阵行是“首访周”,列是“距首访后的第 N 周”,数值是该 cohort 在对应周期的活跃用户占比。
cohort 表格必须按「首访时间 → 后续行为时间」双维度聚合
常见错误是只按自然周聚合,结果把不同 cohort 混在一起;或者用 groupby('week') 单维度切片,丢失了“谁在什么时候开始”的锚点。正确做法:
- 先用
df['first_visit_week'] = df.groupby('user_id')['visit_date'].transform('min').dt.to_period('W')算每个用户的首次访问周 - 再算每次访问距首访的周数:
df['week_diff'] = (df['visit_date'].dt.to_period('W') - df['first_visit_week']).apply(lambda x: x.n) - 最后用
pd.crosstab(df['first_visit_week'], df['week_diff'], values=df['user_id'], aggfunc=pd.Series.nunique)得到原始频次矩阵
计算留存率时,分母必须是每个 cohort 的首周用户数
很多人直接用 df.div(df.iloc[:, 0], axis=0),但前提是第一列确实是首周(week_diff == 0)。如果数据里有缺失周或排序乱了,分母就错。稳妥做法:
- 确保
crosstab结果列索引从 0 开始且连续,可用matrix = matrix.reindex(columns=range(matrix.shape[1]), fill_value=0) - 显式取首列做分母:
cohort_size = matrix.iloc[:, 0],再用matrix.div(cohort_size, axis=0).round(3) - 注意:若某 cohort 首周只有 1 人,后续出现 0.333 这种值,说明你没去重
user_id——检查aggfunc是否用了pd.Series.nunique
seaborn.heatmap 容易忽略的三个渲染细节
热力图看着像,但业务同学常反馈“看不出趋势”,问题多出在可视化层:
立即学习“Python免费学习笔记(深入)”;
- 别用默认
cmap='viridis',它对低值不敏感;换成cmap='Blues'或cmap='RdYlBu_r',并加vmin=0, vmax=1 -
annot=True时,小数位太多会挤满格子;加fmt='.1%'自动转百分比并保留一位小数 - 横纵坐标标签太长?用
plt.xticks(rotation=0)和plt.yticks(rotation=0),别信网上那些 45 度旋转方案—— cohort 名是“2024-W12”这种固定格式,歪着反而难对齐
真正卡住人的不是代码,是 cohort 边界定义:比如“首访”到底指注册、首次点击还是首次付费?这个口径一旦和业务方没对齐,后面所有数字都是幻觉。画图前,先确认你们的 first_visit_week 是基于哪条事件日志、是否已剔除测试账号和爬虫流量。









