# RFM Customer Segmentation

Automatically segment your customer base by purchasing behavior — so every campaign reaches the right audience at the right time.

## Overview and Use Cases

RFM AI Signals from Treasure AI's machine learning platform (AI Signal) automatically segments your customer base by scoring each customer on three dimensions from their transaction history — Recency (days since last purchase), Frequency (number of purchases), and Monetary value (total spend) — then maps those scores to ten industry-standard segments such as Champions, Loyal Customers, and Lost Customers. The segments feed directly into Master Segments for use in personalization, suppression, win-back campaigns, and loyalty programs, giving marketing and CRM teams a fast, actionable view of who is drifting away and who is primed to buy again — without writing SQL or building a custom model.

Common use cases:

* Identify and reward your top Champions with VIP offers before they are tempted by a competitor
* Trigger automated win-back campaigns for "Cannot Lose Them" customers who have gone quiet
* Suppress low-priority "Lost Customers" from expensive paid media to reduce wasted spend
* Calibrate email frequency — weekly for Champions, major campaigns only for Hibernating segments
* Feed RFM scores as input features into downstream CLTV and churn propensity 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.

## Model Configuration

### Training Inputs

RFM AI Signals accepts a pre-aggregated customer-level table. Each row represents one customer, already summarized across all transactions.

| **Field**  | **Type**  | **Required**  | **Description**  |
|  --- | --- | --- | --- |
| `user` | VARCHAR | Yes | Unique customer identifier. The column name can be customized via the `user_column` parameter. |
| `recency` | INTEGER | Yes | Days since the customer's most recent transaction. |
| `frequency` | INTEGER | Yes | Total number of transactions in the analysis window. |
| `monetary_value` | DOUBLE | Yes | Total spend across all transactions. |


Data quality requirements
RFM AI Signals does not perform missing value handling automatically. Pre-process your data to remove or impute null values in `recency`, `frequency`, and `monetary_value` before running the model. Negative or zero values will produce incorrect scores and must be handled upstream.

### Training Outputs

The model writes the following columns to the output table for each customer record.

| **Field**  | **Type**  | **Description**  |
|  --- | --- | --- |
| `user` | STRING | Customer identifier, carried forward from input. |
| `recency` | LONG | Days since most recent transaction. |
| `frequency` | LONG | Total transaction count. |
| `monetary_value` | DOUBLE | Total spend. |
| `r_quartile` | LONG | Recency rank (1–4). 4 = most recent quartile. |
| `f_quartile` | LONG | Frequency rank (1–4). 4 = most frequent quartile. |
| `m_quartile` | LONG | Monetary rank (1–4). 4 = highest spend quartile. |
| `rfm_quartile` | STRING | Concatenated quartile label (e.g., `R3F1M4`). Also added as an attribute column on the Master Segment when `audience_name` is configured. |
| `rfm_score` | DOUBLE | Average of the three quartile scores: `(r + f + m) / 3`. Ranges from 1.0 to 4.0. |
| `rfm_segment` | STRING | Named business segment (e.g., `Champions`, `Hibernating`). See segment definitions below. |


### Example Output

A sample of the RFM prediction table after a model run:

| **user**  | **recency**  | **frequency**  | **monetary_value**  | **r_quartile**  | **f_quartile**  | **m_quartile**  | **rfm_quartile**  | **rfm_score**  | **rfm_segment**  |
|  --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `3105285968` | 10 | 8 | 4115.00 | 4 | 4 | 4 | R4F4M4 | 4.0000 | Champions |
| `1850985734` | 20 | 5 | 1456.00 | 2 | 3 | 2 | R2F3M2 | 2.3333 | Need attention |
| `274382808` | 95 | 2 | 200.12 | 2 | 1 | 1 | R2F1M1 | 1.3333 | Hibernating |
| `358273144` | 310 | 6 | 2770.00 | 1 | 2 | 3 | R1F2M3 | 2.0000 | High Value Sleeping |
| `45110` | 400 | 1 | 133.00 | 1 | 1 | 1 | R1F1M1 | 1.0000 | Lost customers |


Reading the output, `rfm_score` is the arithmetic mean of the three quartile values. A score of 4.0 is a Champion; 1.0 is a Lost customer. The `rfm_quartile` string encodes each dimension explicitly — `R2F1M1` means moderate recency, low frequency, and low spend. When `audience_name` is configured, `rfm_quartile` is also written as an attribute column on the Master Segment, enabling direct filtering in the CDP segment builder.

### Parameters

