RFM-анализ на SQL
RFM — это сегментация клиентов по трём метрикам: Recency — как давно клиент совершал покупку/действие. Показывает, "жив" ли клиент. Frequency — как часто он покупает. Показывает уровень вовлечённости и лояльности. Monetary — сколько денег он приносит. Показывает ценность клиента.
Логика простая: клиенты, которые покупали недавно, часто и много — самые ценные, а те, кто давно не появлялся и покупал редко/мало — кандидаты на отток. На основе этого строят точечные маркетинговые действия: VIP-программы, реактивационные кампании, персональные предложения — вместо того чтобы обращаться со всей базой одинаково.
Ключевые нюансы
Метод разбиения на баллы. NTILE делит клиентов на равные по количеству группы — удобно, но если распределение сильно скошено (у многих бизнесов 80% выручки делают 5% клиентов), лучше задавать фиксированные пороги через CASE WHEN.
Направление скоринга.
Recency инвертируется — чем меньше дней прошло, тем выше балл. Frequency и Monetary — чем больше, тем выше балл. Легко ошибиться и перепутать сортировку.
Окно анализа.
Период в данных сильно влияет на результат — слишком короткое окно урезает Frequency, слишком длинное — тащит устаревшее поведение.
Что считать Monetary.
Депозиты/выручка/чистая прибыль — не одно и то же. Для бизнеса важнее не "оборот", а реальная маржинальность клиента.
Статичность.
Классический RFM — это снимок на момент расчёта, а не прогноз. Он не предсказывает будущее поведение, только описывает прошлое — поэтому его часто комбинируют с churn-моделями или LTV-прогнозами.
Присвоение сегментов через CASE в конце — это ручная бизнес-логика, пороги нужно подбирать под конкретную отрасль и объём базы, а не использовать универсальный шаблон.
Дальше — сам SQL-запрос, который считает метрики, переводит их в баллы и присваивает сегменты.
WITH customer_metrics AS (
SELECT
customer_id,
DATE_PART('day', CURRENT_DATE - MAX(order_date)) AS recency_days,
COUNT(DISTINCT order_id) AS frequency,
SUM(order_amount) AS monetary
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '365 days'
GROUP BY customer_id
),
rfm_scores AS (
SELECT
customer_id,
recency_days,
frequency,
monetary,
-- Чем меньше recency_days, тем выше балл (5 = купил недавно)
NTILE(5) OVER (ORDER BY recency_days DESC) AS r_score,
NTILE(5) OVER (ORDER BY frequency ASC) AS f_score,
NTILE(5) OVER (ORDER BY monetary ASC) AS m_score
FROM customer_metrics
),
rfm_segments AS (
SELECT
customer_id,
recency_days,
frequency,
monetary,
r_score,
f_score,
m_score,
CONCAT(r_score, f_score, m_score) AS rfm_cell,
(r_score + f_score + m_score) AS rfm_total
FROM rfm_scores
)
SELECT
customer_id,
recency_days,
frequency,
monetary,
r_score,
f_score,
m_score,
rfm_cell,
rfm_total,
CASE
WHEN r_score >= 4 AND f_score >= 4 AND m_score >= 4 THEN 'Champions'
WHEN r_score >= 3 AND f_score >= 3 THEN 'Loyal Customers'
WHEN r_score >= 4 AND f_score <= 2 THEN 'New Customers'
WHEN r_score <= 2 AND f_score >= 3 THEN 'At Risk'
WHEN r_score <= 2 AND f_score <= 2 AND m_score <= 2 THEN 'Lost'
ELSE 'Others'
END AS segment
FROM rfm_segments
ORDER BY rfm_total DESC;
Как это работает customer_metrics — считает базовые метрики для каждого клиента: сколько дней прошло с последнего заказа, сколько всего заказов и на какую сумму. rfm_scores — с помощью NTILE(5) разбивает клиентов на 5 равных групп (квинтилей) по каждой метрике, получая баллы от 1 до 5. rfm_segments — объединяет баллы в код ячейки (например, "555" — лучшие клиенты) и суммарный балл. Финальный запрос — присваивает понятные названия сегментов на основе баллов.
Где я использовал
Трэвел: для отелей и туристических агентств RFM анализирует актуальность (последнее пребывание), частоту (повторные визиты) и денежную ценность (расходы), помогая выявлять лояльных клиентов для специальных предложений и увеличивая количество повторных бронирований.
Электронная коммерция: RFM в электронной коммерции помогает выявлять ценных клиентов на основе недавних и частых покупок, сегментируя их для целевых акций, чтобы стимулировать повторные покупки и увеличивать продажи за счёт персонализированных предложений.