|
1 | | -""" pyplots.ai |
| 1 | +"""pyplots.ai |
2 | 2 | heatmap-cohort-retention: Cohort Retention Heatmap |
3 | 3 | Library: plotnine 0.15.3 | Python 3.14.3 |
4 | 4 | Quality: 85/100 | Created: 2026-03-16 |
|
51 | 51 | else: |
52 | 52 | base_decay = 100 * np.exp(-0.25 * period) |
53 | 53 | noise = np.random.uniform(-3, 3) |
54 | | - trend_bonus = i * 0.4 |
| 54 | + trend_bonus = i * 1.5 |
55 | 55 | retention = np.clip(base_decay + noise + trend_bonus, 5, 100) |
56 | 56 | rows.append( |
57 | 57 | {"cohort": cohort, "period": period, "retention_rate": round(retention, 1), "cohort_size": cohort_sizes[i]} |
|
72 | 72 | # Format retention text |
73 | 73 | df["label"] = df["retention_rate"].apply(lambda v: f"{v:.0f}%") |
74 | 74 |
|
75 | | -# Highlight best-performing cohort at month 3 for storytelling |
76 | | -month3 = df[df["period"] == 3].copy() |
77 | | -best_cohort_idx = month3["retention_rate"].idxmax() |
78 | | -best_cohort = df.loc[best_cohort_idx] |
| 75 | +# Compare earliest vs latest cohort at same period for storytelling |
| 76 | +compare_period = 4 |
| 77 | +earliest = df[(df["cohort"] == "Jan 2024") & (df["period"] == compare_period)]["retention_rate"].values[0] |
| 78 | +latest = df[(df["cohort"] == "Jun 2024") & (df["period"] == compare_period)]["retention_rate"].values[0] |
| 79 | +improvement = latest - earliest |
79 | 80 |
|
80 | | -# Multi-stop color palette: deep navy → teal → warm amber for high retention |
81 | | -colors = ["#0d1b2a", "#1b2838", "#1b4965", "#2a6f97", "#62b6cb", "#bee9e8", "#fefae0", "#dda15e"] |
| 81 | +# Clean 3-stop color palette: cream → teal → deep navy |
| 82 | +colors = ["#0d1b2a", "#2a9d8f", "#fefae0"] |
82 | 83 |
|
83 | 84 | # Plot |
84 | 85 | plot = ( |
85 | 86 | ggplot(df, aes(x="period", y="cohort_label", fill="retention_rate")) |
86 | 87 | + geom_tile(color="#f8f9fa", size=0.6) |
87 | | - + geom_text(aes(label="label", color="text_color"), size=11, fontweight="bold") |
| 88 | + + geom_text(aes(label="label", color="text_color"), size=13, fontweight="bold") |
88 | 89 | + scale_fill_gradientn(colors=colors[::-1], limits=(0, 100), name="Retention %") |
89 | 90 | + scale_color_identity() |
90 | 91 | + scale_x_continuous(breaks=range(n_cohorts), labels=[f"Month {i}" for i in range(n_cohorts)]) |
91 | 92 | + scale_y_discrete(expand=(0.05, 0)) |
92 | 93 | + annotate( |
93 | 94 | "text", |
94 | | - x=n_cohorts - 1.5, |
| 95 | + x=n_cohorts - 2, |
95 | 96 | y=3, |
96 | | - label=f"Best Month 3:\n{best_cohort['cohort']}\n{best_cohort['retention_rate']:.0f}%", |
97 | | - size=10, |
98 | | - color="#2a6f97", |
| 97 | + label=f"Month {compare_period} retention improved\n+{improvement:.0f}pp from Jan→Jun 2024", |
| 98 | + size=11, |
| 99 | + color="#0d1b2a", |
99 | 100 | ha="center", |
100 | 101 | fontweight="bold", |
101 | 102 | ) |
102 | 103 | + labs( |
103 | 104 | x="Months Since Signup", |
104 | 105 | y="", |
105 | 106 | title="heatmap-cohort-retention · plotnine · pyplots.ai", |
106 | | - subtitle="Monthly cohort retention — later cohorts show improving retention trends", |
| 107 | + subtitle="Monthly cohort retention — newer cohorts retain significantly better over time", |
107 | 108 | ) |
108 | 109 | + theme_minimal() |
109 | 110 | + theme( |
|
0 commit comments