RFM-анализ с ИИ: расчёт и проверка сегментов

Считаем RFM-анализ по учебным транзакциям: давность, частоту и сумму покупок. CSV, скрипт, возвраты и граничные клиенты для независимой проверки сегментов.

RFM-анализ по CSV можно сделать проверяемым, если сначала зафиксировать, что считать покупкой и на какую дату считать давность, затем рассчитать R/F/M по этим правилам и сверить результат с контрольной таблицей. Ниже — учебный пример с полными транзакциями и ожидаемыми значениями. Расчёт скриптом и сверка выполнены локально; предложенный запрос к ИИ не запускался, поэтому этот пример не проверяет качество ответа модели или эффект маркетинговых действий.

Определите покупку и срез

Учебный пример. Все данные и условия синтетические: это не клиентский кейс. Полный комплект со скриптом run.py — в ZIP-архиве.

Microsoft описывает RFM как инструмент оценки данных, возникающих из покупок клиентов. Документация Microsoft о RFM

В трёхзначной оценке R обозначает давность последней покупки, F — частоту покупок, M — сумму расходов клиента. Документация Microsoft о RFM

В расчёте дата среза — 1 апреля 2026 года, UTC. Окно покупок полуоткрытое: от 1 января включительно до 1 апреля не включительно — [2026-01-01, 2026-04-01). Давность — число дней между датой последней подходящей покупки и датой среза.

Правила ниже относятся только к этому учебному расчёту.

Правило Как поступаем
Подходящая покупка Заказ со статусом paid, известным клиентом, датой в окне и net-суммой после возврата больше нуля
Несколько позиций заказа Складываем разные позиции одного order_id; для F считаем заказ один раз
Повтор строки Точный дубль с тем же line_id второй раз не учитываем
Частичный возврат Уменьшает net-сумму заказа; дата исходной покупки сохраняется
Полный возврат При net-сумме 0 заказ не входит в R, F и M
Нет customer_id Отдельное исключение; клиента по догадке не назначаем
Пустая date Отдельное исключение с неизвестной датой; дату не восстанавливаем по соседним строкам
Возвраты вне смоделированных условий Возвраты с датой вне окна и возвраты по покупкам до начала окна здесь не моделируются; для них нужны отдельные правила отнесения к периоду

Это политика данного примера, а не универсальное правило CRM. В частности, возвраты с датой вне окна и возвраты по покупкам до начала окна требуют отдельно заданной политики.

Полный входной transactions.csv:

line_id,order_id,customer_id,date,amount_rub,status,refund_of
l1,o1,A,2026-03-22,1000,paid,
l2,o1,A,2026-03-22,500,paid,
l2,o1,A,2026-03-22,500,paid,
l3,o2,A,2026-03-02,500,paid,
r1,o2,A,2026-03-20,-200,refund,o2
l4,o3,B,2026-03-01,1000,paid,
l5,o4,C,2026-01-31,3000,paid,
l6,o5,D,2026-01-30,999,paid,
l7,o6,E,2026-03-31,700,paid,
r2,o6,E,2026-03-31,-700,refund,o6
l8,o7,F,2026-03-25,800,cancelled,
l9,o8,,2026-03-25,200,paid,
l10,o9,H,2025-12-31,600,paid,
l11,o10,I,2026-04-01,500,paid,
l12,o11,J,2026-03-10,700,paid,
r3,o11,J,2026-03-15,-200,refund,o11
l13,o12,K,2026-03-02,1000,paid,
l14,o13,K,2026-03-01,1000,paid,
l15,o14,K,2026-02-10,999,paid,
l16,o15,G,,400,paid,

Чтобы сохранить в результате и клиентов без подходящих покупок, используем список всех 11 клиентов:

customer_id
A
B
C
D
E
F
G
H
I
J
K

У o1 две разные позиции по 1 000 и 500 рублей: складываем их в одну покупку на 1 500 рублей. Повтор line_id=l2 второй раз не прибавляем. У o2 после возврата 200 рублей net-сумма равна 300. Возврат связываем с исходной покупкой через refund_of.

Рассчитайте R/F/M

Для каждого клиента расчёт берёт дату последней подходящей покупки, считает дни до даты среза, количество уникальных подходящих заказов и сумму net по этим заказам. В этом примере частичный возврат уменьшает Monetary, но не меняет дату исходной покупки.

После исключения отменённого o7 промежуточная таблица orders-net.csv содержит 10 заказов с известным клиентом и датой внутри окна. В ней оставлен и o6 с нулевой net-суммой: он не подходит для расчёта R/F/M.

order_id,customer_id,purchase_date,gross_rub,refund_rub,net_rub,qualifies
o1,A,2026-03-22,1500,0,1500,True
o2,A,2026-03-02,500,-200,300,True
o3,B,2026-03-01,1000,0,1000,True
o4,C,2026-01-31,3000,0,3000,True
o5,D,2026-01-30,999,0,999,True
o6,E,2026-03-31,700,-700,0,False
o11,J,2026-03-10,700,-200,500,True
o12,K,2026-03-02,1000,0,1000,True
o13,K,2026-03-01,1000,0,1000,True
o14,K,2026-02-10,999,0,999,True

