Challenges · C04 · ★★★☆☆

Customer churn: most of your customers have not left yet

Difficulty
★★★☆☆
Time
3-4 hours
Data
IBM Telco churn sample: 7,043 customers with tenure, churn flag, contract and billing details
Skills
Right-censoring with pm.Censored · accelerated-failure-time regression with coords/dims and index variables · designing a posterior predictive check for censored data · Kaplan-Meier · LOO across survival families · conditional survival · turning a posterior into a targeting decision

The brief

The retention team of a telecom company has budget for a retention offer and two questions:

  1. How long do our customers last?
  2. Which of our current customers should get the offer?

A colleague has already answered the first one on a slide: "churned customers stayed 18 months on average". You suspect that number is badly wrong, because three quarters of the customers in the file have not churned at all - and they are not missing data, they are the customers who last longest.

How this notebook works

  • Each task states what to deliver, not how. Write your code in the YOUR CODE HERE cells.
  • Stuck? h.hint("task2") reveals hints one level at a time: nudge → approach → code skeleton. Try to get by on nudges.
  • h.check("task2", k_shared=...) compares your numbers with the reference solution.
  • A full worked solution lives in notebooks/solutions/. Open it only when you are done (or truly stuck).
  • Work through example E06 first: this challenge assumes you know what pm.Censored does.
import arviz as az
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pymc as pm
from scipy import stats

from pymc_challenges import Hints, data

RANDOM_SEED = 7043
rng = np.random.default_rng(RANDOM_SEED)
az.style.use("arviz-variat")

h = Hints("C04")
h.tasks()

C04 - Customer churn: most of your customers have not left yet

  • task0 - Frame churn as a time-to-event problem (3 hints, 2 check(s))
  • task1 - Put a number on the colleague's mistake (3 hints, 3 check(s))
  • task2 - A censored Weibull regression (3 hints, 2 check(s))
  • task3 - Design a check the model can fail (3 hints, 1 check(s))
  • task4 - Diagnose, fix, compare (3 hints, 3 check(s))
  • task5 - Conditional churn risk for active customers (3 hints, 2 check(s))
  • task6 - Who gets the offer? (3 hints, 2 check(s))

The data

One row per customer, taken at a single snapshot date. tenure is the number of months the customer has been (or was) with the company, Churn says whether they have left. The other columns describe the contract, the services booked and how the customer pays.

data.describe("telco")
telco = data.load("telco")
telco[["customerID", "tenure", "Churn", "Contract", "InternetService", "PaymentMethod",
       "MonthlyCharges", "TotalCharges"]].head()
telco: Tenure (months), churn flag and contract details for 7043 customers.
  source: IBM sample data, Telco customer churn.
  url:    https://raw.githubusercontent.com/IBM/telco-customer-churn-on-icp4d/master/data/Telco-Customer-Churn.csv
customerID tenure Churn Contract InternetService PaymentMethod MonthlyCharges TotalCharges
0 7590-VHVEG 1 No Month-to-month DSL Electronic check 29.85 29.85
1 5575-GNVDE 34 No One year DSL Mailed check 56.95 1889.5
2 3668-QPYBK 2 Yes Month-to-month DSL Mailed check 53.85 108.15
3 7795-CFOCW 45 No One year DSL Bank transfer (automatic) 42.30 1840.75
4 9237-HQITU 2 Yes Month-to-month Fiber optic Electronic check 70.70 151.65

Task 0 · Frame churn as a time-to-event problem

Deliver

  1. A clean analysis table. Two things need a decision you can defend in one sentence each: TotalCharges does not parse as a number, and some customers have tenure == 0.
  2. The framing: what is the "event", what is the "time", which rows are censored and at what value? What share of customers is censored - overall and per contract type?
  3. Kaplan-Meier survival curves per contract type in one figure. What do they tell you beyond "customers on long contracts churn less"? How far out can each curve be trusted?
Your turn Work this task in the notebook.

Task 1 · Put a number on the colleague's mistake

"Churned customers stayed 18 months on average, so a customer lasts about a year and a half."

Deliver

  1. Reproduce the number. Then give the approach its best shot: fit an intercept-only Weibull model to the churned customers alone and report the median customer lifetime and the probability that a new customer is still there after 24 months.
  2. The same model and the same two numbers, fitted to all customers with the censoring handled properly.
  3. Both survival curves on top of the overall Kaplan-Meier estimate. By what factor is the naive median off? And of your two "correct" numbers, which one rests on data and which is an extrapolation?
