of these selections that appears easy till you need to measure it. A buyer’s introductory charge expires, the bill goes up, and also you need to know whether or not the value change harm retention. Easy sufficient in idea.
The issue is that one thing else is sort of all the time occurring on the identical time. The initiative that drove the unique buy, whether or not it was a system migration, compliance push, gross sales transformation, or product launch, has wrapped up. The staff that championed the instrument has moved on to the subsequent factor. And the product that after felt important is quietly turning into a line merchandise somebody goes to query.
So when the shopper churns, the account staff says it’s the worth. The retention technique staff says the use case ran its course. Product says the platform by no means received previous the unique purchaser. Everybody has a idea and a spreadsheet to again it up.
Which attribution you land on issues, not abstractly, however by way of what you do subsequent.
| If the first trigger is… | The enterprise response is… |
|---|---|
| Promo expiry (worth shock) | Lengthen discounting, redesign renewal packaging, alter worth ladder |
| Initiative completion (worth exhaustion) | Put money into enlargement use instances, set off lifecycle retention performs, enhance onboarding to recurring workflows |
| Each forces work together | Time renewal affords round new enterprise moments; low cost alone is not going to clear up a price drawback |
Every technique under builds a distinct counterfactual for a similar occasion. Choosing the right one isn’t the laborious half. Figuring out which query you are attempting to reply earlier than you open a pocket book, that’s the place most of those analyses go sideways.
Outline the query earlier than the strategy
Earlier than you contact the information, it is advisable determine what you might be really making an attempt to estimate. The identical churn occasion at renewal can produce three meaningfully totally different numbers relying on what you might be asking:
- The promo-cohort impact. What was the typical churn impression on clients whose introductory low cost expired? The finance staff normally desires this quantity as a result of it strains up with how renewal income will get reported.
- The initiative-completion impact. What was the churn impression on clients whose unique adoption use case had concluded by renewal? The retention technique staff desires this one as a result of it speaks as to whether the product achieved sticky worth or simply served a mission.
- The joint impact and its interplay. What occurred to clients who confronted each on the identical time, worth improve and worth exhaustion arriving collectively? This quantity is sort of all the time bigger than both drive alone would predict, and it’s normally the one that really explains the churn spike.
These will not be the identical quantity and they don’t reply the identical query. Treating them as interchangeable is the most typical mistake I see in renewal churn analyses, and it’s normally what retains the account staff versus retention technique debate getting into circles.
The Setup
The artificial dataset has 10,000 B2B clients noticed round their renewal dates. Every has two flags: promo_expired (did their introductory charge finish at renewal?) and initiative_complete (had the unique use case concluded earlier than renewal?). One factor price flagging upfront: initiative_complete must be outlined utilizing pre-renewal alerts, issues like buyer relationship administration (CRM) milestones, implementation completion, or buyer success well being scores. For those who infer it from declining utilization after the actual fact, you’ll find yourself calling early churn habits a reason behind churn moderately than a symptom of it. The true results baked into the simulation:
- Baseline 6-month churn (neither drive): 8%
- Promo expiry alone: +5 pp (13% churn)
- Initiative completion alone: +4 pp (12% churn)
- Each forces collectively: +14 pp (22% churn), a +5 pp interplay surplus above the additive expectation of 17%
import numpy as np
import pandas as pd
import statsmodels.system.api as smf
RNG = np.random.default_rng(158) # seeded RNG for reproducibility
N = 10_000 # variety of clients
# True results baked into the information, what every technique ought to recuperate.
TRUE_BASELINE = 0.08 # 8% baseline 6-month churn (neither drive)
TRUE_PROMO = 0.05 # +5 pp from promo expiry alone
TRUE_INITIATIVE = 0.04 # +4 pp from initiative completion alone
TRUE_INTERACTION = 0.05 # +5 pp further raise when BOTH forces hit
clients = pd.DataFrame({
'customer_id': np.arange(N),
'promo_expired': RNG.selection([0,1], N, p=[0.45, 0.55]),
'initiative_complete': RNG.selection([0,1], N, p=[0.50, 0.50]),
'arr_usd': RNG.lognormal(10.5, 0.8, N), # annual rev
'tenure_months': RNG.uniform(10, 14, N),
'n_seats': RNG.integers(5, 200, N), # seats offered
})
# Every buyer's churn chance = baseline + promo + init + interplay.
# The interplay time period solely fires when BOTH forces are lively.
churn_prob = (
TRUE_BASELINE
+ TRUE_PROMO * clients['promo_expired']
+ TRUE_INITIATIVE * clients['initiative_complete']
+ TRUE_INTERACTION * clients['promo_expired']
* clients['initiative_complete']
)
clients['churned'] = (RNG.uniform(measurement=N) < churn_prob).astype(int)

