Skip to content

Survival Analysis for Product Questions

11 min read · updated August 4, 2026

Every question of the form “how long until” has the same awkward property: most of your subjects have not done it yet, and throwing them away biases the answer downward. Survival analysis is the machinery for using them, and on the eight customers below it moves the median lifetime from 15 days to 20.

The problem is censoring

A customer signed up 40 days ago and has not churned. How long was their subscription? Not 40 days — that is a lower bound. They are right-censored: the event has not happened yet and you stopped watching.

The two obvious ways out are both wrong, and wrong in opposite directions. Dropping censored subjects keeps only those who have already churned, which selects for short lifetimes and understates the answer. Treating the censoring time as the lifetime says everyone churned today, which understates it too. Censored subjects carry real information — they survived at least that long — and the whole method is about extracting exactly that much and no more.

This is also why a binary churn classifier is the wrong tool for some questions. It forces a fixed horizon and it silently discards timing: a customer who churns on day 3 and one who churns on day 89 are the same row in a 90-day label. The churn page builds that classifier; this page is when not to.

Kaplan-Meier, worked on eight customers

Eight subscriptions. A plus sign marks a censored observation — still active when observation ended.

customer   days   status
   A          5    churned
   B          8    censored (+)
   C         12    churned
   D         15    churned
   E         18    censored (+)
   F         20    churned
   G         22    censored (+)
   H         30    churned

The estimator works event by event. At each time an event occurs, the conditional probability of surviving it is (at risk − events) / at risk. Multiply those together and you have the survival curve. Censored subjects contribute by being at risk until they leave, then quietly dropping out of the denominator.

time   at risk   events   survive this step        S(t)

  5        8         1      (8-1)/8 = 0.8750       1.0000 x 0.8750 = 0.8750
  8        7         0      censored: B leaves the risk set
 12        6         1      (6-1)/6 = 0.8333       0.8750 x 0.8333 = 0.7292
 15        5         1      (5-1)/5 = 0.8000       0.7292 x 0.8000 = 0.5833
 18        4         0      censored: E leaves the risk set
 20        3         1      (3-1)/3 = 0.6667       0.5833 x 0.6667 = 0.3889
 22        2         0      censored: G leaves the risk set
 30        1         1      (1-1)/1 = 0.0000       0.3889 x 0.0000 = 0.0000

Median survival = the first t where S(t) <= 0.50
  S(15) = 0.5833   still above
  S(20) = 0.3889   crossed
  -> median = 20 days

Trace what censoring did at time 12. Six customers are at risk, not seven, because B was censored at day 8 — B contributed to the denominator through days 5 and 8 and then stopped. That is the whole mechanism: partial credit for the time actually observed.

What the naive answers get wrong

NAIVE 1: drop the censored rows
  observed lifetimes: 5, 12, 15, 20, 30
  median = 15 days                          understates by 25%

NAIVE 2: "churn rate = events / customers"
  5 / 8 = 62.5%
  A rate with no time attached. 62.5% over what period? The three
  censored customers had not finished. The number is not wrong so much
  as meaningless.

NAIVE 3: treat censoring time as the lifetime
  lifetimes: 5, 8, 12, 15, 18, 20, 22, 30
  median = 16.5 days                        understates by 17.5%

KAPLAN-MEIER
  median = 20 days

A 25% error in median lifetime propagates directly into lifetime value, payback period and every acquisition decision built on them. And the bias is always in the same direction — pessimistic — which is particularly awkward because a pessimistic customer-lifetime number gets accepted without argument.

The bias is largest exactly where it hurts most: a fast-growing product has a large share of recent, still-active customers, so a large share of censored rows, so the naive estimate is worst precisely when growth is fastest.

Expected lifetime, and why it feeds LTV

Mean survival time is the area under the survival curve. Because the curve here reaches zero, the area is exactly computable as a sum of rectangles.

interval      S(t)     width    contribution

 0 to 5      1.0000     5        5.0000
 5 to 12     0.8750     7        6.1250
12 to 15     0.7292     3        2.1875
15 to 20     0.5833     5        2.9167
20 to 30     0.3889    10        3.8889
                              ---------