| **Parameter**  | **Type**  | **Default**  | **Required**  | **Description**  |
|  --- | --- | --- | --- | --- |
| `input_table` | string | — | Yes | Source table in `dbname.table_name` format (e.g., `ml_dataset.td_rfm`). Must contain pre-aggregated RFM columns. |
| `output_table` | string | — | Yes | Destination table in `dbname.table_name` format (e.g., `ml_output.rfm`). Scored results are written here. |
| `output_mode` | string | `append` | No | Write mode for the output table. Use `append` to add rows across runs, or `replace` to overwrite with the latest scores only. |
| `user_column` | string | `user` | No | The column name in your input table that holds the customer identifier. Change this if your table uses a different name (e.g., `customer_id`). |
| `use_sql` | boolean | `false` | No | When `true`, RFM calculations run as Trino SQL queries rather than in Python. Strongly recommended for large datasets — significantly faster at scale. |
| `use_hive` | boolean | `false` | No | When `true` (in combination with `use_sql: true`), calculations run via Hive instead of Trino. Use only if your environment requires Hive compatibility. |
| `mv_threshold` | float | — | No | Customers whose `monetary_value` falls below this threshold are assigned to monetary quartile 1 (`m_quartile = 1`), regardless of their actual relative rank. Useful for filtering out low-value or near-zero transactions. Example: `0.05`. |


**Tuning notes:**

* **`use_sql: true`** — Enable for any dataset exceeding 10 million behavior rows. It eliminates Python memory overhead and cuts runtime substantially — see the Scalability section for benchmarks.
* **`mv_threshold`** — Set when your transaction data includes free-tier signups or negligible purchases that would otherwise distort quartile boundaries.
* **`output_mode: append`** — Safe for scheduled runs since each run adds a new scored snapshot. Use `replace` only when you want a single current-state table.


## Example Workflow Code

RFM AI Signals runs via the ML Batch API, called from a Treasure Workflow. The workflow below is a complete, copy-pasteable example.

```yaml
# rfm_signal_workflow.dig
# Treasure Workflow: RFM AI Signals (PrecisionML)
# Submits an RFM scoring job to the ML Batch API, then polls
# for completion before proceeding to downstream steps.

# Step 1: Submit the RFM job to the ML Batch API
+run_rfm:
  http>: https://ml-batch-api.treasuredata.com/v1/runs/
  method: POST
  headers:
    - authorization: ${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.aggregated_customers
    output_table: your_database.rfm_output
    solution_name: rfm
    solution_arguments:
      user_column: user
      use_sql: true
      # mv_threshold: 0.05   # Uncomment to suppress near-zero monetary values

# Step 2: Log the API response (job ID used for polling)
+print_response:
  echo>: "Job submitted. Response: ${http.last_content}"

# Step 3: Poll until the job is complete
# The API returns HTTP 408 while the job is running — Treasure Workflow
# automatically retries this step 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: ${secret:td.apikey}

# Step 4: Confirm completion
+finished:
  echo>: "RFM scoring complete. Results written to your_database.rfm_output"
```

**What this workflow does:** The workflow submits an RFM configuration to the ML Batch API and receives a job ID in the response. It then polls the status endpoint in a retry loop — the API returns HTTP 408 while the job is running, and Treasure Workflow retries automatically until it receives HTTP 200. Once complete, the scored output table is ready for downstream activation or audience creation.

Security
Store your TD API key as a Workflow secret named `td.apikey`. Never hardcode API keys in a workflow definition or commit them to version control.

## Segment Definitions

RFM AI Signals maps quartile combinations to ten industry-standard segments. Higher quartile values indicate better performance on that dimension.

| **Segment**  | **Description**  | **Quartile Pattern**  |
|  --- | --- | --- |
| Champions | Bought recently, buy often, spend the most. Your most valuable customers. | R4F4M4 |
| Loyal Customers | Consistently active and high-value. Responsive to promotions. | R3–4, F3–4, M3–4 (excl. Champions) |
| Potential Loyalists | Recent buyers who have purchased more than once and spent a good amount. | R3–4, F2–4, M2–4 |
| Promising | Recent shoppers with strong recency but lower frequency or spend so far. | R3–4, F1–4, M1–2 |
| New Customers | Purchased recently but only once. | R3–4, F1, M1 |
| Cannot Lose Them | Made large, frequent purchases — but a long time ago. High-value re-engagement priority. | R1–2, F3–4, M3–4 |
| Need Attention | Former Potential Loyalists whose engagement is declining. | R2, F2–4, M2 |
| Hibernating | Low recency, low frequency, low spend. Low ROI to re-engage. | R2, F1–4, M1–2 |
| High Value Sleeping | Past Potential Loyalists who have gone quiet. Worth a targeted re-activation. | R1, F2–4, M2–4 |
| Lost Customers | Lowest scores across all three dimensions. Lowest re-engagement priority. | R1, F1–3, M1–2 |


## Scalability

RFM AI Signals is designed to handle enterprise-scale datasets. The table below shows measured runtimes across varying dataset sizes. Enable `use_sql: true` for any dataset above 10 million behavior rows.

| **Behavior Rows**  | **Customer Rows**  | **Runtime**  | **Peak Memory per Task**  |
|  --- | --- | --- | --- |
| 1 million | 313 thousand | ~4 min | 4.4 GiB |
| 10 million | 3.13 million | ~12 min | 5.4 GiB |
| 100 million | 31.3 million | ~89 min | 37.9 GiB |
| 500 million | 156 million | ~445 min | 182.8 GiB |
| 800 million (8 parallel tasks) | 250 million | ~96 min | 37.3 GiB |