Picture by Writer
Technique 1: Distinction-in-Variations
Enterprise query: What was the typical churn impression of promo expiry on clients who really confronted a worth improve at renewal, and does that impression differ relying on whether or not the use case had already concluded?
Technique-specific estimand: Common remedy impact of promo expiry on the promo-expired cohort, with a triple interplay time period to detect whether or not the value shock is amplified when initiative completion co-occurs.
Figuring out assumption: Parallel tendencies. Absent promo expiry, the churn trajectory of expired and non-expired clients would have tracked one another inside comparable initiative-completion teams across the renewal date.
To run this, you’ll mixture clients into cohort-week cells across the renewal date, with every row representing a cohort’s weekly churn charge and the variety of clients nonetheless in danger that week. The triple interplay lets the mannequin detect whether or not the promo shock is amplified when the use case has additionally concluded:
# A 'cohort' is the (promo_expired, initiative_complete) cell: 4 cohorts.
# Every row within the panel is one cohort in a single week, with that cohort's
# weekly churn charge and the variety of clients nonetheless in danger that week.
# week = 0 is the renewal date; unfavourable weeks are pre-renewal.
panel = build_cohort_week_panel(clients) # lengthy format: cohort x week
panel['post'] = (panel['week'] >= 0).astype(int) # 1 if post-renewal
panel['A'] = panel['promo_expired'] # rename for readability
panel['B'] = panel['initiative_complete']
# 'put up * A * B' expands to: put up, A, B, put up:A, put up:B, A:B, put up:A:B.
# Weighting by at_risk provides greater cohort-weeks extra affect.
did_model = smf.wls(
'churn_rate ~ put up * A * B',
knowledge = panel,
weights = panel['at_risk'],
).match(cov_type='HC3') # heteroskedasticity-robust normal errors
# Coefficients to learn:
# put up:A = promo shock when the initiative remains to be ongoing
# put up:B = initiative shock when the promo has not expired
# put up:A:B = further churn when each forces hit in the identical week

Picture by Writer
A observe on initiative_complete. It isn’t randomly assigned and it correlates with issues that independently predict churn: buyer measurement, how lengthy the unique purchaser has been on the firm, and product match. Controlling for covariates helps, however what you can’t do is let the mannequin outline it. Measure it earlier than the renewal determination utilizing CRM or buyer success milestones, not from utilization patterns you observe after the shopper has already began disengaging.
Failure mode: anticipation. Renewal quotes exit early. If clients begin procuring options the second they see the brand new charge, the pre-period is already contaminated. Test the event-study plot earlier than you belief the coefficient.
Studying the end result. The triple interplay time period, put up:A:B, is what the setup is constructing towards. A constructive coefficient there means the value shock bites tougher when the use case has already pale. For those who see that, a reduced renewal bill is not going to repair it.
Technique 2: Regression with interplay phrases
Enterprise query: What are the separate results of worth improve and mission completion, and do they work together?
Technique-specific estimand: Foremost results and interplay coefficient from a regression that explicitly fashions each forces and their joint time period.
Figuring out assumption: No unmeasured confounders, ample overlap throughout all 4 circumstances, appropriately specified practical type.
# Buyer-level regression. End result: 1 if buyer churned inside 6 months.
# np.log1p(x) = log(1 + x); used to regulate for skewed greenback/depend covariates
# (annual income, seat counts) so just a few massive clients don't dominate.
# The * operator under expands to: predominant results of A and B AND their interplay.
interaction_model = smf.ols(
'churned ~ promo_expired * initiative_complete'
' + np.log1p(arr_usd) + np.log1p(n_seats)',
knowledge=clients,
).match(cov_type='HC3') # HC3 = heteroskedasticity-robust normal errors
# Coefficients (illustrative, matching simulation reality):
# promo_expired: +0.049 (b1, predominant impact of A)
# initiative_complete: +0.041 (b2, predominant impact of B)
# promo_expired:initiative_complete: +0.051 (b3, interplay A x B)
One factor that journeys folks up: b1 isn’t ‘the impact of promo expiry.’ It’s the impact of promo expiry when initiative_complete equals zero. As soon as the initiative has additionally concluded, the marginal impact of promo expiry is b1 + b3, the place b3 is the interplay coefficient. The complete image:
Impact of promo expiry, initiative ongoing: b1 = +0.049
Impact of promo expiry, initiative full: b1 + b3 = +0.100
Impact of initiative completion, promo ongoing: b2 = +0.041
Impact of initiative completion, promo expired: b2 + b3 = +0.092

