Skip to content

Revenue at risk (RFM "At risk")

How much of our revenue comes from good customers who have stopped buying?

Current value (customers case data): 9.4% - ✓ On target. Open the case.

Field Value
Definition Revenue in the look-back window from customers in the RFM "At risk" segment (high frequency/monetary score, low recency score) divided by total revenue.
Formula sum(monetary where segment = 'At risk') / sum(monetary)
Unit %
Granularity Customer; recomputed monthly on a 12-month window.
Direction Lower is better ↓
Target and thresholds ≤ 10.0% (amber ≤ 20.0%)
Parameters –
Owner CRM Lead
Source Transactions (UCI Online Retail II), RFM scoring in this repo
Input table customer_rfm, columns segment, monetary
Recommended chart bar - Revenue by segment, sorted, with the At-risk bar highlighted.
Related KPIs Customer retention (quarter over quarter), Repeat purchase rate, Net Promoter Score (simulated survey)

How to read it

The size of the win-back opportunity. The segment list is the action list: call the top customers by monetary value first.

Common misreadings

  • RFM scores are quintiles of this dataset - an "At risk" label is relative, not absolute.
  • Recency depends on the snapshot date; recompute on a fixed calendar to keep it comparable.

Worked example (from the test suite)

segment monetary
Champions 500.0
At risk 200.0
At risk 100.0
Hibernating 200.0

Expected result: 30.00%. At risk revenue 200 + 100 = 300 of 1000 = 0.3.

This example is asserted in tests/test_formulas.py against the Python function and in tests/test_sql.py against the SQL below.

Implementations

Python function (the single implementation used by the site):

@kpi("revenue_at_risk_share")
def revenue_at_risk_share(df: pd.DataFrame) -> float:
    at_risk = df.loc[df["segment"].eq("At risk"), "monetary"].sum()
    return safe_ratio(at_risk, df["monetary"].sum())

Reference SQL (tested on SQLite):

SELECT SUM(CASE WHEN segment = 'At risk' THEN monetary ELSE 0 END) / SUM(monetary) FROM customer_rfm