# Churn cohort retention

Build monthly, weekly, or daily cohort retention and churn tables from signup and activity CSV data, with weighted averages and export formats.

## Run it

- **CLI:** `gizza tool churn-cohort-retention "user,date
u1,2024-01-05
u1,2024-02-03
u2,2024-01-20
u2,2024-03-02
u3,2024-02-10"`
- **Web:** https://gizza.ai/tools/churn-cohort-retention/
- **Agents:** machine-readable descriptor (parameters JSON Schema) at https://gizza.ai/tools/churn-cohort-retention/tool.json

## Inputs

- `data` — Activity events CSV _(field)_
- `signups` — Signup/users CSV (optional) _(field)_
- `user` — User id column _(field)_
- `date` — Activity date column _(field)_
- `signup_date` — Signup date column _(field)_
- `granularity` — Cohort period _(field)_
- `periods` — Follow-up periods _(field)_
- `metric` — Metric _(field)_
- `values` — Cell values _(field)_
- `as_of` — Analysis date (optional) _(field)_
- `header` — First row contains headers _(field)_
- `delimiter` — Delimiter _(field)_
- `format` — Output _(field)_

## Output

- Cohort table (text)

## Query parameters

Open the tool pre-filled and auto-run via URL:

- `data` — Activity events CSV
- `signups` — Signup/users CSV (optional)
- `user` — User id column
- `date` — Activity date column
- `signup_date` — Signup date column
- `granularity` — Cohort period
- `periods` — Follow-up periods
- `metric` — Metric
- `values` — Cell values
- `as_of` — Analysis date (optional)
- `header` — First row contains headers
- `delimiter` — Delimiter
- `format` — Output

Example: `https://gizza.ai/tools/churn-cohort-retention/?data=user%2Cdate%0Au1%2C2024-01-05%0Au1%2C2024-02-03%0Au2%2C2024-01-20%0Au2%2C2024-03-02%0Au3%2C2024-02-10&signups=user%2Csignup_date%0Au1%2C2024-01-01%0Au2%2C2024-01-15%0Au3%2C2024-02-01&user=user&date=date&signup_date=signup_date&granularity=month&periods=6&metric=retention&values=percent&as_of=2024-06-30&header=true&delimiter=comma&format=table`

---

## About this tool

Cohort analysis answers a different question from a single churn percentage: users who joined in
January may behave differently from users who joined in March. This tool starts from raw activity
rows, assigns each user to a signup cohort, and builds a cohort-by-period grid so you can compare
retention or churn at the same age across cohorts.

Paste an activity table with a user id and activity date. If you also paste a signup/users table,
cohorts come from the signup date; otherwise each user's first activity becomes their cohort date.
Choose monthly, weekly, or daily buckets, how many follow-up periods to display, whether cells show
percentages, counts, or both, and whether to output a readable table, CSV, or JSON. Cells that a
cohort has not aged into yet are shown as `-` rather than a misleading zero.

### Worked example

Activity events:

```csv
user,date
u1,2024-01-05
u1,2024-02-03
u1,2024-03-01
u2,2024-01-20
u2,2024-03-02
u3,2024-02-10
u3,2024-03-11
```

With **Cohort period** set to monthly, **Follow-up periods** set to `3`, and **Cell values** set to
counts and percent, the January cohort starts at P0 with two users, P1 shows the users active one
month later, and the weighted average row combines every cohort old enough to observe each period.
Switch **Metric** to churn to see period-over-period losses instead of retained users.

### Limits and edge cases

- Activity and signup inputs are capped at **50,000 rows** each.
- At most **1,000 cohorts** are rendered; use month/week granularity for long histories.
- **Follow-up periods** accepts **1–36**.
- Dates must be ISO-like (`YYYY-MM-DD`, an ISO timestamp, `YYYY-MM`, `YYYYMMDD`) or Unix epoch
  seconds/milliseconds. Ambiguous `03/04/2024` style dates are rejected instead of guessed.
- The tool treats a user as active in a period if they have at least one activity row in that period.
  Define "active" upstream before pasting the data.
- Activity before a user's signup date is ignored and reported in the notes.
- Users in the signup table with no activity are still included in the cohort size, so P0 may be less
  than 100%.
- This is user retention, not revenue retention. It does not compute MRR, NDR, GDR, LTV, SQL queries,
  saved dashboards, or benchmark overlays.

## FAQ

<details>
<summary>Do I need a separate signup table?</summary>

No. If **Signup/users CSV** is blank, each user's first activity date defines the cohort. Paste a
signup table when you want users with no activity to count in the denominator, or when signup and
first activity are different events.

</details>

<details>
<summary>What is P0?</summary>

P0 is the signup period itself: the month, week, or day containing the signup date. P1 is one full
period later, P2 is two periods later, and so on. Comparing P1 across cohorts is usually more useful
than comparing calendar months directly.

</details>

<details>
<summary>How is churn calculated?</summary>

Churn is period-over-period loss: users active in the previous observable period minus users active
in the current period, divided by the previous period's active users. If users come back after being
inactive, churn can be negative for that step.

</details>

<details>
<summary>Why are some cells a dash?</summary>

A dash means the cohort is not old enough to observe that period as of the analysis date. For
example, a March cohort cannot have P3 retention in April. Set **Analysis date** to reproduce a
specific reporting cut; otherwise the latest date in the input is used.

</details>

<details>
<summary>Is my user data uploaded?</summary>

No. The parser and retention calculations run in WebAssembly inside the browser. For the CLI, the
same deterministic Rust core runs locally. No account, warehouse connection, or remote service is
used.

</details>

## Related tools

- [Absolute value, sign, or negation for a whole column](https://gizza.ai/tools/absolute-value-transformer/): Paste a column of numbers and apply absolute value, sign extraction (-1/0/1), sign flipping, or force-negative to every value at once, with rounding and an audit table.
- [Adjacency Matrix Converter](https://gizza.ai/tools/adjacency-matrix-converter/): Convert a graph between edge list, adjacency matrix, and incidence matrix — directed or undirected, weighted or not. Free, private, runs in your browser.
- [Amazon Order Analyzer](https://gizza.ai/tools/amazon-order-analyzer/): Paste an Amazon order-history CSV export to summarize total spend by month, top items, and category breakdowns. Browser-only, private, with Markdown or JSON output.
- [ARFF Converter](https://gizza.ai/tools/arff-converter/): Convert Weka ARFF datasets to CSV and CSV tables back to ARFF locally — nominal attributes, numeric types, dates, sparse rows, missing values, and type rows.
- [Avro to JSON Converter](https://gizza.ai/tools/avro-to-json/): Decode Apache Avro Object Container Files (.avro / OCF) to JSON, NDJSON, or the embedded schema — no .avsc needed, free and private in your browser.
