Calculate Customer Churn Metrics
Churn results when a customer cancels their subscription or relationship with a company. Preventing customer churn is critical, and some estimates suggest that acquiring a new customer can be as much as 25 times as costly as keeping an existing one.
The first step to reducing churn is understanding the extent to which your customers are churning. This template will cover three churn metrics you can use to know how many users and how much revenue you are losing to churn. It is appropriate for subscription data or other forms of recurring revenue data.
Imports and data preparation
The following cell imports the packages necessary to import and manipulate user payment data. It also loads and prepares the example data.
The example data in this template contains the payment date, user id, and price paid for a subscription service.
👇 To use your data, you can:
- Upload a file and update the
path
variable. You will then need to perform the following steps:- Set the column that contains the date/time the payment was made.
- Update the date offset below if you want to use a period other than monthly.
- Connect to a database using a custom integration and perform the required preprocessing to convert payment or activity dates to your period of choice.
# Import packages
import numpy as np
import pandas as pd
import plotly.express as px
# Set the path to your file
path = "data/payments.csv"
# Set your date column
date_col = "payment_date"
# Import churn data
payments = pd.read_csv(path, parse_dates=[date_col])
# Categorize payment periods (optional)
payments["payment_period"] = pd.to_datetime(payments[date_col]) + pd.offsets.MonthBegin(-1)
# Preview the data
payments
Net retention rate
Calculating the net retention rate
The first churn metric that we will calculate is the net retention rate. The net retention rate is the percentage of recurring revenue that comes from customers who were subscribed at the start of a period.
Note that this calculation does not include any revenue from new subscribers. It does include the effect of churns, upsells (e.g., customers upgrading their subscription), and down sells (e.g., customers reducing their subscription tier). Because it includes upsells, the net retention rate is a less specific measure of churn than other measures. It is, however, a good overall metric that captures churn, upsells, and down sells that can be easily interpreted by stakeholders (such as investors).
💡 In this and the following cells that define the metric functions, you will need to update the DateOffset()
if you wish to use a period other than monthly.
# Define a function to calculate the net retention rate
def nrr(x, dataframe):
# Identify current and previous period based on group
current_period = x["payment_period"].max()
previous_period = current_period - pd.DateOffset(months=1)
# Calculate the previous period's recurring revenue
previous_period_rr = dataframe[dataframe["payment_period"] == previous_period]["price"].sum()
# Identify the users in the previous period
previous_period_users = dataframe[dataframe["payment_period"] == previous_period]["userid"].unique()
# Calculate the current period's recurring revenue that comes from the previous periods's users
current_period_retained_rr = dataframe[
(dataframe["userid"].isin(previous_period_users))
& (dataframe["payment_period"] == current_period)
]["price"].sum()
# Caculate and return the net retention rate
try:
nrr = current_period_retained_rr / previous_period_rr
return nrr
except:
return None
# Group the data by payment period and calculate the net retention rate
nrr_data = (
payments.groupby("payment_period")
.apply(nrr, dataframe=payments)
.reset_index(name="nrr")
)
# Review the data
nrr_data
Note: You may notice low retention and high churn rates in the final month of data (February 2017). This is because the data is incomplete for this final period.
Plotting the net retention rate
We can plot the net retention rate using a line chart from Plotly, which provides an interactive visualization of each period.
fig = px.line(
nrr_data,
x="payment_period",
y="nrr",
title="Net Retention Rate by Period"
)
fig.show()
Standard account churn
Calculating standard account churn
The next churn metric we will calculate is "standard account churn". Standard account churn is the proportion of customers who cancel to the total number of customers at the start of a period. A customer is only considered a churn if they cancel all subscriptions, and therefore a down sell or reduction in the number of subscriptions a customer holds does not constitute churn.
This metric does not consider upsells and down sells and provides an easy-to-interpret measure of churn. Standard account churn is a good metric to use when you have a free service or when all customers pay the same price.
# Define a function to calculate the standard account churn
def std_churn(x, dataframe):
# Identify current and previous period based on group
current_period = x["payment_period"].max()
previous_period = current_period - pd.DateOffset(months=1)
# Calculate the previous period's total number of users
previous_period_user_count = dataframe[
dataframe["payment_period"] == previous_period
]["userid"].nunique()
# Identify the users in the previous and current periods
previous_period_users = dataframe[dataframe["payment_period"] == previous_period]["userid"].unique()
current_period_users = dataframe[dataframe["payment_period"] == current_period]["userid"].unique()
# Calculate the number of churned users
churns = len(np.setdiff1d(previous_period_users, current_period_users))
# Caculate and return the standard churn
try:
std_churn = churns / previous_period_user_count
return std_churn
except:
return None
# Group the data by payment period and calculate the net retention rate
std_churn_data = (
payments.groupby("payment_period")
.apply(std_churn, dataframe=payments)
.reset_index(name="std_churn")
)
# Review the data
std_churn_data
Plotting the standard account churn
We can can again plot the churn by using a Plotly line chart.
fig = px.line(
std_churn_data,
x="payment_period",
y="std_churn",
title="Standard Account Churn by Period",
)
fig.show()
Monthly recurring revenue churn
Calculating monthly recurring revenue churn
The final churn metric to cover is "monthly recurring revenue", a variant of net retention rate. Whereas net retention rate includes upsells, monthly recurring revenue does not, as technically, these do not constitute churn.
Monthly recurring revenue churn is a useful metric if you have a variety of different prices (i.e., different pricing tiers). It is considered a more accurate definition of churn because it focuses exclusively on down sells and total churn.
# Define a function to calculate the periodly recurring revenue churn
def mrr(x, dataframe):
# Identify current and previous period based on group
current_period = x["payment_period"].max()
previous_period = current_period - pd.DateOffset(months=1)
# Identify total recurring revenue from previous period
previous_period_rr = dataframe[dataframe["payment_period"] == previous_period]["price"].sum()
# Identify the users in the previous and current periods
previous_period_users = dataframe[dataframe["payment_period"] == previous_period]["userid"].unique()
current_period_users = dataframe[dataframe["payment_period"] == current_period]["userid"].unique()
# Identify retained and churned users
retained_users = np.intersect1d(previous_period_users, current_period_users)
churned_users = np.setdiff1d(previous_period_users, current_period_users)
# Identify revenue of retained users in each period
current_period_retained_rr = dataframe[
(dataframe["userid"].isin(retained_users))
& (dataframe["payment_period"] == current_period)
]["price"].values
previous_period_retained_rr = dataframe[
(dataframe["userid"].isin(retained_users))
& (dataframe["payment_period"] == previous_period)
]["price"].values
# Define mask to filter arrays for downsells
downsells = previous_period_retained_rr > current_period_retained_rr
# Calculate total downsell loss
downsell_loss = np.sum(
previous_period_retained_rr[downsells] - current_period_retained_rr[downsells]
)
# Calculate loss to users who churned
churn_loss = dataframe[
(dataframe["userid"].isin(churned_users))
& (dataframe["payment_period"] == previous_period)
]["price"].sum()
# Caculate and return the monthly recurring revenue
try:
mrr = (downsell_loss + churn_loss) / previous_period_rr
return mrr
except:
return None
# Group the data by payment period and calculate the monthly recurring revenue churn
mrr_data = (
payments.groupby("payment_period")
.apply(mrr, dataframe=payments)
.reset_index(name="mrr")
)
# Review the data
mrr_data
Plotting monthly recurring revenue churn
As with the previous metrics, we can use a Plotly line chart to plot the monthly recurring revenue churn.