Deeplinkly

Glossary/Metrics and growth

Cohort analysis

Definition

Cohort analysis groups users by a shared starting event, usually their install date, and measures each group separately over time so that changes in behaviour can be separated from changes in acquisition mix.

It exists to solve one problem: aggregate metrics move for two completely different reasons, and you cannot tell which from the aggregate. A drop in daily active users can mean the product got worse or that you simply acquired fewer users last month. Cohorts separate those, because each row follows a fixed group of people whose composition never changes.

Reading a cohort table

The canonical output is triangular: one row per cohort, one column per period since that cohort started, and progressively fewer filled cells as you move down toward cohorts that have not lived long enough to reach the later columns.

Classic retention by weekly install cohort. Blank cells are periods that have not happened yet, not zeros.
CohortSizeD1D7D14D30D60
Week of 2 Jun8,41227.1%11.0%8.4%5.1%3.6%
Week of 9 Jun9,07726.6%10.7%8.1%4.9%3.4%
Week of 16 Jun14,90319.2%6.1%4.0%2.2%1.4%
Week of 23 Jun9,54026.9%10.9%8.3%5.0%
Week of 30 Jun9,88128.4%12.2%9.1%
Week of 7 Jul10,20428.8%12.6%

Read down a column to compare cohorts at the same age: this is the comparison that tells you whether the product or the traffic changed. Read across a row to see one group's decay curve. The two readings answer different questions and mixing them is the most common misinterpretation of the format.

The table above contains one obvious event. The week of 16 June is 60% larger than its neighbours and retains at roughly two-thirds of their rate at every column, then everything returns to normal the following week. That is a volume spike of low-quality traffic — a burst campaign, an incentivised placement, or something worth checking against click spamming. Crucially, the blended monthly retention figure for June would show a mild decline and hide the cause entirely.

What to cohort on

Install date is the default, not the only option. The starting event should be whatever moment you are trying to reason about, and changing it changes which question the table answers.

Cohort definitions and the question each is built to answer.
Cohort byQuestion answeredWatch out for
Install dateIs acquisition quality changing over time?Mixes all sources unless split further
Install date × sourceWhich channels bring users who stay?Small cells get noisy fast
First purchase dateDoes early monetisation predict retention?Survivorship — everyone here already converted
Feature adoption dateDoes this feature create habit?Self-selection; adopters were already engaged
App version at installDid this release change onboarding?Staged rollouts mix versions within a date
Acquisition campaignWhich creative brings durable users?Campaign renames break the grouping

Feature-adoption cohorts almost always overstate the feature

Users who adopt a feature were more engaged before they adopted it, so their better retention is partly the cause rather than the effect. A cohort table can show the correlation clearly and can never establish the direction on its own. If a decision depends on the direction, the experiment is the only answer.

Building the table

A weekly cohort table with a maturity guard on every cell
-- One row per cohort week, one column per day index.
-- Cells that have not matured are NULL, never 0 — a zero would be
-- read as churn and a NULL is read as "not yet known".
WITH cohort AS (
  SELECT
    user_id,
    DATE_TRUNC(DATE(installed_at), WEEK(MONDAY)) AS cohort_week,
    DATE(installed_at)                           AS install_date
  FROM installs
),
activity AS (
  SELECT DISTINCT
    c.user_id,
    c.cohort_week,
    DATE_DIFF(DATE(s.started_at), c.install_date, DAY) AS day_n
  FROM cohort c
  JOIN sessions s USING (user_id)
  WHERE s.is_foreground
),
sizes AS (
  SELECT cohort_week, COUNT(*) AS cohort_size FROM cohort GROUP BY cohort_week
)
SELECT
  z.cohort_week,
  z.cohort_size,
  ROUND(100.0 * COUNT(DISTINCT IF(a.day_n = 1,  a.user_id, NULL)) / z.cohort_size, 1) AS d1,
  ROUND(100.0 * COUNT(DISTINCT IF(a.day_n = 7,  a.user_id, NULL)) / z.cohort_size, 1) AS d7,
  IF(z.cohort_week <= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY),
     ROUND(100.0 * COUNT(DISTINCT IF(a.day_n = 30, a.user_id, NULL)) / z.cohort_size, 1),
     NULL) AS d30
FROM sizes z
LEFT JOIN activity a USING (cohort_week)
GROUP BY z.cohort_week, z.cohort_size
ORDER BY z.cohort_week DESC;

The IF wrapper on the D30 column is the pattern to copy for every column. Rendering an immature cell as 0.0% produces a cohort table whose bottom-right corner always looks like a catastrophe, and teams learn to ignore the corner — which is where a real regression would first appear.

The four mistakes that make a cohort table lie

  1. Zero instead of null for immature cells. Invents churn that has not happened.
  2. Cohorts too small to read. A 200-user cohort at 6% is twelve people; weekly or monthly grouping is usually the fix.
  3. Blending sources in one row. A shifting acquisition mix moves the row for reasons that have nothing to do with the product.
  4. Changing the activity definition mid-history. Every row before the change becomes incomparable, and nothing in the table records that it happened.

The third is worth restating because it is the most expensive. A cohort table split only by date, for an app whose channel mix changed, measures channel mix. Adding a source dimension turns the same data into a decision: which channels to keep buying — and paired with spend, it is also where effective CPI becomes a payback question rather than a cost question.

Finally, decide the retention definition before building the table and write it into the title. A cohort table is a grid of retention numbers, so every ambiguity in the underlying definition is reproduced in every one of its cells.

Free developer tools

The most useful cohort table is split by acquisition source, which requires campaign labels that stay consistent and links that land every cohort in the same place. Our free tools cover that link layer — campaign tagging, deep link routing and verification — so the rows differ by traffic rather than by tagging drift.

Open the free developer tools

Frequently asked questions

What is cohort analysis?
It is the practice of grouping users by a shared starting event, usually install date, and following each group separately over time. Because a cohort's membership never changes, movements in its metrics reflect behaviour rather than a shifting mix of users, which is what makes it possible to separate a product change from an acquisition change.
How do you read a cohort table?
Read down a column to compare different cohorts at the same age, which reveals whether product or traffic quality is changing. Read across a row to see a single cohort's decay curve over time. The two directions answer different questions, and the empty cells in the lower right are periods that have not happened yet rather than zeros.
What should I cohort users by?
Install date is the default, but the starting event should match the question. Cohorting by install date and acquisition source together answers which channels bring durable users; by app version answers whether a release changed onboarding; by feature adoption answers whether a feature correlates with habit, though self-selection means it cannot establish causation.
Why does the bottom of my cohort table look terrible?
Almost always because immature cells are rendered as zero instead of null. A cohort installed ten days ago has no D30 value, but a query without a maturity guard will compute one from incomplete data and display it as near-zero retention. Show those cells as blank so a genuine regression in recent cohorts remains visible.
How large does a cohort need to be?
Large enough that the metric you are reading is not dominated by a handful of users. At six percent D7, a 200-user cohort represents twelve people, so a swing of three users moves the number by more than a percentage point. Weekly or monthly cohorts are the standard remedy at lower install volumes.

Related terms

  • Retention rateRetention rate is the percentage of a cohort of users who are still active after a defined period, measured from a fixed starting event such as install.
  • N-day retentionN-day retention is the percentage of a cohort that is active on the Nth day after their starting event, where day zero is the day of the starting event itself.
  • eCPIEffective cost per install is total marketing spend divided by all installs attributable to that spend, including organic uplift and referred installs, rather than only the installs a network claims.
  • Classic retentionClassic retention is the percentage of a cohort that is active on day N exactly, counting only activity on that specific day and ignoring activity on any other day.