# RFM Customer Segmentation

Know which customers are active, which are drifting, and which are gone.

## Overview and Use Cases

No two customers behave the same way. Some buy constantly, while some spent heavily last year and have not been back. RFM tells them apart using three things you already know: how recently each person bought, how often they buy, and how much they spend. Those three answers sort your customer list into audience groups you can act on, showing you who to recognize, who to nurture, who to win back, and who has moved on.

Mechanically, RFM AI Signals runs that analysis across your customer profiles and places every customer into one of ten industry-standard segments, from Champions through Lost customers. Rankings come from your own customers, so what counts as a frequent buyer is defined by your business. Each customer arrives labeled, with the underlying scores alongside, ready to build audiences from.

**Common use cases**

- Identify and reward top-performing customers with VIP offers before competitive pressure increases
- Launch automated re-engagement campaigns for at-risk high-value customers who have gone quiet
- Exclude low-priority customers from expensive paid channels to improve marketing efficiency
- Adjust communication cadence by customer tier
- Feed RFM scores as input features into CLTV, propensity, and lookalike models


**Who benefits most:** Marketing analysts, CRM managers, and lifecycle marketing teams who need fast, actionable customer segments without a data science team on standby.

### How It Fits with the Other AI Signals

| **Question you're asking**  | **Use**  |
|  --- | --- |
| **Who matters, based on past purchasing?** | **RFM AI Signals (you are here)**: descriptive segments, no modeling required |
| How likely is this customer to do X? | [Propensity Scoring AI Signals](/products/customer-data-platform/machine-learning/ai-signals/propensity-ai-signals): a probability per event you define |
| How much will this customer be worth? | [CLTV AI Signals](/products/customer-data-platform/machine-learning/ai-signals/cltv-ai-signals): a value forecast and percentile rank |
| Which product should I recommend next? | [NBP AI Signals](/products/customer-data-platform/machine-learning/ai-signals/nbp-ai-signals): a ranked list of products, services, or content |
| Which action should I take for this customer? | [NBA AI Signals](/products/customer-data-platform/machine-learning/ai-signals/nba-ai-signals): a learned per-user policy across candidate actions |


***RFM or CLTV?** This is the pair readers regularly confuse, since both speak to customer worth. RFM is descriptive and backward-looking: it summarizes what a customer has already done and sorts them relative to everyone else. CLTV is predictive and forward-looking: it forecasts spend over a horizon you choose. RFM tells you who your best customers have been; CLTV tells you who they will be. Reach for RFM when you want segments today with no training step, and for CLTV when you are allocating budget against future value.*

*These signals feed each other rather than compete. RFM quartiles make strong input features for CLTV, propensity, and lookalike models.*

## Quick Start

RFM runs as a single function on the Treasure AI ML Batch API. There is no separate training and prediction step: **`rfm`** reads your aggregated table, computes quartiles across the whole base, and writes the scored output in one run.