Your turn Work this task in the notebook.

Task 2 · A censored Weibull regression

Build an accelerated-failure-time model for tenure: a censored Weibull whose log-scale is linear in contract type, internet service, payment method and monthly charges, with a single shape parameter.

MonthlyCharges is largely determined by the internet service (check this). Enter it as the deviation from the average charge for the customer's internet service type, in units of $20, so that its coefficient reads "paying more than others with the same service".

Deliver

  1. The model with named dimensions and index variables (no dummy matrices). Think about how to keep three categorical predictors identified.
  2. Priors you can defend, with a prior predictive check on the scale that matters: survival curves.
  3. Clean diagnostics and a table of time ratios with 94% intervals: one-year and two-year contracts relative to month-to-month, plus the other covariates.
  4. One sentence: do you believe the two-year time ratio?
Your turn Work this task in the notebook.

Task 3 · Design a check the model can fail

az.plot_ppc_dist on tenure is close to meaningless here (why?). Design a posterior predictive check that respects the censoring and looks at the model by contract type.

Deliver

  1. The figure, with posterior uncertainty.
  2. A verdict per contract type: where, in which direction and by how much does the model miss?
  3. The model's predicted 12-month survival for the customers on one-year contracts, next to the model-free estimate.
Your turn Work this task in the notebook.

Task 4 · Diagnose, fix, compare

Deliver

  1. A diagnosis of the misfit in terms of the hazard: what is the model structurally unable to express? (Task 0 already showed you the answer.)
  2. A fix to the model - not to the sampler, and not more covariates - and the refit with clean diagnostics.
  3. At least one other survival family with the same fix applied.
  4. A PSIS-LOO comparison of all your models, and your calibration check from Task 3 for the contenders. Do the two agree about which model is best?
  5. What happened to the contract time ratios from Task 2, and why?
Your turn Work this task in the notebook.

Task 5 · From lifetimes to "who is at risk now?"

The retention team cannot act on customers who already left. For every active customer they need the probability of churning within the next 6 months, given that the customer has stayed until today.

Deliver

  1. The formula, and its posterior for every active customer under your final model.
  2. The average 6-month churn probability per contract type.
  3. A figure of this risk against current tenure, by contract type, for the final model and for the shared-shape model of Task 2. What did the fix change about who looks risky?
  4. A sanity check of this very quantity against the data: Kaplan-Meier can estimate "churn within 6 months given survival to $t$" too. Use it to decide which of your Task 4 contenders to carry into the decision.
Your turn Work this task in the notebook.

Task 6 · Who gets the offer?

The offer on the table, with the retention team's working assumptions:

OFFER_COST = 40.0        # dollars per customer who receives the offer
OFFER_EFFECT = 0.30      # assumed: the offer cuts the 6-month churn probability by 30% (relative)
VALUE_MONTHS = 12        # a customer saved from churning is worth 12 months of their MonthlyCharges

Deliver

  1. The posterior of the expected net value of making the offer, for every active customer.
  2. A ranked top-10 list with the uncertainty a manager needs to see.
  3. A targeting rule, the number of customers it selects, and the total expected profit with an interval. Compare it with the blanket policy "offer it to every month-to-month customer".
  4. Robustness: how much does the target list change under the runner-up model from Task 4? And which assumption would you most want to test before spending real money?
  5. Three sentences for the retention team.
Your turn Work this task in the notebook.

Going further

  • Month-to-month churn is a bathtub: a spike in month one, a long quiet period, and a rise again after four years. Neither family here can express that. Try a piecewise-constant hazard (a piecewise exponential model is a Poisson regression in disguise) and rerun the Task 5 check.
  • tenure is recorded in whole months, so a "churn at 1" is really a churn somewhere in (0, 1]. Treat events as interval-censored - pm.Censored will not do it, but a pm.Potential or pm.CustomDist on $S(t-1) - S(t)$ will. Does the first-month spike look different?
  • The offer's effect was assumed. Put a prior on it (say, a Beta centred on 0.3), push it through the decision, and work out how large a randomised pilot would have to be before the target list stops changing.
  • The file is a snapshot taken on one date, not a cohort followed from sign-up. What would you need to know about how customers entered the file to rule out left-truncation, and how would it enter the likelihood if you knew each customer's sign-up date?
h.progress()

C04: 0/21 hints used, 15/15 checks passed.

Reference solution

A complete, executed solution with commentary on every task — including the ways the obvious model fails. Try the hint ladder first.

Open the worked solution →