Из этих 10 заказов 9 подходят для расчёта R/F/M: у o6 полный возврат, net-сумма равна нулю.

В этом примере R — давность последней покупки в днях, F — число уникальных подходящих заказов, M — сумма net по ним. У A последняя подходящая покупка была за 10 дней до среза; у клиента два заказа и 1 800 рублей после возвратов. Исходные значения A — 10 дней, 2 заказа и 1 800 рублей.

Полный ожидаемый результат для всех 11 клиентов:

customer_id,recency_days,frequency,monetary_rub,rfm,status
A,10,2,1800,322,active
B,31,1,1000,212,active
C,60,1,3000,213,active
D,61,1,999,111,active
E,,0,0,,no_qualifying_purchase
F,,0,0,,no_qualifying_purchase
G,,0,0,,no_qualifying_purchase
H,,0,0,,no_qualifying_purchase
I,,0,0,,no_qualifying_purchase
J,22,1,500,311,active
K,30,3,2999,332,active

Основной контрольный файл — expected_rfm.csv. В комплекте также есть старый expected-rfm.csv: это побайтово совпадающий совместимый alias той же контрольной таблицы, а не второй расчёт. run.py сверяет результат с каноническим expected_rfm.csv; если canonical-файла нет, восстанавливает его из alias. Если два файла с разными именами содержат разные данные, скрипт останавливается с ошибкой.

Задайте границы

В расчёте используются фиксированные условные пороги:

  • R=3, если давность не больше 30 дней; R=2, если она от 31 до 60 дней включительно; иначе R=1.
  • F=1 для одного заказа, F=2 для двух, F=3 для трёх и более.
  • M=1 при сумме меньше 1 000 рублей; M=2 от 1 000 до суммы меньше 3 000 рублей; M=3 при сумме от 3 000 рублей.

Скрипт применяет именно эти границы: он не вычисляет квантили и не подбирает пороги по положению клиентов в базе. Для сравнения, в документации Microsoft настройка Even distribution предназначена для равного числа клиентов в каждой секции оценки. Документация Microsoft о RFM

В примере Microsoft при 100 клиентах и пяти группах в каждой оказывается по 20 человек; самые недавние 20 клиентов получают оценку давности 5. Документация Microsoft о RFM

Это настройка с равным числом клиентов в группах, а не правило данного скрипта. Здесь используется шкала из трёх баллов, а границы заданы числами дней, заказов и рублей. Ни эти пороги, ни приведённый пример Microsoft не объявляются отраслевой нормой.

Пограничные значения проверяем по строкам результата. У B давность 31 день, у C — 60, у D — 61; оценки R у них соответственно 2, 2 и 1. У K давность ровно 30 дней, три заказа и 2 999 рублей, поэтому код — 332. У C сумма ровно 3 000 рублей, поэтому M равен 3.

ИИ объясняет, расчёт проверяет

В этом процессе агент может собрать файлы, проверить структуру и строки, запустить run.py, сопоставить результат с контрольными таблицами и объяснить расхождения. Числовые значения при этом рассчитывает скрипт по зафиксированным правилам. Это предложенный порядок работы, а не гарантия безошибочного поведения модели.

Человеку не нужно вручную вычитывать каждую единицу данных. Его задача — сообщить недостающие бизнес-факты и принять решения по исключениям, для которых правила не заданы. Если у агента нет инструмента для запуска кода, он должен обозначить, что расчёт не выполнен, и перечислить, что осталось запустить и сверить отдельно.

В этом run.py нет расчёта вероятности оттока или прогноза следующей покупки. Код выдаёт значения R/F/M по истории покупок и заданной политике. Сумма Monetary — сумма net по прошлым покупкам в этом расчёте; она сама по себе не гарантирует будущую выручку или возврат денег. Любое предлагаемое маркетинговое действие для сегмента — гипотеза для отдельной проверки, а не результат этого расчёта.

Ниже — полный шаблон запроса для агента, который работает с файлами комплекта. Это предложение для применения, а не выполненный запрос: модель его не запускала.

Используй customers.csv, transactions.csv, rules.json, run.py, orders-net.csv, excluded.csv, rfm.csv, expected_rfm.csv и expected-excluded.csv.

Проверь входные файлы и рассчитай R/F/M только по записанным правилам. Не меняй данные или правила, чтобы подогнать результат под контрольную таблицу.

Сначала проверь заголовки и наличие столбцов. Просмотри все строки transactions.csv: найди точные дубли line_id, проверь статусы, customer_id, даты, amount_rub и связи возвратов через refund_of. Не восстанавливай отсутствующие customer_id или date догадкой.

Примени правила из rules.json: окно [2026-01-01, 2026-04-01), UTC; точный дубль line_id учитывай один раз; разные позиции одного order_id складывай; F считай по уникальным заказам с net больше нуля. Частичный возврат уменьшает net и сохраняет дату исходной покупки; полный возврат с net 0 исключает заказ из R/F/M. Пустую дату и отсутствие customer_id записывай в разные исключения. Не считай их доказательством, что клиент никогда не покупал.