Picture by Writer
Failure mode: collinearity. For those who offered lots of clients into the identical wave of transformation work, promo expiry and initiative completion shall be correlated by building. When that occurs, b1, b2, and b3 get laborious to separate and the usual errors will flag it. At that time, report the joint prediction for every cohort moderately than making an attempt to interpret the coefficients individually.
Studying the end result. That interplay coefficient is as massive as both predominant impact by itself. A buyer dealing with each forces is not only further at-risk, they’re in a essentially totally different scenario. That’s what ought to drive the industrial response.
Technique 3: Shapley worth attribution
Enterprise query: On condition that each forces collectively induced 14 pp of incremental churn, how a lot of that ought to every drive be accountable for, for the needs of finances allocation and renewal technique?
Technique-specific estimand: Honest allocation of the joint churn impression throughout the 2 causal forces, utilizing Shapley values from cooperative recreation idea.
Figuring out assumption: The coalition worth estimates v(S), the place S is a subset of the drivers and v(S) is the incremental churn attributable to that subset, are credible. They arrive from the regression or experiment above, not from a confounded mannequin.
With simply two drivers, Shapley is definitely fairly intuitive. Every driver retains its standalone contribution, after which the 2 cut up the interplay surplus evenly. Promo expiry will get its 5 pp plus half the 5 pp interplay. Initiative completion will get its 4 pp plus the opposite half. The code makes this concrete:
from itertools import permutations
import math
# A 'coalition' is any subset of drivers lively collectively.
# v(S) = the incremental churn (in pp) attributable to coalition S.
# These coalition values come from the interplay regression above:
v = {
frozenset(): 0, # neither driver lively
frozenset(['promo']): 5, # promo expiry alone
frozenset(['init']): 4, # initiative completion alone
frozenset(['promo', 'init']): 14, # each, contains +5 pp interplay
}
# 'gamers' = the drivers we're allocating credit score throughout.
# For every ordering of gamers, every participant's 'marginal contribution' is
# how a lot the coalition worth grows when that participant joins.
# Shapley worth = common marginal contribution throughout all orderings.
def shapley_values(v, gamers):
n = len(gamers)
phi = {p: 0.0 for p in gamers} # accumulator for every participant
for perm in permutations(gamers): # strive each ordering
coalition = frozenset() # begin with no drivers lively
for participant in perm:
# how a lot does the coalition develop when this participant joins?
marginal = v[coalition | {player}] - v[coalition]
phi[player] += marginal
coalition = coalition | {participant}
# common throughout all n! orderings
return {p: spherical(phi[p] / math.factorial(n), 2) for p in gamers}
print(shapley_values(v, ['promo', 'init']))
# {'promo': 7.5, 'init': 6.5} # sums to 14 pp, the complete joint impact

