clvkit.CohortMatrix#

class CohortMatrix(matrix, *, metric, period)[source]#

Bases: object

A cohort-by-period matrix of retention or revenue, and its heatmap.

Rows are acquisition cohorts (the period of a customer’s first observed purchase); columns are whole periods elapsed since that cohort’s own period. Cell values are:

metric="retention"

the number of distinct customers of that cohort active in that period (a customer buying three times in one period counts once).

metric="revenue"

the total amount those customers spent in that period.

Recent cohorts have been observed for fewer periods, so the matrix is a triangle: cells past a cohort’s observation window are NaN, never 0. A zero means “observed, nobody bought”; missing means “not yet knowable”. Because of those NaN cells the retention matrix is float-typed even though it counts customers.

>>> matrix = CohortMatrix.from_transactions(df, period="M")
>>> matrix.to_pandas(relative=True)   # retention rates
>>> matrix.plot()                     # the heatmap

There is no fit(): a pivot has no state to estimate.

Parameters:
classmethod from_transactions(transactions, *, period='M', metric='retention', customer_id_col='customer_id', datetime_col='date', amount_col='amount', datetime_format=None, engine='pandas')[source]#

Pivot a raw transaction log into a cohort-by-period matrix.

period is any pandas offset alias ("M", "W", "Q", "Y"); it sets both the cohort grain and the column grain, since a cohort matrix only makes sense when both use the same clock.

The observation window ends at the last transaction in the log, and that is what makes a cell unobserved rather than zero.

engine="dask" takes a dask.dataframe.DataFrame and reduces the log without holding it in memory; the matrix it returns is the same pandas one, since a cohort-by-period grid is small whatever built it. It needs the optional dask extra.

Parameters:
  • transactions (DataFrame)

  • period (str)

  • metric (Literal['retention', 'revenue'])

  • customer_id_col (str)

  • datetime_col (str)

  • amount_col (str | None)

  • datetime_format (str | None)

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

Return type:

CohortMatrix

to_pandas(*, relative=False)[source]#

The matrix as a DataFrame: cohorts down, periods across.

relative=True divides every cohort by its own period-0 value — retention rates rather than counts, or a revenue index rather than currency. That is the comparable view, since cohorts differ in size.

Parameters:

relative (bool)

Return type:

DataFrame

to_json(*, relative=False)[source]#

Cohort-keyed JSON, the same shape as to_pandas().

Unobserved cells serialise as null, keeping the triangle’s shape legible outside pandas.

Parameters:

relative (bool)

Return type:

str

plot(ax=None, *, relative=True, **kwargs)[source]#

Draw the cohort heatmap — the standard triangle chart.

Defaults to the relative view because that is what the chart is read for: how fast each cohort decays, independent of how big it was.

Parameters:
  • ax (Axes | None)

  • relative (bool)

Return type:

Axes