33 — Data Analytics

Cohort & Retention Analysis#

Analytics✓ Mathematical
◆ The PatternGroup users by when they arrived, track them over time — the retention heatmap

Averages hide everything in user analytics: growth pumps new users into the numerator and masks that older users are quietly leaving. Cohort analysis fixes this by grouping users by their start month and tracking each cohort separately. The classic output is the retention triangle — rows are cohorts, columns are months since signup, cells are the share still active.

retention(c, t) = active(c, t) / size(c)
Retention = share of cohort c still active t periods after joining. Read down a column to compare cohorts at the same age.
churn = 1 − retention  ·  LTV ≈ ARPU / churn
Churn is retention's complement — and the denominator of the standard lifetime-value approximation.
// Interactive retention triangle — set churn, then let newer cohorts improve
Monthly churn25%
Newer cohorts improve0%
Month-3 retention (latest)
# Python — retention triangle in pandas
import pandas as pd

# events: user_id, event_date
events['month'] = events['event_date'].dt.to_period('M')
events['cohort'] = events.groupby('user_id')['month'].transform('min')
events['age'] = (events['month'] - events['cohort']).apply(lambda p: p.n)

counts = (events.drop_duplicates(['user_id', 'month'])
                .pivot_table(index='cohort', columns='age',
                             values='user_id', aggfunc='nunique'))

retention = counts.div(counts[0], axis=0)   # normalize by cohort size
print((retention * 100).round(1))
Read the triangleMeaning
Down a columnSame-age comparison — are newer cohorts retaining better? (Product is improving)
Along a rowOne cohort's decay curve — where is the steepest drop?
Flattening rowsA retained core exists — the curve's plateau is your product-market-fit signal
Rows hitting zeroLeaky bucket — growth is refilling, not compounding
Define "active" first. Logged in? Performed the core action? Paid? Retention numbers are meaningless without a stated activity definition and period — and comparisons across products even more so.
Pattern bridge: Averaging metrics over current users only is survivorship bias — cohort analysis is the antidote, because the churned users stay in their cohort's denominator.
When to use this
Use when: Any subscription or repeat-use product — separating growth from stickiness, judging whether product changes moved long-term behaviour, computing honest LTV.
Skip when: One-shot transactions with no expected repeat behaviour, or cohorts too small for stable percentages (a 20-user cohort moves 5 points per person).
Try it on real data
UCI: Online Retail (540K transactions — build real purchase cohorts)
Assign each customer to their first-invoice month, pivot by months-since, and you have a genuine retention triangle from real e-commerce data.
Quick start — copy to notebook
pip install pandas numpy
# ────────────────────────────────────────
import numpy as np
import pandas as pd

rng = np.random.default_rng(5)
rows = []
for cohort in range(6):                      # signup month
    months_active = rng.geometric(0.25, 500).clip(max=8 - cohort)
    for uid, life in enumerate(months_active):
        rows += [(f"{cohort}-{uid}", cohort, cohort + m)
                 for m in range(life)]

ev = pd.DataFrame(rows, columns=['user_id', 'cohort', 'month'])
ev['age'] = ev['month'] - ev['cohort']
counts = ev.pivot_table(index='cohort', columns='age',
                        values='user_id', aggfunc='nunique')
print((counts.div(counts[0], axis=0) * 100).round(1))
← Previous
GroupBy, Pivot & Aggregation
Open in the full reader, with the topic sidebar →