Возвраты с датой вне окна и возвраты по покупкам до начала окна в этом наборе не моделируются. Не придумывай для них правило. Если для интерпретации нужен такой случай, перечисли вопрос владельцу данных.

Если доступен запуск Python, запусти run.py и сравни результат с expected_rfm.csv и expected-excluded.csv. Проверь также orders-net.csv, excluded.csv и rfm.csv. Если запуска кода нет, прямо укажи, что расчёт не запускался и что осталось выполнить отдельно.

Основное имя контрольного файла — expected_rfm.csv. expected-rfm.csv может находиться рядом как побайтово совпадающий legacy alias. Если оба файла присутствуют, проверь, что их байты совпадают; при различии останови сверку и сообщи об ошибке.

При каждом расхождении укажи файл, строку или клиента, фактическое значение и правило, с которым оно не совпало. Отдельно проверь границы B, C, D и K; полный возврат E; частичный возврат J; пустую дату G; строку без customer_id; дубль l2.

В объяснении каждой группы отделяй наблюдаемое значение от применённого правила и полученного R, F или M. Если для вывода не хватает бизнес-факта, назови его и сформулируй вопрос владельцу данных.

Не приписывай клиентам вероятность оттока, будущую покупку или гарантированный возврат денег. Любое предложенное маркетинговое действие обозначь как гипотезу для отдельной проверки.

В этом учебном примере скрипт и сверка с контрольными файлами выполнены локально; запрос к модели не запускался. Поэтому результат показывает расчёт и контроль на заданных данных, но не качество ответа ИИ.

Проверьте ошибки

В excluded.csv записаны все шесть исключённых строк и причины, по которым они не попали в расчёт:

row,line_id,order_id,customer_id,date,amount_rub,reason
3,l2,o1,A,2026-03-22,500,duplicate_line_id
11,l8,o7,F,2026-03-25,800,cancelled
12,l9,o8,,2026-03-25,200,missing_customer
13,l10,o9,H,2025-12-31,600,outside_window
14,l11,o10,I,2026-04-01,500,outside_window
20,l16,o15,G,,400,missing_date

У l9/o8 нет customer_id, поэтому строку нельзя отнести к клиенту. У l16/o15/G другая проблема: пустая date. Это неизвестная дата, а не ноль, не дата соседней строки и не доказательство, что покупка была вне окна. Заказ не участвует в R, F и M, но клиент G остаётся в общей таблице.

Статус no_qualifying_purchase означает, что в заданном учебном окне не нашлось подходящей покупки с известной датой. Это не означает, что клиент никогда не покупал. Отменённый o7 исключён по статусу; o9 датирован до начала окна, а o10 — датой окончания, которая в окно не входит. По правилам комплекта непустая дата, не соответствующая формату ISO, должна останавливать запуск с ошибкой, а не заменяться другим значением.

Перед использованием объяснения агента сверьте результат с контрольными строками:

  1. Во входе 20 строк: 14 учтены после обработки правил, 6 записаны в исключения.
  2. Повтор line_id=l2 не учтён второй раз; разные позиции заказа o1 сложены в 1 500 рублей.
  3. F считает уникальные заказы, а не строки: у A два заказа, у K — три.
  4. Частичные возвраты по o2 и o11 уменьшили net до 300 и 500 рублей и не изменили даты исходных покупок.
  5. Полный возврат по o6 дал net 0 и исключил заказ из R, F и M.
  6. Границы R проверены: 30 дней дают R=3, 31 и 60 дней — R=2, 61 день — R=1.
  7. Граница M проверена: 2 999 рублей дают M=2, 3 000 рублей — M=3.
  8. Отмена, отсутствие клиента, выход даты за окно и пустая дата имеют разные причины исключения.
  9. Пустая дата G не превращена в ноль и не восстановлена по соседним строкам.
  10. В expected_rfm.csv есть все 11 клиентов, включая клиентов без подходящей покупки в учебном окне.
  11. В расчёте 9 подходящих заказов, сумма Monetary — 10 298 рублей, активны 6 клиентов.
  12. Баланс входных строк: 20 = 14 + 6. Баланс входных сумм: 13 298 = 10 298 + 3 000. Это сверка данных, а не выручка за период.
  13. Результат сверяется с expected_rfm.csv и expected-excluded.csv; пропущенная дата не назначается заказу или сегменту.

Эти итоги относятся только к заданным учебным данным. Перед расчётом на реальных данных отдельно задайте политику для возвратов с датой вне окна и для возвратов по покупкам до начала окна: этот пример такие случаи не моделирует.

Если ошибка в данных меняет отнесение клиента к сегменту, продолжение — материал о сегментации клиентов с ИИ. После сверки таблицы можно перейти к разбору маркетинговых данных с нейросетью.

Валерий Курземнек

Автор метода «Машиночитаемый маркетинг»

Что ещё почитать