Cohort Analysis Explained with Examples

Cohort analysis explained with a worked example on real online shop transactions: grouping customers by the month they first bought, building a retention table in pandas, reading it along rows, columns and diagonals, and two traps it exposes: a cohort that only looks loyal and a spike that is really the calendar.

GloryTecks Team

Training & Placement Faculty

3 March 2026 6 min read

Last updated: 25 September 2026

Share:

A single retention number, such as "20% of customers came back", hides the most important question: is it getting better or worse? Customers who arrived in different months may behave very differently, and an overall average blends them together. Cohort analysis separates them. A cohort is a group of users who share a characteristic, most often the date they were acquired (Google Analytics: cohort exploration), and following each cohort over time shows whether newer customers stay better or worse than older ones.

This guide builds a retention table from a real UK online gift retailer's transactions (UCI: Online Retail) and shows how to read it, including two traps the example exposes.

Step 1: Assign every customer to a cohort

The cohort is the month of each customer's first purchase. Activity is recorded as one row per customer per month in which they bought, and the key column is the number of months since the cohort month:

python
import io
import urllib.request
import zipfile
import pandas as pd

url = "https://archive.ics.uci.edu/static/public/352/online+retail.zip"
with zipfile.ZipFile(io.BytesIO(urllib.request.urlopen(url).read())) as archive:
    raw = pd.read_excel(archive.open("Online Retail.xlsx"), dtype={"InvoiceNo": str, "StockCode": str})
services = {"POST", "DOT", "M", "m", "C2", "D", "S", "B", "BANK CHARGES", "AMAZONFEE", "CRUK"}
sales = raw[~raw["InvoiceNo"].str.startswith("C") & ~raw["StockCode"].isin(services)
            & ~raw["StockCode"].str.startswith("gift_") & (raw["Quantity"] > 0) & (raw["UnitPrice"] > 0)
            & raw["CustomerID"].notna()]
active = pd.DataFrame({"customer": sales["CustomerID"].astype(int),
                       "month": sales["InvoiceDate"].dt.to_period("M")}).drop_duplicates()
active = active[active["month"] <= pd.Period("2011-11", "M")]             # complete months only
active["cohort"] = active.groupby("customer")["month"].transform("min")    # month of first purchase
active["months_since"] = (active["month"] - active["cohort"]).apply(lambda offset: offset.n)
print("customers first seen per month:",
      active[active["months_since"] == 0].groupby("cohort").size().head(4).rename(index=str).to_dict())
text
customers first seen per month: {'2010-12': 884, '2011-01': 416, '2011-02': 380, '2011-03': 452}

Three preparation choices matter. Only purchases with a customer ID can be followed, so anonymous orders are excluded. Cancellations, postage and fees are removed, so "active" means a real purchase. And December 2011 is dropped because the data stops on 9 December, which would make that month look like a collapse.

Step 2: Build the retention table

Count the active customers in each cohort for each month since their first purchase, and divide by the cohort's size:

python
counts = active.groupby(["cohort", "months_since"])["customer"].nunique().unstack()
retention = counts.div(counts[0], axis=0)
print(retention.loc[:"2011-06", 0:5].round(2).to_string())
text
months_since    0     1     2     3     4     5
cohort                                         
2010-12       1.0  0.37  0.32  0.38  0.36  0.40
2011-01       1.0  0.22  0.27  0.23  0.32  0.29
2011-02       1.0  0.19  0.19  0.29  0.27  0.24
2011-03       1.0  0.15  0.25  0.20  0.22  0.17
2011-04       1.0  0.21  0.20  0.21  0.20  0.23
2011-05       1.0  0.19  0.17  0.17  0.21  0.23
2011-06       1.0  0.17  0.16  0.26  0.23  0.33

Each row is a cohort; each column is a number of months after first purchase; each cell is the share of the cohort that bought in that month. Month 0 is always 100% by definition. This is "classic" or "N-month" retention: a customer counts in any month they are active, even after skipping months. Tools such as Mixpanel present the same idea as a retention report with one row per cohort (Mixpanel: retention).

Step 3: Read it three ways

Along a row: how one cohort behaves over time. The January cohort returns at 22% to 32% in each following month, a fairly stable core of repeat buyers rather than a steady decline.

Down a column: how cohorts compare at the same age. One month after their first purchase, 15% to 22% of the 2011 cohorts came back. If a column trends down over time, newer customers are worse at returning, an early warning that acquisition quality or the first experience is slipping. Here the column is broadly flat.

Along a diagonal: cells on the same diagonal fall in the same calendar month. The June cohort's month 5 is November, and it jumps to 33%. Extending the table beyond the columns shown, November is the best month after the first purchase for nine of the first ten cohorts. Calendar effects such as seasons, promotions or outages show up as diagonals, not rows.

Trap 1: the cohort that only looks loyal

The December 2010 cohort retains 32% to 40% every month, almost double the others. Before celebrating, ask how it was formed. The data starts on 1 December 2010, so "first purchase in December 2010" really means "first purchase we can see". That cohort includes every long-standing customer who happened to buy in December, not just new ones, and loyal customers naturally return more. It is also the largest cohort, 884 customers against 380 to 452 in the following months, another sign it is not a normal acquisition month. Exclude or flag any cohort that begins where your data begins.

Trap 2: the spike that is really the calendar

The June cohort's jump to 33% at month 5 is not a sign that June customers became more loyal in their fifth month. November is the pre-Christmas peak for a gift retailer, and every cohort active that month is lifted. Read diagonals before explaining spikes by cohort age, and compare like-for-like calendar months across years when you have them.

Choices that change the numbers

  • Cohort definition: first purchase, sign-up, first use of a feature or a campaign. Choose the one tied to your question.
  • Period length: days or weeks for apps used daily, months for shopping, quarters for business purchases.
  • Retention definition: active in that exact period, as here, or still active in that period or any later one, often called unbounded or rolling retention. Say which you use.
  • What counts as active: a purchase, a login, or a meaningful action. A login-based measure can look healthy while buying declines.
  • Revenue cohorts: tracking revenue per original customer instead of counts shows whether returning customers also spend more.

Where cohorts fit

Cohort tables answer "do customers come back?" Funnels answer "where do users drop out on the way to converting?" and segmentation answers "which kinds of customers behave differently?" Used together, they explain most of what happens to customers after they arrive. Product analytics 101 shows how the three connect, and customer segmentation for analysts covers the third.

What is a good retention rate?

It depends on the product and period. A monthly gift shop, a daily messaging app and an annual software licence have completely different norms. Compare cohorts with each other and with your own history first.

How is cohort analysis different from churn rate?

A churn rate is one number for everyone in a period. Cohort analysis keeps groups separate, so you can see whether newer customers behave differently from older ones.

Can I do cohort analysis in Excel or SQL?

Yes. Assign each customer a first-purchase month, compute months since then for each activity, and pivot counts of distinct customers by cohort and month. The logic is the same as the pandas code above.

How many cohorts do I need?

Enough to see a trend down the columns, usually at least six periods, with enough customers in each that one or two people do not swing the percentages.

Customer segmentation, business reporting and Python for data analysis are part of the Data Analytics course.

Sources

GloryTecks Team

Training & Placement Faculty

The GloryTecks faculty team mentors learners across Data Science, AI and Data Engineering, with a focus on real-time projects and placement readiness in Hyderabad.

Want to master Data Analytics?

Join GloryTecks’ expert-led Data Analytics training in Hyderabad with real-time projects and 100% placement support.

Explore course