Skip to content

Commit f54bf68

Browse files
fix(altair): address review feedback for heatmap-cohort-retention
Attempt 1/3 - fixes based on AI review
1 parent dafdbb3 commit f54bf68

1 file changed

Lines changed: 71 additions & 12 deletions

File tree

  • plots/heatmap-cohort-retention/implementations

plots/heatmap-cohort-retention/implementations/altair.py

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
""" pyplots.ai
1+
"""pyplots.ai
22
heatmap-cohort-retention: Cohort Retention Heatmap
33
Library: altair 6.0.0 | Python 3.14.3
44
Quality: 86/100 | Created: 2026-03-16
@@ -32,7 +32,6 @@
3232
rows = []
3333
for i, cohort in enumerate(cohort_labels):
3434
max_periods = n_cohorts - i
35-
base_retention = 100.0
3635
for period in range(max_periods):
3736
if period == 0:
3837
retention = 100.0
@@ -59,52 +58,112 @@
5958
cohort_order = [f"{c} (n={s:,})" for c, s in zip(cohort_labels, cohort_sizes, strict=True)]
6059
period_order = [f"Month {p}" for p in range(n_periods)]
6160

61+
# Custom dark teal-to-gold diverging-inspired sequential palette for sophistication
62+
color_domain = [0, 20, 40, 60, 80, 100]
63+
color_range = ["#f7f7f7", "#d4e8e0", "#7bc8b5", "#2a9d8f", "#264653", "#1d3557"]
64+
6265
# Heatmap rectangles
6366
heatmap = (
6467
alt.Chart(df)
65-
.mark_rect(stroke="white", strokeWidth=2.5)
68+
.mark_rect(stroke="#e8e8e8", strokeWidth=1.5, cornerRadius=3)
6669
.encode(
6770
x=alt.X(
6871
"period_label:O",
6972
title="Months Since Signup",
7073
sort=period_order,
71-
axis=alt.Axis(labelFontSize=17, titleFontSize=22, labelAngle=0),
74+
axis=alt.Axis(
75+
labelFontSize=17,
76+
titleFontSize=22,
77+
titleFontWeight="bold",
78+
labelAngle=0,
79+
domainWidth=0,
80+
tickWidth=0,
81+
titlePadding=16,
82+
labelPadding=8,
83+
),
7284
),
7385
y=alt.Y(
7486
"cohort_label:O",
7587
title="Signup Cohort",
7688
sort=cohort_order,
77-
axis=alt.Axis(labelFontSize=15, titleFontSize=22),
89+
axis=alt.Axis(
90+
labelFontSize=17,
91+
titleFontSize=22,
92+
titleFontWeight="bold",
93+
domainWidth=0,
94+
tickWidth=0,
95+
titlePadding=16,
96+
labelPadding=8,
97+
),
7898
),
7999
color=alt.Color(
80100
"retention_rate:Q",
81-
scale=alt.Scale(scheme="blues", domain=[0, 100]),
82-
legend=alt.Legend(title="Retention %", titleFontSize=18, labelFontSize=16, gradientLength=400),
101+
scale=alt.Scale(domain=color_domain, range=color_range),
102+
legend=alt.Legend(
103+
title="Retention %",
104+
titleFontSize=18,
105+
titleFontWeight="bold",
106+
labelFontSize=16,
107+
gradientLength=400,
108+
gradientThickness=18,
109+
orient="right",
110+
offset=12,
111+
),
83112
),
113+
tooltip=[
114+
alt.Tooltip("cohort:N", title="Cohort"),
115+
alt.Tooltip("period_label:O", title="Period"),
116+
alt.Tooltip("retention_rate:Q", title="Retention %", format=".1f"),
117+
],
84118
)
85119
)
86120

87-
# Text annotations inside cells
121+
# Text annotations with suffix
88122
text = (
89123
alt.Chart(df)
90-
.mark_text(fontSize=16, fontWeight="bold")
124+
.mark_text(fontSize=15, fontWeight="bold")
91125
.encode(
92126
x=alt.X("period_label:O", sort=period_order),
93127
y=alt.Y("cohort_label:O", sort=cohort_order),
94128
text=alt.Text("retention_rate:Q", format=".1f"),
95-
color=alt.condition(alt.datum.retention_rate > 55, alt.value("white"), alt.value("#333333")),
129+
color=alt.condition(alt.datum.retention_rate > 50, alt.value("white"), alt.value("#333333")),
130+
)
131+
)
132+
133+
# Percent symbol as separate smaller text layer for polish
134+
pct = (
135+
alt.Chart(df)
136+
.mark_text(fontSize=10, fontWeight="normal", dx=20)
137+
.encode(
138+
x=alt.X("period_label:O", sort=period_order),
139+
y=alt.Y("cohort_label:O", sort=cohort_order),
140+
text=alt.value("%"),
141+
color=alt.condition(
142+
alt.datum.retention_rate > 50, alt.value("rgba(255,255,255,0.7)"), alt.value("rgba(51,51,51,0.5)")
143+
),
96144
)
97145
)
98146

99147
# Combine
100148
chart = (
101-
(heatmap + text)
149+
alt.layer(heatmap, text, pct)
102150
.properties(
103151
width=1400,
104152
height=900,
105-
title=alt.Title("heatmap-cohort-retention · altair · pyplots.ai", fontSize=28, anchor="middle"),
153+
title=alt.Title(
154+
"heatmap-cohort-retention · altair · pyplots.ai",
155+
fontSize=28,
156+
fontWeight="bold",
157+
anchor="middle",
158+
subtitle="Monthly SaaS user retention — earliest cohorts show strongest long-term engagement",
159+
subtitleFontSize=18,
160+
subtitleColor="#666666",
161+
subtitlePadding=8,
162+
),
106163
)
107164
.configure_view(strokeWidth=0)
165+
.configure(padding={"left": 20, "right": 20, "top": 20, "bottom": 20}, background="#ffffff")
166+
.configure_axis(labelColor="#444444", titleColor="#333333")
108167
)
109168

110169
# Save

0 commit comments

Comments
 (0)