Your input is one row per customer with four columns: an identifier, plus **`recency`**, **`frequency`**, and **`monetary_value`**. RFM does not aggregate transactions for you, so those three metrics must already be calculated. Only the identifier can be named freely, using `user_column`. The other three names are fixed, so alias them in the query that builds the table. [Data Preparation](#data-preparation) covers the full schema and data requirements.

**You only need to set two parameters.** `input_table` and `output_table` are required. Add `user_column` only if your identifier column is not already named `user`. Everything else in this document has a working default.

Security
Store your Treasure AI API key as a Workflow secret named `td.apikey`, never as a literal value in the workflow definition. Referencing it as a secret keeps the key out of version control, so teams can securely manage sensitive information. See [Setting Workflow Secrets from Treasure Console](/products/customer-data-platform/data-workbench/workflows/setting-workflow-secrets-from-td-console).

```yaml
# rfm_workflow.dig
# Score every customer in the input table and write quartiles,
# an RFM score, and a named segment.
+run_rfm:
  http>: https://ml-batch-api.treasuredata.com/v1/runs
  method: POST
  headers:
    - authorization: "TD1 ${secret:td.apikey}"
    - X-TD-ML-SESSION-ID: ${session_id}
    - X-TD-ML-ATTEMPT-ID: ${attempt_id}
  store_content: true
  content:
    input_table: your_database.rfm_aggregated
    output_table: your_database.rfm_output
    solution_name: rfm
    solution_arguments:
      user_column: user
      use_sql: true
      # mv_threshold: 0.05   # Uncomment to set a monetary floor

+print_response:
  echo>: "RFM job submitted. Response: ${http.last_content}"

# Poll until the job completes. The API returns HTTP 408 while the job
# is running; Treasure Workflow retries until it receives HTTP 200.
+poll_status:
  http>: https://ml-batch-api.treasuredata.com/v1/runs/${JSON.parse(http.last_content)['id']}/status
  method: GET
  headers:
    - authorization: "TD1 ${secret:td.apikey}"
```

**Expected output:** one row per customer in `your_database.rfm_output`, carrying your three input metrics plus `r_quartile`, `f_quartile`, `m_quartile`, `rfm_quartile`, `rfm_score`, and `rfm_segment`.

```
user     recency  frequency  monetary_value  r_quartile  f_quartile  m_quartile  rfm_quartile  rfm_score  rfm_segment
61612    3181     4          4115            3           1           4           R3F1M4        2.6667     Promising
39408    3290     9          4893            2           4           4           R2F4M4        3.3333     Cannot lose them
21495    3326     6          2770            1           2           3           R1F2M3        2.0        High Value Sleeping
```

To create and schedule this workflow, see [Getting Started with Treasure Workflow](/products/customer-data-platform/data-workbench/workflows/getting-started-with-treasure-workflow).

## Model Configuration

RFM AI Signals takes two kinds of input: the aggregated table you point it at, and the parameters you pass it. Both are documented under Data Preparation and Workflow Parameters below.

### `rfm`

`rfm` ranks every customer on each of the three dimensions, combines the ranks into a score and a label, and writes the result in a single run.

#### Data Preparation

A pre-aggregated customer table, one row per customer. Unlike CLTV, RFM does not summarize transactions for you: you supply recency, frequency, and monetary value already computed.

| **Field**  | **Type**  | **Required**  | **Description**  |
|  --- | --- | --- | --- |
| `user` | VARCHAR | Yes | Unique customer identifier. This is the only column whose name is customizable, via `user_column`. |
| `recency` | INTEGER | Yes | Days since the customer's most recent transaction. Lower means more recent. Column name is fixed. |
| `frequency` | INTEGER | Yes | Number of transactions in the analysis window. Column name is fixed. |
| `monetary_value` | DOUBLE | Yes | Total spend across the analysis window, in whichever currency or unit your business records. Column name is fixed. |


Example rows:

```
user     recency  frequency  monetary_value
61612    3181     4          4115
39408    3290     9          4893
21495    3326     6          2770
45110    3818     2          133
```

Data quality requirements
- **Missing values are not handled for you.** Remove or impute nulls in `recency`, `frequency`, and `monetary_value` before running. This differs from the older AutoML notebook, which imputed on your behalf.
- **Negative or zero values corrupt the scoring.** Clean refunds, voids, and promotional credits upstream.
- **Only `user` can be renamed.** The other three column names are fixed, so alias them in the query that builds your aggregated table.
- **Aim for at least 12 months of history.** Shorter windows compress the recency signal and push customers toward the same quartile.


#### Workflow Parameters

| **Parameter**  | **Type**  | **Default**  | **Required**  | **Description**  |
|  --- | --- | --- | --- | --- |
| `input_table` | string | — | Yes | Source aggregated table in `dbname.table_name` format. |
| `output_table` | string | — | Yes | Destination table for the scored output, in `dbname.table_name` format. |
| `output_mode` | string | `append` | No | How results are written to the output table. Accepted values: `append` and `overwrite`. See [Output Modes](#output-modes) for execution-specific behavior. |
| `user_column` | string | `user` | No | Name of the customer identifier column in your input table. |
| `use_sql` | boolean | `false` | No | Run the calculation in Trino instead of Python. Strongly recommended above roughly 10 million rows. See [Execution Modes](#execution-modes). |
| `use_hive` | boolean | `false` | No | Run the calculation in Hive. Must be set together with `use_sql: true`. See [Execution Modes](#execution-modes). |
| `mv_threshold` | float | — | No | Monetary floor. Customers below this value are assigned monetary quartile 1 regardless of their relative rank. See [Monetary Value Threshold](#monetary-value-threshold). |
| `audience_name` | string | — | No | Creates a Master Segment and a child Segment per RFM classification. |


#### Output

One row per customer. Your three input metrics are carried through unchanged, followed by the scoring columns.

| **Field**  | **Type**  | **Description**  |
|  --- | --- | --- |
| `user` | STRING | Customer identifier, carried forward from input. |
| `recency` | LONG | Carried forward from input. |
| `frequency` | LONG | Carried forward from input. |
| `monetary_value` | DOUBLE | Carried forward from input. |
| `r_quartile` | LONG | Recency quartile, 1 to 4. Higher means more recent. |
| `f_quartile` | LONG | Frequency quartile, 1 to 4. Higher means more frequent. |
| `m_quartile` | LONG | Monetary quartile, 1 to 4. Higher means higher spend. |
| `rfm_quartile` | STRING | All three quartiles encoded as a label, for example `R3F1M4`. |
| `rfm_score` | DOUBLE | Average of the three quartiles, `(r + f + m) / 3`. Ranges from 1.0 to 4.0. |
| `rfm_segment` | STRING | Named segment, for example `Champions`. See [Segment Definitions](#segment-definitions). |


Example rows:

```
user     recency  frequency  monetary_value  r_quartile  f_quartile  m_quartile  rfm_quartile  rfm_score  rfm_segment
61612    3181     4          4115            3           1           4           R3F1M4        2.6667     Promising
39408    3290     9          4893            2           4           4           R2F4M4        3.3333     Cannot lose them
21495    3326     6          2770            1           2           3           R1F2M3        2.0        High Value Sleeping
45110    3818     2          133             1           1           1           R1F1M1        1.0        Lost customers
```

**Using the output.** Join the table to your customer table so `rfm_segment`, `rfm_score`, and `rfm_quartile` become attributes available to Master Segment rules. Build audiences directly from them: a VIP audience as `rfm_segment = 'Champions'`, a win-back audience as `rfm_segment IN ('Cannot lose them', 'High Value Sleeping')`, or a suppression list as `rfm_score < 1.5`. Setting `audience_name` in the workflow can create the Master Segment and per-segment child Segments for you. Those segments push to downstream activation destinations through standard CDP activation workflows and can feed Journeys, Triggers, and Personalization.

## Segment Definitions

### How Scoring Works

Each dimension is ranked into quartiles across your entire scored base. Quartile 4 is the top 25%, quartile 1 the bottom 25%. Recency is inverted before ranking, so a recent buyer earns `r_quartile` 4 even though their raw recency number is low.

The three quartiles combine two ways. `rfm_quartile` concatenates them into a label such as `R3F1M4`, useful for exact pattern matching. `rfm_score` averages them, `(r + f + m) / 3`, giving a single number from 1.0 to 4.0 for ranking and thresholds.

Segments are then assigned from the quartile pattern. Because quartiles are relative to your own base, every run produces roughly a quarter of customers in each quartile. The segment mix, however, is not fixed: it depends on how the three dimensions correlate in your data.

### The Ten Segments

| **Segment**  | **Quartile pattern**  | **What it means and what to do**  |
|  --- | --- | --- |
| Champions | R4F4M4 | Bought recently, buy often, spend the most. Your most valuable customers. Reward them before competitors do. |
| Loyal Customers | R4F4M3, R4F3M4, R4F3M3, R3F4M4, R3F4M3, R3F3M4, R3F3M3 | Consistently active and high-value. Responsive to promotions. |
| Potential Loyalists | R4F4M2, R4F3M2, R4F2M4, R4F2M3, R4F2M2, R3F4M2, R3F3M2, R3F2M4, R3F2M3, R3F2M2 | Recent buyers who have purchased more than once and spent a good amount. Nurture toward Loyal. |
| Promising | R4F4M1, R4F3M1, R4F2M1, R4F1M4, R4F1M3, R4F1M2, R3F4M1, R3F3M1, R3F2M1, R3F1M4, R3F1M3, R3F1M2 | Recent shoppers with strong recency but lower frequency or spend so far. Build the habit. |
| New Customers | R4F1M1, R3F1M1 | Purchased recently but only once. Focus on the second purchase. |
| Cannot lose them | R2F4M4, R2F4M3, R2F3M4, R2F3M3, R2F2M4, R2F2M3, R2F1M4, R2F1M3 | Made large, frequent purchases, but a long time ago. High-value re-engagement priority. |
| Need Attention | R2F4M2, R2F3M2, R2F2M2 | Former Potential Loyalists whose engagement is declining. Intervene before they lapse. |
| Hibernating | R2F4M1, R2F3M1, R2F2M1, R2F1M2, R2F1M1 | Low recency, low frequency, low spend. Low ROI to re-engage. |
| High Value Sleeping | R1F4M4, R1F4M3, R1F4M2, R1F3M4, R1F3M3, R1F3M2, R1F2M4, R1F2M3, R1F2M2, R1F1M4, R1F1M3 | Past Potential Loyalists who have gone quiet. Worth a targeted re-activation. |
| Lost customers | R1F4M1, R1F3M1, R1F2M1, R1F1M2, R1F1M1 | Lowest scores across all three dimensions. Lowest re-engagement priority. |


### Reading the Mix

Two segments are worth watching as diagnostics rather than audiences.

**Cannot lose them and High Value Sleeping** are where retention spend earns the most. Both describe customers who were valuable and have stopped buying, which is the most recoverable form of churn.

**A very large Lost customers segment** usually points at a data problem rather than a business one. Check the maximum value in your recency column first: if it is far larger than expected, the input table is stale and everyone has drifted toward low recency. If the data is current, the segment is real and reflects acquisition quality or a high one-time purchase rate.

## Relevant Topics

### Execution Modes

RFM AI Signals can run its calculation three ways. The default is Python, which is fine for small and mid-sized bases.

| **Mode**  | **How to set it**  | **When to use it**  |
|  --- | --- | --- |
| Python | Default, set nothing | Under roughly 10 million behavior rows. |
| Trino | `use_sql: true` | Above roughly 10 million rows. Significantly faster at scale and avoids Python memory limits. |
| Hive | `use_sql: true` **and** `use_hive: true` | When environment constraints require Hive rather than Trino. `use_hive` alone has no effect. |


The output schema is identical across all three modes, so switching is safe and requires no downstream changes.

### Output Modes

RFM accepts `append` and `overwrite` for `output_mode`. In `append` mode, each run adds rows to the output table without removing existing rows. Repeated runs against the same output table can therefore produce duplicate customer rows.

The behavior of `overwrite` depends on the execution mode:

- **Python RFM always appends**, even when you request `overwrite`. Distributed workers must append so they do not replace one another's output.
- **SQL RFM respects the requested mode.** When you request `overwrite`, SQL RFM drops and recreates the output table before inserting the complete result. This applies to Trino and Hive execution.


### Monetary Value Threshold

`mv_threshold` sets a spending floor. Any customer whose `monetary_value` falls below it is assigned monetary quartile 1 regardless of where they would rank relatively.

This matters because quartiles are relative. If a large share of your base has negligible spend, free-tier signups or a single low-value order, those customers still fill the lower quartiles and push the boundaries down, compressing the range across which your genuinely valuable customers are distinguished. Setting a floor pins the negligible group at quartile 1 and lets the remaining three quartiles spread across customers who actually vary.

Set it in the same unit as `monetary_value`. There is no default: leave it unset and every customer is ranked purely relatively.

## FAQs

### Getting Started

**What data do I need?** A pre-aggregated table with one row per customer and three metrics already computed: `recency`, `frequency`, and `monetary_value`. Aim for at least 12 months of history.

**Do I need to set thresholds or train anything?** No. Quartiles are computed from your own base on every run, so the cut points come from your data.

**How often should I re-run the model?** Match your purchase cycle. Weekly suits e-commerce and retail, where churn signals appear quickly. Monthly is fine for lower-frequency categories like travel or automotive. Stale input pushes everyone toward looking inactive, so the schedule matters more than it might appear.

### Data and Quality

**What happens to missing values?** RFM AI Signals does not impute missing values today, so remove or fill nulls before running.

**Can I rename the input columns?** Only `user`, via `user_column`. The names `recency`, `frequency`, and `monetary_value` are fixed today, so alias them in the query that builds your aggregated table.

**Can I use RFM if my customers only buy once?** It will run, but the frequency dimension carries no information when everyone has the same value, which effectively reduces RFM to a two-dimensional score. Consider supplementing with behavioral engagement metrics, or use Propensity Scoring against a specific outcome instead.

**Why are so many of my customers Lost customers?** Check data freshness first. Look at the maximum value in your recency column; if it is far higher than expected, the input table is stale. If the data is current, the segment is genuine and reflects acquisition quality or a high rate of one-time purchases. See [Reading the Mix](#reading-the-mix).

### Running at Scale

**When should I enable `use_sql: true`?** Above roughly 10 million behavior rows. See [Execution Modes](#execution-modes).

**What does `use_hive` do?** It routes the calculation through Hive instead of Trino, and must be paired with `use_sql: true`. Use it only when environment constraints require Hive.

### Limits

**Can RFM predict what a customer will do next?** No. RFM is descriptive: it summarizes what has already happened. For forward-looking questions use [CLTV AI Signals](/products/customer-data-platform/machine-learning/ai-signals/cltv-ai-signals) for future value, or Propensity Scoring for a specific outcome.

**Are quartiles comparable between runs?** No. Quartiles are relative to the base scored in that run, so a customer can change quartile without changing behavior if the population around them shifts. Compare segments over time with that in mind, and use raw `recency`, `frequency`, and `monetary_value` when you need an absolute measure.

**Does RFM work for subscriptions?** Poorly. When monetary value is effectively constant across customers, as with fixed-price plans, the monetary dimension stops discriminating and RFM reduces to recency and frequency. Propensity Scoring against churn is usually the better fit.

**How do I activate segments in a campaign tool?** Build audiences from `rfm_segment`, `rfm_score`, or `rfm_quartile` in the output table. Setting `audience_name` in the workflow can create a Master Segment plus a child Segment per classification automatically. Those segments push to downstream activation destinations through standard CDP activation workflows.

## Glossary

| **Term**  | **Definition**  |
|  --- | --- |
| **Recency** | Days since a customer's most recent transaction. Lower values mean more recent, and the dimension is inverted before ranking so recent buyers earn a high quartile. |
| **Frequency** | Number of transactions in the analysis window. |
| **Monetary value** | Total spend across the analysis window, in whichever unit your business records. |
| **Quartile** | One of four equal-sized groups ranked on a single dimension. Quartile 4 is the top 25%. |
| **`rfm_score`** | The average of the three quartile ranks, from 1.0 to 4.0. |
| **`rfm_quartile`** | A label encoding all three quartiles, for example `R3F1M4`. Also available as a Master Segment attribute. |
| **`rfm_segment`** | The named business category assigned from the quartile pattern, such as Champions or Hibernating. |
| **`mv_threshold`** | A monetary floor. Customers below it are assigned monetary quartile 1 regardless of relative rank. |
| **`use_sql`** | Parameter routing the calculation through Trino instead of Python, for faster processing on large datasets. |
| **Win-back campaign** | A targeted re-engagement effort aimed at inactive but formerly valuable segments such as Cannot lose them. |
| **Master Segment** | A Treasure AI CDP construct holding all scored customers. Child Segments are subsets filtered by RFM classification. |
| **PrecisionML** | The production ML infrastructure powering RFM AI Signals, shared across the AI Signals suite. |