For very large datasets, parallelizing across 8 tasks (the maximum concurrency per account) can reduce wall-clock time dramatically — the 800 million row case dropped from ~445 minutes to ~96 minutes using parallel execution. Contact your Treasure AI account team if your dataset regularly exceeds 500 million behavior rows to discuss infrastructure options.

## Limitations and Known Issues

* **Works best with at least 12 months of transaction history.** Shorter windows compress the recency signal and make new customers hard to distinguish from returning ones.
* **Quartile ranking is relative, not absolute.** A "Champion" in a low-activity period may spend less than a "Loyal Customer" in a high-activity period. Interpret segment labels in the context of when the model was run.
* **Not designed for subscription or SaaS products.** When monetary value is flat across customers (e.g., fixed-price subscriptions), the M dimension loses predictive power. Consider substituting engagement metrics (logins, feature usage) in a pre-aggregated input instead.
* **Does not predict future behavior.** RFM describes past patterns. For forward-looking CLTV forecasting, combine RFM scores with probabilistic models (BG/NBD, Gamma-Gamma) as downstream features.
* **Sensitive to data freshness.** If the input table is not kept current, recency scores drift and segments become stale. Schedule workflow runs to match your campaign cadence — weekly for fast-moving retail, monthly for lower-frequency categories.
* **Pre-aggregated input requires clean data.** Missing value handling is not performed automatically. Null or negative values in `recency`, `frequency`, or `monetary_value` will produce incorrect scores and must be handled upstream.


## Glossary

| **Term**  | **Definition**  |
|  --- | --- |
| Recency | The number of days since a customer's most recent transaction. Lower values mean the customer purchased more recently. |
| Frequency | The total count of transactions a customer has made within the analysis window. |
| Monetary Value | The total amount a customer has spent across all transactions in the analysis window. |
| Quartile | One of four equal-sized groups created by ranking all customers on a single dimension. Customers in quartile 4 are in the top 25% for that dimension. |
| RFM Score | The average of a customer's three quartile ranks: `(r + f + m) / 3`. Ranges from 1.0 (lowest) to 4.0 (highest). |
| RFM Segment | A named business label (e.g., "Champions", "Hibernating") assigned based on a customer's combined quartile pattern. |
| `rfm_quartile` | A string label encoding all three quartile values (e.g., `R3F1M4`). Also written as an attribute on the Master Segment when audience creation is enabled. |
| Master Segment | A Treasure AI CDP construct that holds all customers scored by the model. Child Segments are subsets filtered by `rfm_segment`. |
| PrecisionML | The production ML infrastructure that powers RFM AI Signals. It is optimized for scale and replaces the earlier AutoML-based implementation. |
| `mv_threshold` | A monetary value floor. Customers spending below this amount are assigned to the lowest monetary quartile, regardless of their relative rank among all customers. |
| `use_sql` | A parameter that routes RFM calculations through Trino SQL rather than Python, enabling faster processing for large datasets. |
| Win-back Campaign | A targeted campaign aimed at re-engaging customers who have not purchased recently — typically directed at "Cannot Lose Them" or "High Value Sleeping" segments. |


## FAQs

| **Question**  | **Answer**  |
|  --- | --- |
| How often should I re-run the RFM model? | It depends on your customers' typical purchase frequency. For e-commerce or retail, a weekly schedule keeps segments fresh enough to catch early churn signals. For lower-frequency categories (travel, automotive), monthly runs are sufficient. You can schedule the workflow directly in Treasure Workflow Console. |
| When should I enable `use_sql: true`? | Enable it for any dataset with more than 10 million behavior rows. SQL-based execution runs RFM calculations directly in Trino, bypassing Python memory limits and dramatically reducing runtime. For smaller datasets, the default Python execution is fine and requires no configuration change. |
| What does `mv_threshold` do and when should I use it? | `mv_threshold` sets a minimum monetary value floor. Any customer spending below that threshold is assigned to monetary quartile 1, regardless of how they rank relative to other customers. Use it when your transaction data includes free-tier signups, near-zero purchases, or promotional credits that would otherwise distort segment assignments — for example, `mv_threshold: 0.05` prevents $0.01 transactions from influencing quartile boundaries. |
| Can I use RFM AI Signals if my customers only buy once? | RFM works best with repeat-purchase data. If most customers have a frequency of 1, the F dimension will be nearly uniform and add little signal. In this case, recency and monetary value carry most of the weight. Consider enriching the model with behavioral engagement data (site visits, email opens) passed in as a pre-aggregated input. |
| How do I activate RFM segments in a campaign tool? | The output table contains an `rfm_segment` column for every customer after the model run. If you configure `audience_name` in the workflow, the model automatically creates a Master Segment and one child Segment per `rfm_segment` value in the CDP. These Segments can then be pushed to downstream activation destinations through standard CDP activation workflows. |
| Why is a large portion of my customers showing as "Lost Customers"? | A very large Lost segment usually means one of two things: your input table has not been refreshed recently (making everyone appear inactive by recency), or your business genuinely has a high one-and-done purchase rate. Check the maximum recency value in your input table to confirm data freshness before investigating acquisition quality. |