Picture by Writer
The factor price repeating right here. Shapley is an allocation rule. It distributes credit score pretty given the coalition values you feed it, however it can’t repair unhealthy inputs. In case your v(S) estimates come from a confounded regression, your Shapley shares are confounded too. The maths is clear; the causal work nonetheless has to occur upstream.
Studying the end result. A 7.5 to six.5 cut up isn’t a sign to place 54% of your retention finances into pricing and 46% into buyer success. It’s a sign that you simply want each, and that the timing of the renewal provide issues as a lot as what’s in it.
Selecting between the strategies
There isn’t a universally appropriate technique right here. The suitable selection depends upon what query you might be answering and what your knowledge can really assist. In observe, I run multiple:
| Technique | Estimand | Assumption | Tradeoffs |
|---|---|---|---|
| DiD | Avg. impact on promo-expired cohort | Parallel tendencies round renewal | Clear cohorts and pre-period; breaks below anticipation or correlation |
| Regression + Interplay | Foremost results + interplay time period | No confounders; overlap throughout cells | Quantifies the interplay; breaks below collinearity |
| Shapley attribution | Honest allocation of joint impression | Credible v(S) from above | Helpful for finances framing; unstable when v(S) is noisy |
When the identification checks, the interplay mannequin, and the attribution layer all level in the identical course, I’m comfy presenting the end result. Once they diverge, that’s price understanding earlier than you convey something to a stakeholder assembly. A pointy disagreement between strategies is normally telling you one thing about which assumption isn’t holding.
Translating the impact into income and LTV
Getting a churn coefficient isn’t the identical as getting a pricing advice. The identical churn improve can nonetheless be web constructive if the value raise is massive sufficient. It’s important to propagate it ahead earlier than you understand whether or not the change really labored.
# LTV = anticipated income per buyer over a set horizon (in months).
# survival[m] = chance the shopper remains to be subscribed in month m.
# Multiply by month-to-month MRR and sum: undiscounted 2-year LTV.
def ltv(monthly_churn, monthly_mrr, horizon=24):
months = np.arange(horizon)
survival = (1 - monthly_churn) ** months
return (survival * monthly_mrr).sum()
# Convert 6-month churn charges into month-to-month churn charges.
# (1 - p)^(1/6) is the month-to-month survival charge; subtracting from 1 provides month-to-month churn.
baseline_monthly = 1 - (1 - 0.08) ** (1/6) # 0.0138 month-to-month churn
treated_monthly = 1 - (1 - 0.22) ** (1/6) # 0.0406 month-to-month churn
old_mrr = 1_000 # pre-renewal month-to-month recurring income (MRR)
new_mrr = 1_130 # post-renewal MRR (+13% worth improve)
baseline_ltv = ltv(baseline_monthly, old_mrr) # $20,550
treated_ltv = ltv(treated_monthly, new_mrr) # $17,546
# Internet 2-year LTV change per buyer: -$3,004
# The 13% worth improve doesn't offset the accelerated churn.
# Breakeven: what new MRR would restore the baseline 2-year LTV?
price_grid = np.linspace(1_000, 1_600, 1_000)
ltv_grid = [ltv(treated_monthly, p) for p in price_grid]
breakeven = price_grid[np.searchsorted(ltv_grid, baseline_ltv)]
# Breakeven MRR: ~$1,324 (a 32% improve, not the 13% that shipped)
Studying the end result. On this situation, the 13% worth improve pays for itself within the quarter it ships however eats via medium-term buyer worth. To interrupt even on 2-year LTV on the identical churn charge, you’ll should be charging roughly $1,324, a 32% improve moderately than the 13% that went out. That isn’t a niche you shut with a distinct worth level. The underlying use-case drawback must be addressed first.
The decomposition is the deliverable. The causal estimate is simply the enter.
A number of closing pitfalls
- Correlated project. The land-and-expand gross sales movement creates a pure correlation between promo expiry and initiative completion. You offered the shopper on an enormous initiative and gave them a year-one deal to get them shifting. Now each issues are expiring on the identical time by design. Cross-sectional variation alone is not going to untangle them. You want timing variation, comparability cohorts, or an eligibility cutoff.
- Anticipation. The pre-period solely stays clear if clients don’t react to the renewal quote earlier than the official price-change date. Once they do, the parallel-trends assumption breaks earlier than the remedy even fires, and the DiD coefficient picks up the early response moderately than the value shock itself. The event-study plot is your first line of protection; a slope within the pre-period weeks is the inform.
- Estimand drift. The most typical mistake I see in renewal churn reads is bringing the incorrect quantity to the assembly. The promo-cohort common impact, the interplay coefficient, and the Shapley allocation are three totally different solutions to a few totally different questions. Know which one you might be presenting and why.
- Attribution with no determination. Shapley will get you to an allocation. It doesn’t get you to a plan. A 54/46 cut up between worth and use-case exhaustion is helpful context for a dialog, not a finances instruction. Somebody nonetheless has to determine what the precise retention intervention is.
All code on this article runs finish to finish on the artificial dataset. The complete pocket book with the cohort-week panel building, diagnostic plots, and sensitivity checks is on GitHub and runnable immediately in Colab.
Employees Information Scientist targeted on causal inference, experimentation, and determination science. I write about turning ambiguous enterprise questions into decision-ready evaluation.
Extra like this on LinkedIn 👇















