Customer Lifetime Value

Bayesian CLV models (BG/NBD + Gamma-Gamma) powering LTV-aware experiment economics.

LTV / CLV modeling: RFM preprocessing + BG/NBD + Gamma-Gamma likelihoods.

preprocess is pandas-only and imported eagerly; the pytensor likelihood expressions live in mmm_framework.ltv.likelihood (import it explicitly — kept out of this namespace so import mmm_framework.ltv stays light, the same lazy-import convention as mmm_extensions).

mmm_framework.ltv.transactions_to_rfm(df, *, customer_col='customer_id', date_col='date', value_col=None, observation_end=None, freq='W', segment_col=None)[source]

Collapse a transaction log to one row per customer (RFM summaries).

Multiple transactions by the same customer on the same date are merged into one purchase (values summed) — sub-period repeats are indistinguishable at the model’s time resolution and would otherwise inflate frequency.

Parameters:
  • df (DataFrame) – long transaction log (one row per transaction).

  • value_col (str | None) – transaction value column; omit for frequency-only (no monetary output column).

  • observation_end (Timestamp | str | None) – end of the calibration window (transactions after it are dropped). Defaults to the last transaction date.

  • freq (str) – time unit for recency/T — ‘D’, ‘W’ (default) or ‘M’.

  • segment_col (str | None) – per-customer attribute (e.g. acquisition channel) carried through as a segment column — the FIRST transaction’s value per customer (the acquisition event’s attribution).

Return type:

DataFrame

Returns:

DataFrame indexed by customer with columns frequency, recency, T, n_txn (raw purchase count) and, when value_col is given, monetary (mean REPEAT-transaction value; NaN for one-time buyers), plus segment when segment_col is given.

mmm_framework.ltv.rfm_arrays(rfm)[source]

(frequency, recency, T, monetary-or-None) float arrays for the model.

Return type:

tuple[ndarray, ndarray, ndarray, ndarray | None]

mmm_framework.ltv.new_customer_clv_series(transactions, clv_per_customer, *, customer_col='customer_id', date_col='date', freq='W-MON')[source]

Weekly (or freq-period) cohort CLV: for each period, the number of NEWLY ACQUIRED customers and the sum of their (posterior-mean) CLV.

clv_per_customer is indexed by customer id (e.g. the posterior mean of the fitted model’s per-customer clv deterministic). Customers in the log but missing from the CLV index contribute count but zero value (and are counted in n_unvalued for honesty).

Returns a DataFrame indexed by period start with columns new_customers, cohort_clv, mean_clv, n_unvalued.

Return type:

DataFrame

mmm_framework.ltv.clv_to_cac(segment_clv, cac)[source]

Blended acquisition economics per segment/channel.

Parameters:
  • segment_clv (dict[str, float]) – posterior mean CLV per acquired customer, by segment (e.g. from the fitted model’s segment_clv_<name> deterministics).

  • cac (dict[str, float]) – customer-acquisition cost per segment (spend / new customers — supplied by the caller; the CLV model has no spend data).

Return type:

DataFrame

Returns:

DataFrame with clv, cac, clv_minus_cac and clv_to_cac per segment, sorted by clv_minus_cac descending. Segments missing a CAC get NaN economics (still listed, flagged).