RFM-анализ — что это, формула, пример SQL для сегментации

RFM-анализ — метод сегментации клиентов по 3 параметрам: Recency (давность последней покупки), Frequency (частота покупок), Monetary (сумма покупок). Используется в e-comm маркетинге для таргетинга: VIP / Loyal / At Risk / Lost. Здесь — формула, SQL и Python.
Содержание (5 разделов)
  1. Что такое RFM
  2. Как считать RFM-скор
  3. Стандартные сегменты
  4. Python pandas вариант
  5. Применение в реальном маркетинге

Что такое RFM

3 параметра клиента:

Как считать RFM-скор

Каждому клиенту присваиваем скор от 1 до 5 по каждому параметру через NTILE или процентили:

WITH customer_metrics AS (
  SELECT
    customer_id,
    CURRENT_DATE - MAX(order_date) AS days_since_last_order,
    COUNT(*) AS total_orders,
    SUM(order_amount) AS total_revenue
  FROM orders
  GROUP BY customer_id
),
rfm_scores AS (
  SELECT
    customer_id,
    -- Reverse для Recency (меньше дней = выше скор)
    6 - NTILE(5) OVER (ORDER BY days_since_last_order) AS R,
    NTILE(5) OVER (ORDER BY total_orders) AS F,
    NTILE(5) OVER (ORDER BY total_revenue) AS M
  FROM customer_metrics
)
SELECT
  customer_id, R, F, M,
  CONCAT(R, F, M) AS rfm_code,
  CASE
    WHEN R >= 4 AND F >= 4 AND M >= 4 THEN 'Champions'
    WHEN R >= 3 AND F >= 3 AND M >= 3 THEN 'Loyal'
    WHEN R >= 4 AND F < 3 THEN 'New Customers'
    WHEN R < 2 AND F >= 3 THEN 'At Risk'
    WHEN R < 2 AND F < 3 THEN 'Lost'
    ELSE 'Average'
  END AS segment
FROM rfm_scores;

Стандартные сегменты

Топ-10 сегментов по RFM-коду:

СегментRFM-кодДействие
Champions555, 554VIP-программа, эксклюзивы
Loyal Customers454, 553Реферальная программа
Potential Loyalists543, 542Cross-sell, upsell
New Customers511, 512Welcome series, onboarding
Promising512, 521Бренд-awareness email
At Risk244, 144Reactivation кампания + бонус
Cannot Lose Them155, 254Personal call, премиум-сервис
Lost111, 112Reactivation скидка 50%+

Python pandas вариант

Тот же расчёт на pandas:

import pandas as pd

def rfm_analysis(orders: pd.DataFrame, today=pd.Timestamp.now()) -> pd.DataFrame:
    # Метрики на клиента
    metrics = orders.groupby('customer_id').agg(
        recency_days=('order_date', lambda x: (today - x.max()).days),
        frequency=('order_id', 'count'),
        monetary=('order_amount', 'sum'),
    ).reset_index()

    # RFM скоры (1-5)
    metrics['R'] = pd.qcut(metrics['recency_days'], 5, labels=[5,4,3,2,1])
    metrics['F'] = pd.qcut(metrics['frequency'].rank(method='first'), 5, labels=[1,2,3,4,5])
    metrics['M'] = pd.qcut(metrics['monetary'].rank(method='first'), 5, labels=[1,2,3,4,5])

    # RFM код
    metrics['RFM'] = metrics['R'].astype(str) + metrics['F'].astype(str) + metrics['M'].astype(str)

    # Сегмент
    metrics['Segment'] = 'Average'
    metrics.loc[(metrics['R']>=4) & (metrics['F']>=4) & (metrics['M']>=4), 'Segment'] = 'Champions'
    metrics.loc[(metrics['R']<=2) & (metrics['F']<=2), 'Segment'] = 'Lost'
    # ... etc

    return metrics

Применение в реальном маркетинге

Реальные кейсы из РФ-компаний:

Частые вопросы

Сколько сегментов делать?

Классически — 5×5×5 = 125 возможных кодов, группируются в 8-12 actionable сегментов. Меньше — теряется детализация, больше — нечего делать с микро-группами.

Что лучше: 1-5 или процентили?

NTILE(5) даёт равные группы по 20%. Custom thresholds (например, R=5 if days<7) — более интерпретируемые. На production используют второе.

Зачем reverse в Recency?

Меньше дней с последней покупки = более активен = должен иметь высокий R-скор. NTILE(5) ORDER BY days даёт ASC (1 = меньше дней), поэтому делаем 6-NTILE = 5 для свежих покупок.

Как часто пересчитывать RFM?

Раз в месяц для большинства бизнесов. Для fast-moving (food delivery) — раз в неделю. Для luxury — раз в квартал.

RFM vs cohort analysis?

RFM сегментирует клиентов в моменте (snapshot). Когорты отслеживают изменения во времени. RFM используется для action (на кого таргетить), когорты — для measurement (что меняется).

Начать практику бесплатно →