clvkit.CustomerBase#

class CustomerBase(data, *, time_unit, observation_period_end, has_monetary, on_negative, collapse=None, events=None, customer_id_col='customer_id', amount_col='amount', engine='pandas')[source]#

Bases: object

A self-describing RFM summary built from a raw transaction log.

frequency is the BTYD repeat purchase count (total purchases minus one), not the raw transaction count — the #1 gotcha for anyone new to the BTYD/probability-model tradition.

Parameters:
classmethod from_transactions(transactions, customer_id_col='customer_id', datetime_col='date', amount_col='amount', *, time_unit='D', collapse=None, observation_period_end=None, datetime_format=None, on_negative='net', engine='pandas')[source]#

Summarise a raw transaction log into RFM plus provenance.

time_unit is the ruler: the unit recency and T are reported in. collapse is the event grain: transactions falling in the same collapse period become one purchase, because the counting process the BTYD models assume wants separated events.

They default to the same thing, which is what makes time_unit=”W” alone a trap — it does not merely re-scale the ruler, it deletes every second purchase inside a week, and it deletes most from your heaviest buyers. Pass both to reproduce the published CDNOW fit, which collapses at the data’s daily resolution and reports time in weeks:

CustomerBase.from_transactions(log, time_unit="W", collapse="D")

engine="dask" takes a dask.dataframe.DataFrame instead of a pandas one and never holds the whole log in memory. The summary it returns is an ordinary pandas-backed CustomerBase — per-customer and small — but it does not retain the event frame, so .split() refuses on it. It needs the optional dask extra.

The two engines agree on every value. They differ in exactly two ways, both cosmetic and both tested: rows come back in shuffle order rather than first-appearance order, and the customer id keeps whatever dtype the input frame carried — which Dask itself may have rewritten to string[pyarrow] on the way in.

Parameters:
  • transactions (DataFrame)

  • customer_id_col (str)

  • datetime_col (str)

  • amount_col (str | None)

  • time_unit (str)

  • collapse (str | None)

  • observation_period_end (str | Timestamp | None)

  • datetime_format (str | None)

  • on_negative (Literal['net', 'drop', 'raise'])

  • engine (Literal['pandas', 'dask'])

Return type:

CustomerBase

split(*, calibration_period_end, observation_period_end=None)[source]#

Split into a calibration CustomerBase and a holdout frame.

Calibration RFM is computed against calibration_period_end exactly as from_transactions would; the holdout frame carries frequency_holdout (repeat purchases in the holdout window), monetary_value_holdout (mean holdout spend, only when monetary), and duration_holdout (holdout length in time_unit). Customers whose first purchase falls in the holdout window have no calibration history and are excluded from both outputs.

Parameters:
Return type:

tuple[CustomerBase, DataFrame]

to_pandas()[source]#

Return the RFM summary as a DataFrame indexed by customer_id.

Return type:

DataFrame