mean survival time                20.118 days

compare: mean of the uncensored lifetimes only
  (5 + 12 + 15 + 20 + 30) / 5 = 16.4 days      understates by 18%

When the curve does not reach zero — the usual case, because some customers are still active at the end of observation — the area is undefined past the last observation and you must report a restricted mean: the area up to a stated horizon, quoted with that horizon. “Restricted mean survival over 24 months” is an honest number. “Average customer lifetime” computed from a two-year-old product is not, and it is the single most common piece of fiction in a growth deck.

Adding covariates: the Cox model

Kaplan-Meier describes a population. To ask whether a feature changes the timing, you need a regression, and the Cox proportional-hazards model is the standard one. It models the hazard — the instantaneous rate of the event given survival so far — as a baseline shape shared by everyone, multiplied by a factor depending on the covariates.

h(t | x) = h0(t) * exp(b1*x1 + b2*x2 + ...)

The baseline h0(t) is left completely unspecified, which is why the
model is popular: no assumption about the shape of the hazard over
time is required.

Coefficients read as hazard ratios:

  exp(b) = 1.00   no effect
  exp(b) = 1.50   50% higher rate of churning at any moment
  exp(b) = 0.70   30% lower rate

Example reading:
  onboarding_call = 1   ->  exp(b) = 0.62
  "customers who had an onboarding call churn at 62% of the rate of
   those who did not, at every point in time"
from lifelines import KaplanMeierFitter, CoxPHFitter

km = KaplanMeierFitter()
km.fit(df["duration_days"], event_observed=df["churned"])
print(km.median_survival_time_)
print(km.survival_function_)

cph = CoxPHFitter()
cph.fit(df[["duration_days", "churned", "plan_tier", "seats",
            "onboarding_call"]],
        duration_col="duration_days", event_col="churned")
cph.print_summary()          # coefficients, hazard ratios, p-values

# the proportional-hazards check, which is not optional
cph.check_assumptions(df, p_value_threshold=0.05)

A hazard ratio is not a causal effect. Customers who took an onboarding call chose to, and whatever made them choose it probably also made them more likely to stay. The Cox model has adjusted for the covariates you included and for nothing else — what an observational estimate can honestly claim applies here without modification.

The assumption that fails

Proportional hazards says the ratio between two groups is constant over time. That assumption fails routinely in product data, and it fails in a way that inverts conclusions rather than blurring them.

The classic case: an onboarding programme greatly reduces churn in the first month and does nothing after. The hazard ratio is 0.3 early and 1.0 later. A Cox model reports the average, perhaps 0.75, and everyone concludes that onboarding has a moderate permanent effect — when what it actually has is a large temporary one. The two imply completely different investments.

  1. Test it. check_assumptions above, or plot the scaled Schoenfeld residuals against time and look for a trend.
  2. Stratify. Where a variable violates the assumption and you only need to adjust for it, stratify on it: each stratum gets its own baseline hazard, and the variable’s own coefficient disappears.
  3. Add a time interaction. Where the changing effect is the finding, model it explicitly so the report says “strong for 30 days, negligible after” rather than averaging the two.
  4. Or split the horizon. Fit separate models for days 0–30 and 31 onward. Blunt, easy to explain, and usually enough for a product decision.

One further distinction that matters in practice: competing risks. Voluntary cancellation and involuntary churn from a failed card are different events with different remedies, and treating the second as censoring for the first assumes they are independent, which they are not. Model them separately or you will attribute payment-processing failures to product dissatisfaction.

Three product questions this fixes

  • “What is our churn rate?” Replace with a survival curve and a stated horizon: 82% survive 6 months, 61% survive 12. It is more informative, it is comparable between cohorts of different ages, and it stops the monthly number bouncing every time acquisition changes.
  • “What is a customer worth?” Restricted mean survival over a stated horizon, times margin per period, discounted. The horizon must be stated and must not exceed your observation window, however tempting the extrapolation.
  • “Did the change help?” Compare survival curves between cohorts, and prefer a randomised comparison to a Cox-adjusted observational one. If it must be observational, name the confounders you adjusted for and the ones you could not — which is the whole discipline of causal inference.