Skip to Content
Course content

103: Cohort Analysis with R

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I was working with a team on a subscription-based SaaS tool. Our main dashboard showed a steady, upward climb in total active users, and the leadership team was thrilled. They thought we had hit a goldmine. However, I had a nagging feeling that the aggregate numbers were lying to us. I decided to stop looking at the "total" and instead broke the users into groups based on when they joined. What I found was sobering: our users from six months ago had almost entirely vanished, and we were only staying afloat because a massive new marketing spend was pumping in fresh sign-ups. We weren't growing; we were just filling a leaky bucket. That's the power of cohort analysis—it strips away the noise of aggregate growth to show you if your product is actually retaining people.

At its core, cohort analysis is about grouping users who share a common characteristic—usually their acquisition date—and tracking their behavior over a set period. In R, this mostly involves some clever data manipulation with dplyr and lubridate to transform a raw list of events into a matrix of retention rates.

Transforming Transaction Logs into Cohorts

To get started, you can't just use your raw event log. You need to establish two things for every single transaction: when the user first appeared (their cohort) and how much time has elapsed since that first appearance (their period). I usually handle this by creating a "cohort month" column that floors every date to the first of the month.

library(dplyr)
library(lubridate)
library(tidyr)
library(ggplot2)

# Assuming 'df' has user_id and order_date
cohort_data <- df %>%
mutate(order_month = floor_date(order_date, "month")) %>%
group_by(user_id) %>%
summarise(cohort_month = min(order_month)) %>%
right_join(df, by = "user_id") %>%
mutate(cohort_index = interval(cohort_month, order_month) / months(1))

The cohort_index is the secret sauce here. Instead of dealing with calendar dates, we're now dealing with "Month 0," "Month 1," and so on. This allows us to compare a user who joined in January to a user who joined in June on an equal playing field.

Calculating Retention Rates

Once you have your index, you need to count how many unique users from each cohort returned in each subsequent month. You'll likely end up with a long-format table, but for a cohort analysis, we typically want a wide-format matrix where the rows are the cohorts and the columns are the months of tenure. I've found that pivot_wider is the cleanest way to handle this transition.

The trick is to divide the number of returning users by the original size of the cohort. If 100 people joined in January and 20 came back in February, your Month 1 retention is 20%. If you don't normalize by the initial cohort size, your data will be skewed by your growth rate, making it look like newer cohorts are "better" simply because they are larger.

Mapping the Decay with Heatmaps

A table of percentages is fine for a report, but it's terrible for spotting patterns. To actually "see" the churn, you want a heatmap. Using ggplot2 with geom_tile allows you to visualize the retention decay. I always recommend using a color scale that emphasizes the drop-off—something like "Blues" or "Viridis."

ggplot(cohort_matrix, aes(x = cohort_index, y = cohort_month, fill = retention_rate)) +
geom_tile() +
scale_fill_gradient(low = "white", high = "steelblue") +
labs(title = "User Retention by Cohort", x = "Month Index", y = "Cohort") +
theme_minimal()

When you look at this map, you're looking for "vertical stripes" or "horizontal shifts." If every single cohort drops off sharply at Month 2, you probably have an onboarding problem. If newer cohorts are retaining better than older ones, your recent product updates are likely working.




📋 Practical Task

Building a Retention Matrix for "ElectroShop"

You have been provided with a dataset called electro_sales containing two columns: customer_id and purchase_date. Your task is to build a cohort analysis pipeline to determine the health of the customer base.

Requirements:

  • Create a cohort_month column based on the customer's first purchase.
  • Calculate a cohort_index representing the number of months since the first purchase.
  • Compute the retention rate for each cohort across the indices (number of active users in month n divided by the total users in the original cohort).
  • Produce a final data frame where the rows are cohort_month, the columns are cohort_index, and the values are the retention percentages.

Starter Data:

electro_sales <- data.frame(
customer_id = c(1, 1, 1, 2, 2, 3, 3, 4, 5, 5),
purchase_date = as.Date(c('2023-01-15', '2023-02-10', '2023-03-05',
'2023-01-20', '2023-03-15', '2023-02-10',
'2023-02-25', '2023-03-01', '2023-03-10', '2023-04-12'))
)
Rating
0 0

There are no comments for now.

to be the first to leave a comment.