Probabilistic Modelling

Customer lifetime value prediction: every customer flips two coins

How a Buy 'Til You Die model turned silent churn into a forecast with under 7% error at a marketplace - and why your averages never will.

A customer booked four experiences with you last spring, then went quiet for five months. Are they gone, or just between trips?

If you cannot answer that question, you cannot do customer lifetime value prediction - you can only do customer lifetime value guessing. In a non-contractual business (e-commerce, marketplaces, travel), nobody cancels. Customers simply stop showing up, and they never tell you. That silence is the entire problem.

We ran into it head-on with a marketplace client. Marketing wanted CAC caps per channel, finance wanted a defensible number for the annual plan, and the existing "CLV" was average order value multiplied by a purchase count someone had estimated in 2021. Nobody trusted it, so nobody used it.

Why averaging your way to CLV fails

The naive approach looks reasonable on a slide: take average revenue per customer per year, multiply by an assumed lifetime of two or three years, done. Two problems.

First, the average customer does not exist. Purchase behaviour in these businesses is brutally skewed - at this client, roughly the top 15% of customers drove over half of repeat revenue. An average smears whales and one-timers into a fictional middle.

Second, cohort-level extrapolation (fit a curve to each cohort's cumulative revenue, extend it forward) treats every customer in a cohort as identical and quietly assumes the past repeats. When we backtested the client's cohort model against a holdout year, it missed by more than 10% - and worse, it missed in different directions for different cohorts, so you couldn't even correct for the bias.

What you actually need is a model of individual behaviour that accepts the central awkward fact: churn is unobservable - silence is the only churn signal you get. Enter the wonderfully named Buy 'Til You Die family, and its workhorse, BG/NBD.

The coin-toss model behind customer lifetime value prediction

BG/NBD stands for Beta-Geometric / Negative Binomial Distribution - the two distributions it uses to describe how often customers buy and when they quietly stop. Behind the name sit both an intuitive mental model and a statistically sound way to estimate the probability that a customer has churned.

What follows is the short version. For the in-depth walkthrough, you can find it in our Towards Data Science publication: Predicting customer lifetime value with "Buy 'Til You Die" probabilistic models in Python.

Every customer walks around with two coins in their pocket: a buy coin and a die coin. You never see the coins. You only see the purchases.

How the two coins work

While a customer is "alive", they periodically flip their buy coin; heads means they make a purchase. After every purchase, they flip their die coin; heads means they silently become inactive, forever. Each customer has their own coins with their own biases - some buy often, some rarely; some are loyal, some are one-and-done.

The model's job is to infer, from the purchase history alone, how each customer's two coins are weighted - and therefore the probability that they are still alive today and how many purchases they will make in the future. It captures the intuition you already have: a weekly buyer who's been silent for two months is probably dead; a twice-a-year buyer silent for two months is probably fine.

Three numbers per customer: recency, frequency, T

The beautiful part is the data shape. You do not need clickstream, demographics, or a feature store. For each customer, the model needs exactly three numbers, all derivable from your orders table:

The gap between recency and T is where the inference lives. A big gap for a high-frequency customer screams "dead coin came up heads". The same gap for a low-frequency customer barely whispers.

For years the standard tool for fitting these models in Python was the lifetimes package. It's no longer actively developed - its models now live on, rebuilt on a proper Bayesian foundation, as the CLV module of PyMC-Marketing, which is where I'd start any new engagement today. The core workflow is a few honest lines:

from pymc_marketing import clv

# one row per customer: customer_id, frequency, recency, T
model = clv.BetaGeoModel(data=rfm)
model.fit()

# expected purchases over the next 26 weeks, per customer
purchases = model.expected_purchases(future_t=26)
alive = model.expected_probability_alive()

Two lines to fit, two to score. The hard work is upstream (defining what counts as a purchase, choosing the time unit, excluding B2B accounts and fraud) and downstream (validating it honestly).

From purchase counts to euros: Gamma-Gamma

BG/NBD predicts how many purchases. To get to money, we paired it with the Gamma-Gamma model, which estimates each customer's average transaction value while accounting for the fact that a customer with three observed orders gives you a much noisier estimate than one with thirty.

Gamma-Gamma assumes purchase frequency and order value are roughly independent - an assumption you must actually check (a five-minute correlation test; at this client it held at r ≈ 0.03). Multiply expected purchases by expected order value, apply a discount rate, and you have a per-customer CLV that finance can interrogate line by line.

Did it work? Calibration and holdout

A CLV model you haven't backtested is astrology with extra steps. We used the standard calibration/holdout split: train the model only on transactions up to a cutoff date, then predict each customer's purchases in the holdout year and compare against what actually happened.

The results, at aggregate level: the BG/NBD approach landed at roughly -6.3% overall error on holdout purchases - a slight underestimate, which is the direction you want when the number feeds acquisition budgets. The incumbent cohort-extrapolation approach was off by more than 10%, with the sign flipping between cohorts.

We also checked calibration at the segment level, because an aggregate number can hide offsetting errors. The model was tightest exactly where the money was - the high-frequency segments - and noisiest on single-purchase customers, which is expected and honest: one data point per customer is one data point.

What changed for the business

Numbers only matter if they change a decision. Three things changed within a quarter:

One caveat I always give clients: CLV prediction tells you who is valuable and who is fading - it does not tell you why. When the why matters, you pair it with proper customer retention analysis on cohort grids to find where the leak is. And before you act on any segment-level insight with a big campaign, test it - even professionals are terrible at predicting what will work.

The takeaway

You don't need deep learning or a data science team of eight to do customer lifetime value prediction well. You need your orders table, three numbers per customer, a forty-year-old model with a great name, and the discipline to backtest it before you believe it.

The buy coin and the die coin are always flipping. The only question is whether you're modelling them or averaging over them.

Want a CLV number your CFO will actually sign off on?

Satyadata builds and validates CLV models for marketplaces, e-commerce, retail, SaaS, fintech and subscription businesses - from raw orders table to CAC caps and forecasts.

Start a conversation