Skip to content

Commit f79b391

Browse files
fix(pygal): address review feedback for heatmap-cohort-retention
Attempt 2/3 - fixes based on AI review
1 parent 4a0d133 commit f79b391

1 file changed

Lines changed: 24 additions & 7 deletions

File tree

  • plots/heatmap-cohort-retention/implementations

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

Lines changed: 24 additions & 7 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: pygal 3.1.0 | Python 3.14.3
44
Quality: 85/100 | Created: 2026-03-16
@@ -57,6 +57,18 @@ def _plot(self):
5757
if not self.matrix_data:
5858
return
5959

60+
# Inject CSS to suppress pygal's default focus/active/hover outlines
61+
defs_node = self.svg.node(self.svg.root, "defs")
62+
style_node = self.svg.node(defs_node, "style", type="text/css")
63+
style_node.text = (
64+
".cell rect, .cell { outline: none !important; stroke-opacity: 1; } "
65+
".cell:focus rect, .cell:hover rect, .cell:active rect, "
66+
".active .cell rect, .cell .active rect "
67+
"{ outline: none !important; } "
68+
"g.cell:focus, g.cell:active { outline: none; } "
69+
".activate-serie, .active .reactive { outline: none !important; }"
70+
)
71+
6072
n_rows = len(self.matrix_data)
6173
n_cols = max(len(row) for row in self.matrix_data)
6274

@@ -110,7 +122,7 @@ def _plot(self):
110122

111123
# Row labels with cohort sizes
112124
row_font_size = min(36, int(cell_height * 0.50))
113-
size_font_size = int(row_font_size * 0.78)
125+
size_font_size = int(row_font_size * 0.88)
114126
for i, label in enumerate(self.row_labels):
115127
ry = y_offset + i * (cell_height + gap) + cell_height / 2
116128
rx = x_offset - 20
@@ -155,10 +167,12 @@ def _plot(self):
155167
cy = y_offset + i * (cell_height + gap)
156168

157169
cell_group = self.svg.node(plot_node, "g", class_="cell")
170+
cell_group.set("style", "outline:none;stroke:none;")
158171
rect = self.svg.node(cell_group, "rect", x=cx, y=cy, width=cell_width, height=cell_height, rx=3, ry=3)
159172
rect.set("fill", color)
160173
rect.set("stroke", "#ffffff")
161174
rect.set("stroke-width", "2")
175+
rect.set("style", "outline:none;")
162176

163177
# Tooltip
164178
cohort_label = self.row_labels[i] if i < len(self.row_labels) else ""
@@ -253,9 +267,9 @@ def _compute(self):
253267
cohort_sizes = [1200, 1350, 980, 1520, 1100, 1430, 1280, 1050, 1380, 1150]
254268

255269
# Base retention curve that decays over time
256-
base_retention = np.array([100.0, 68.0, 52.0, 43.0, 37.0, 33.0, 30.0, 28.0, 26.5, 25.0])
270+
base_retention = np.array([100.0, 65.0, 48.0, 40.0, 34.0, 30.0, 27.0, 25.0, 23.5, 22.0])
257271

258-
# Build triangular retention matrix
272+
# Build triangular retention matrix with visible cohort variation
259273
matrix = []
260274
for i in range(n_cohorts):
261275
n_periods = n_max_periods - i
@@ -264,9 +278,12 @@ def _compute(self):
264278
if j == 0:
265279
row.append(100.0)
266280
else:
267-
# Add cohort-specific variation — later cohorts slightly better retention
268-
improvement = i * 0.8
269-
noise = np.random.uniform(-2.5, 2.5)
281+
# Later cohorts show progressively better retention (product improvements)
282+
improvement = i * 1.8
283+
# Apr 2024 (i=3) had a bad onboarding change — worse retention
284+
if i == 3:
285+
improvement = -4.0
286+
noise = np.random.uniform(-2.0, 2.0)
270287
val = base_retention[j] + improvement + noise
271288
val = max(5.0, min(100.0, val))
272289
row.append(round(val, 1))

0 commit comments

Comments
 (0)