clvkit.CLV#

class CLV(transaction_model=None, monetary_model=None, *, check_independence=True)[source]#

Bases: object

The one-shot composition — a transaction model and a monetary model.

>>> clv = CLV().fit(cb).predict(horizon=12, discount_rate=0.01)
>>> clv.to_pandas()["clv"]

Defaults to BGNBD() for the transaction flow and GammaGamma() for spend; pass either to swap it, e.g. CLV(transaction_model=MBGNBD()).

Parameters:
  • transaction_model (TransactionModel | None)

  • monetary_model (MonetaryModel | None)

  • check_independence (bool)

fit(cb)[source]#

Fit both sub-models on cb, and assess the assumption joining them.

Lifetime value needs amounts, so a CustomerBase built without an amount_col is refused here rather than several frames deep inside the monetary model.

Parameters:

cb (CustomerBase)

Return type:

CLV

predict(*, horizon, discount_rate=0.0, margin=1.0, cb=None)[source]#

Discounted expected residual lifetime value over horizon periods.

  • horizon is a whole number of the fitted base’s own time_unit; the DET sum runs one term per period, so half a period has no increment.

  • discount_rate is d in the DET sum: the rate per `time_unit`, not per year. At weekly granularity 0.01 is 1% a week, ~68% a year.

  • margin is the contribution margin of equation (1). The default of 1.0 makes the result revenue-based lifetime value; pass your gross margin to get contribution-based CLV (see opinions.md).

Parameters:
Return type:

CLVResult

independence_check(cb=None)[source]#

The §2.2 assessment of the assumption this composition rests on.

Defaults to the base the model was fitted on. Read .holds() for the verdict and .plot() for the paper’s Figure 4 on your own data.

Parameters:

cb (CustomerBase | None)

Return type:

IndependenceCheck