Skip to content

Commit 65c5364

Browse files
feat(seaborn): implement heatmap-cohort-retention (#4937)
## Implementation: `heatmap-cohort-retention` - seaborn Implements the **seaborn** version of `heatmap-cohort-retention`. **File:** `plots/heatmap-cohort-retention/implementations/seaborn.py` **Parent Issue:** #4570 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/23165008151)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 3241306 commit 65c5364

2 files changed

Lines changed: 331 additions & 0 deletions

File tree

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
""" pyplots.ai
2+
heatmap-cohort-retention: Cohort Retention Heatmap
3+
Library: seaborn 0.13.2 | Python 3.14.3
4+
Quality: 90/100 | Created: 2026-03-16
5+
"""
6+
7+
import matplotlib.colors as mcolors
8+
import matplotlib.pyplot as plt
9+
import numpy as np
10+
import pandas as pd
11+
import seaborn as sns
12+
13+
14+
# Seaborn styling
15+
sns.set_context("talk", font_scale=1.1)
16+
sns.set_style("white")
17+
18+
# Data
19+
np.random.seed(42)
20+
21+
cohort_labels = [
22+
"Jan 2024",
23+
"Feb 2024",
24+
"Mar 2024",
25+
"Apr 2024",
26+
"May 2024",
27+
"Jun 2024",
28+
"Jul 2024",
29+
"Aug 2024",
30+
"Sep 2024",
31+
"Oct 2024",
32+
]
33+
n_cohorts = len(cohort_labels)
34+
n_periods = n_cohorts
35+
cohort_sizes = np.random.randint(800, 2500, size=n_cohorts)
36+
37+
retention_data = np.full((n_cohorts, n_periods), np.nan)
38+
for i in range(n_cohorts):
39+
max_periods = n_periods - i
40+
retention_data[i, 0] = 100.0
41+
base_decay = np.random.uniform(0.72, 0.85)
42+
for j in range(1, max_periods):
43+
prev = retention_data[i, j - 1]
44+
decay = base_decay + np.random.uniform(-0.03, 0.03)
45+
decay = min(decay, 0.98)
46+
retention_data[i, j] = round(prev * decay, 1)
47+
48+
period_labels = [f"Month {i}" for i in range(n_periods)]
49+
df_heatmap = pd.DataFrame(retention_data, index=cohort_labels, columns=period_labels)
50+
51+
# Custom colormap: warm tones for low retention, cool tones for high
52+
cmap = mcolors.LinearSegmentedColormap.from_list("retention", ["#f7e1d0", "#e8c4a0", "#7fbf7b", "#1b7837", "#00441b"])
53+
54+
# Plot
55+
fig, ax = plt.subplots(figsize=(16, 9))
56+
fig.set_facecolor("#fafafa")
57+
ax.set_facecolor("#f0f0f0")
58+
59+
mask = df_heatmap.isna()
60+
61+
# Annotation array with "%" suffix
62+
annot_array = df_heatmap.copy()
63+
annot_strings = annot_array.map(lambda v: f"{v:.0f}%" if not np.isnan(v) else "")
64+
65+
sns.heatmap(
66+
df_heatmap,
67+
mask=mask,
68+
annot=annot_strings,
69+
fmt="",
70+
cmap=cmap,
71+
vmin=0,
72+
vmax=100,
73+
linewidths=2.5,
74+
linecolor="#fafafa",
75+
ax=ax,
76+
annot_kws={"fontsize": 13, "fontweight": "bold"},
77+
cbar_kws={"label": "Retention %", "shrink": 0.75, "aspect": 30, "pad": 0.02},
78+
square=False,
79+
)
80+
81+
# Style
82+
y_labels = [f"{label} (n={size:,})" for label, size in zip(cohort_labels, cohort_sizes, strict=True)]
83+
ax.set_yticklabels(y_labels, rotation=0, fontsize=16)
84+
ax.set_xticklabels(period_labels, rotation=0, fontsize=16)
85+
ax.set_xlabel("Periods Since Signup", fontsize=20, labelpad=12)
86+
ax.set_ylabel("Signup Cohort", fontsize=20, labelpad=12)
87+
ax.set_title(
88+
"heatmap-cohort-retention · seaborn · pyplots.ai", fontsize=24, fontweight="medium", pad=24, color="#333333"
89+
)
90+
91+
# Colorbar styling
92+
cbar = ax.collections[0].colorbar
93+
cbar.ax.tick_params(labelsize=16)
94+
cbar.set_label("Retention %", fontsize=18, labelpad=10)
95+
cbar.outline.set_visible(False)
96+
97+
# X-axis at top
98+
ax.xaxis.tick_top()
99+
ax.xaxis.set_label_position("top")
100+
101+
# Remove all spines
102+
sns.despine(ax=ax, top=True, right=True, bottom=True, left=True)
103+
ax.tick_params(axis="both", length=0)
104+
105+
plt.tight_layout()
106+
plt.savefig("plot.png", dpi=300, bbox_inches="tight", facecolor=fig.get_facecolor())
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
library: seaborn
2+
specification_id: heatmap-cohort-retention
3+
created: '2026-03-16T20:46:04Z'
4+
updated: '2026-03-16T20:57:29Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 23165008151
7+
issue: 4570
8+
python_version: 3.14.3
9+
library_version: 0.13.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/heatmap-cohort-retention/seaborn/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/heatmap-cohort-retention/seaborn/plot_thumb.png
12+
preview_html: null
13+
quality_score: 90
14+
review:
15+
strengths:
16+
- Excellent spec compliance — all required features implemented correctly
17+
- Custom colormap with warm-to-green transition creates a distinctive professional
18+
look
19+
- 'Clean visual refinement: all spines removed, tick marks hidden, custom background,
20+
styled colorbar'
21+
- Idiomatic seaborn usage with mask, annotations, and context/style configuration
22+
- Well-structured readable code with proper reproducibility
23+
weaknesses:
24+
- Annotation font size (13pt) could be slightly larger for better readability at
25+
full resolution
26+
- Color palette is not perceptually uniform — mid-range retention values may be
27+
harder to distinguish
28+
- Some cohorts show very similar decay patterns limiting cross-cohort comparison
29+
insight
30+
image_description: The plot displays a triangular cohort retention heatmap with
31+
10 monthly cohorts (Jan 2024 – Oct 2024) on the y-axis and 10 periods (Month 0
32+
– Month 9) on the x-axis positioned at the top. Each cell shows bold retention
33+
percentages with a "%" suffix. The color scheme transitions from light tan/peach
34+
(low retention) through medium green to dark forest green (high retention), using
35+
a custom colormap. Cohort sizes (n=921 to n=2,438) are shown in parentheses next
36+
to each cohort label. The triangular shape is correctly formed — earlier cohorts
37+
have more columns. The background is light gray (#fafafa), all spines are removed,
38+
tick marks are hidden, and the colorbar on the right displays "Retention %" from
39+
0–100 with its outline removed. The title reads "heatmap-cohort-retention · seaborn
40+
· pyplots.ai" in medium weight.
41+
criteria_checklist:
42+
visual_quality:
43+
score: 28
44+
max: 30
45+
items:
46+
- id: VQ-01
47+
name: Text Legibility
48+
score: 7
49+
max: 8
50+
passed: true
51+
comment: All font sizes explicitly set (title 24pt, labels 20pt, ticks 16pt,
52+
annotations 13pt). Annotations slightly small but readable.
53+
- id: VQ-02
54+
name: No Overlap
55+
score: 6
56+
max: 6
57+
passed: true
58+
comment: No overlapping text. Clean cell spacing with linewidths=2.5.
59+
- id: VQ-03
60+
name: Element Visibility
61+
score: 6
62+
max: 6
63+
passed: true
64+
comment: Heatmap cells well-sized with clear separation and good contrast.
65+
- id: VQ-04
66+
name: Color Accessibility
67+
score: 3
68+
max: 4
69+
passed: true
70+
comment: Custom tan-to-green palette avoids red-green issues but is not perceptually
71+
uniform.
72+
- id: VQ-05
73+
name: Layout & Canvas
74+
score: 4
75+
max: 4
76+
passed: true
77+
comment: Plot fills canvas well, colorbar appropriately sized and positioned.
78+
- id: VQ-06
79+
name: Axis Labels & Title
80+
score: 2
81+
max: 2
82+
passed: true
83+
comment: 'Descriptive labels: Periods Since Signup, Signup Cohort.'
84+
design_excellence:
85+
score: 15
86+
max: 20
87+
items:
88+
- id: DE-01
89+
name: Aesthetic Sophistication
90+
score: 6
91+
max: 8
92+
passed: true
93+
comment: Custom colormap, custom background, styled colorbar — clearly above
94+
defaults.
95+
- id: DE-02
96+
name: Visual Refinement
97+
score: 5
98+
max: 6
99+
passed: true
100+
comment: All spines removed, tick marks hidden, custom background, colorbar
101+
outline removed.
102+
- id: DE-03
103+
name: Data Storytelling
104+
score: 4
105+
max: 6
106+
passed: true
107+
comment: Triangular shape communicates cohort narrative, color intensity shows
108+
retention decay.
109+
spec_compliance:
110+
score: 15
111+
max: 15
112+
items:
113+
- id: SC-01
114+
name: Plot Type
115+
score: 5
116+
max: 5
117+
passed: true
118+
comment: Correct triangular cohort retention heatmap.
119+
- id: SC-02
120+
name: Required Features
121+
score: 4
122+
max: 4
123+
passed: true
124+
comment: 'All spec features present: triangular shape, Period 0 = 100%, colormap,
125+
annotations, cohort sizes, Month N labels, colorbar.'
126+
- id: SC-03
127+
name: Data Mapping
128+
score: 3
129+
max: 3
130+
passed: true
131+
comment: Cohorts on Y, periods on X, retention as color. Correct.
132+
- id: SC-04
133+
name: Title & Legend
134+
score: 3
135+
max: 3
136+
passed: true
137+
comment: Title format correct. Colorbar serves as legend.
138+
data_quality:
139+
score: 14
140+
max: 15
141+
items:
142+
- id: DQ-01
143+
name: Feature Coverage
144+
score: 5
145+
max: 6
146+
passed: true
147+
comment: Shows varying decay rates, different cohort sizes, full triangular
148+
pattern. Some similar decay profiles.
149+
- id: DQ-02
150+
name: Realistic Context
151+
score: 5
152+
max: 5
153+
passed: true
154+
comment: Monthly SaaS cohort retention — real-world neutral business scenario.
155+
- id: DQ-03
156+
name: Appropriate Scale
157+
score: 4
158+
max: 4
159+
passed: true
160+
comment: Realistic retention values and cohort sizes.
161+
code_quality:
162+
score: 10
163+
max: 10
164+
items:
165+
- id: CQ-01
166+
name: KISS Structure
167+
score: 3
168+
max: 3
169+
passed: true
170+
comment: 'Linear flow: imports, data, plot, save. No functions or classes.'
171+
- id: CQ-02
172+
name: Reproducibility
173+
score: 2
174+
max: 2
175+
passed: true
176+
comment: np.random.seed(42) set.
177+
- id: CQ-03
178+
name: Clean Imports
179+
score: 2
180+
max: 2
181+
passed: true
182+
comment: All imports used.
183+
- id: CQ-04
184+
name: Code Elegance
185+
score: 2
186+
max: 2
187+
passed: true
188+
comment: Clean and Pythonic. Elegant .map() for annotations.
189+
- id: CQ-05
190+
name: Output & API
191+
score: 1
192+
max: 1
193+
passed: true
194+
comment: Saves as plot.png with dpi=300. No deprecated API.
195+
library_mastery:
196+
score: 8
197+
max: 10
198+
items:
199+
- id: LM-01
200+
name: Idiomatic Usage
201+
score: 5
202+
max: 5
203+
passed: true
204+
comment: Expert use of sns.heatmap with mask, annot, fmt, cbar_kws. Idiomatic
205+
set_context, set_style, despine.
206+
- id: LM-02
207+
name: Distinctive Features
208+
score: 3
209+
max: 5
210+
passed: true
211+
comment: Leverages seaborn-specific mask, built-in annotations, cbar_kws,
212+
sns.despine.
213+
verdict: APPROVED
214+
impl_tags:
215+
dependencies: []
216+
techniques:
217+
- colorbar
218+
- annotations
219+
patterns:
220+
- data-generation
221+
- matrix-construction
222+
dataprep: []
223+
styling:
224+
- custom-colormap
225+
- edge-highlighting

0 commit comments

Comments
 (0)