Урок курса
Метрики рекламы: CPA и ROAS — определения и знаменатели
Практическая аналитика данныхПредыдущий урок закончился на анализе клиентского удержания — когортах и retention rate. Теперь задача другая: оценить, во сколько обходится привлечение каждого нового клиента через рекламу и сколько выручки приносит каждый вложенный рубль. Это CPA и ROAS — два числа, с которых начинается любой разговор об эффективности рекламного канала.
Формула CPA: числитель, знаменатель и единица измерения
CPA (Cost per Acquisition) — стоимость привлечения одного нового клиента через рекламу. Формула:
CPA = spend / new_customers
Где:
spend— рекламные расходы из таблицыad_spendза выбранный месяц и канал (полеchannel).new_customers— число уникальныхcustomer_id, у которых первыйpaidзаказ пришёлся именно на этот месяц и именно в этом канале.
Результат измеряется в рублях на одного нового клиента. Это не усреднённая стоимость любого заказа, а цена входа нового человека в базу.
Если в данном периоде и канале не появилось ни одного нового клиента — знаменатель равен нулю, и CPA не определён: результат будет NULL в SQL или NaN в pandas. Делить на ноль нельзя, и это граничный случай, который нужно обрабатывать явно.
Особо важен grain расчёта: одна строка на пару (period, channel). Смешивать каналы или месяцы в одной дроби нельзя — числитель и знаменатель должны описывать одну и ту же аудиторию за одно и то же время.
Как правильно найти первый заказ клиента
Знаменатель CPA требует, чтобы каждый клиент учитывался новым ровно один раз — в месяце и канале своего самого первого paid заказа за всю историю. Здесь скрыта тонкость: у ряда клиентов в реальных данных бывает два заказа с одинаковой датой, причём в разных каналах. Если выбирать строку через MIN(order_date), оба заказа проходят фильтр, и клиент попадёт в знаменатель сразу двух каналов — это неверно.
Правильное решение — выбрать единственную строку с первым заказом по детерминированному критерию: сначала по order_date, при совпадении — по order_id. Это гарантирует, что каждый клиент получает ровно одну «первую» строку вне зависимости от дубликатов дат.
В SQL (SQLite 3.49+) это делается через оконную функцию:
WITH first_orders AS (
SELECT
customer_id,
order_id,
order_date,
channel,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY order_date, order_id
) AS rn
FROM orders
WHERE status = 'paid'
)
SELECT
strftime('%Y-%m', order_date) AS period,
channel,
COUNT(DISTINCT customer_id) AS new_customers
FROM first_orders
WHERE rn = 1
GROUP BY period, channel;
Что здесь происходит:
- CTE
first_ordersнумерует всеpaidзаказы каждого клиента, начиная с самого раннего. При одинаковой дате порядок определяетсяorder_id— меньший идёт первым. - Фильтр
WHERE rn = 1оставляет ровно одну строку на клиента — его единственный «первый заказ». - Затем агрегация считает количество таких клиентов по
(period, channel).
Этот подход даёт корректные результаты даже когда у клиента в первый день оказываются заказы в двух разных каналах — например, в social и search с одной датой. В таком случае ровно один из заказов получит rn = 1 по меньшему order_id, и клиент будет отнесён только к этому каналу. Второй заказ того же дня в другом канале останется с rn = 2 и в подсчёт новых клиентов не войдёт.
Новый клиент vs. новый заказ: почему знаменатель CPA считается по клиентам
Ключевое ограничение CPA — знаменатель считает людей, а не заказы. Это не вопрос вкуса, а следствие определения: метрика измеряет стоимость привлечения нового человека, а не стоимость транзакции.
Вот в чём разница на практике. Один клиент может сделать два заказа в январе — например, купить в первый день и вернуться через неделю. Он — один новый клиент. Его заказов — два. Число заказов всегда больше или равно числу новых клиентов:
paid_orders ≥ new_customers
Если подставить в знаменатель число заказов вместо числа новых клиентов, знаменатель увеличивается, а дробь уменьшается. CPA получается заниженным, канал выглядит эффективнее, чем есть.
Возьмём данные из агрегированной таблицы ads_monthly.csv по магазину «Маяк» за январь 2024, канал email:
| Поле | Значение |
|---|---|
| spend | 5 504 руб. |
| new_customers | 10 |
| paid_orders | 13 |
Верный CPA:
CPA = 5 504 / 10 = 550,4 руб.
Если бы знаменатель был paid_orders:
CPA_wrong = 5 504 / 13 ≈ 423,4 руб.
Разница — 127 рублей на единицу, то есть занижение примерно на 23%. Три лишних заказа (повторные покупки уже пришедших клиентов) создают иллюзию, что канал дешевле.
Как диагностировать ошибку: сравнить paid_orders и new_customers в агрегате по (period, channel). Если paid_orders > new_customers — часть заказов повторные, и подмена знаменателя занизит CPA. Если они равны — каждый новый клиент сделал ровно один заказ в периоде, и обе дроби совпадут.
В данных магазина «Маяк» paid_orders > new_customers — обычная ситуация практически в каждом (period, channel). Например, в январе 2024 по каналу search: 32 заказа против 17 новых клиентов. Подмена знаменателя там дала бы CPA ≈ 746 руб. вместо верных 1 405 руб. — почти вдвое меньше.
Исправление простое: знаменатель — COUNT(DISTINCT customer_id) по строкам, где дата первого paid заказа клиента попадает в данный период и канал этого первого заказа.
Формула ROAS: net_revenue / spend и интерпретация безразмерного коэффициента
ROAS (Return on Ad Spend) — отношение зарегистрированной net-выручки к наблюдаемым рекламным расходам. Формула:
ROAS = net_revenue / spend
Числитель — net_revenue: чистая выручка по paid заказам за выбранный период и канал после вычета возвратов, известных на дату среза. Это не gross_revenue — сумма до возвратов, а уже скорректированное число.
Знаменатель — тот же spend из ad_spend, что и в CPA, привязанный к тому же (period, channel).
ROAS безразмерен: рубли делятся на рубли, единицы сокращаются. ROAS = 3,0 означает: на рубль наблюдаемых рекламных расходов в данном периоде и канале приходится три рубля зарегистрированной net-выручки. ROAS = 1,0 — зарегистрированная выручка равна расходам. ROAS < 1,0 — зарегистрированная выручка меньше, чем потрачено на рекламу.
Важно понимать, что ROAS — это наблюдаемое соотношение двух агрегатов. Он не показывает, сколько выручки создала реклама в причинном смысле: часть клиентов могла бы купить и без рекламного контакта. Также это соотношение не позволяет предсказать, что произойдёт с выручкой при изменении бюджета.
Посмотрим на числа по каналу email в январе 2024:
net_revenue = 109 775 руб.
spend = 5 504 руб.
ROAS = 109 775 / 5 504 ≈ 19,94
Это означает: на рубль рекламных расходов в email-канале в январе пришлось почти 20 рублей зарегистрированной net-выручки. Для сравнения, канал social в том же месяце показал ROAS ≈ 7,03 при расходах 30 121 руб. и выручке 211 725 руб. Разрыв между каналами заметен, но одного лишь ROAS недостаточно, чтобы утверждать, что email «эффективнее» — это соотношение не учитывает ни себестоимость, ни причинный вклад рекламы.
Граничный случай: если spend = 0 или запись в ad_spend отсутствует — знаменатель равен нулю или не определён, ROAS принимает значение NULL/NaN. В данных «Маяка» канал organic стабильно имеет spend = 0, поэтому ROAS для него не вычисляется — это корректное поведение, а не ошибка в данных.
ROAS не учитывает себестоимость товаров и операционные расходы. Высокий ROAS не гарантирует прибыльность канала — это станет темой следующих уроков, когда к расчёту добавится маржа.
Паспорт метрик и верификация на данных магазина «Маяк»
Оба показателя удобно держать в единой структуре — паспорте метрики: числитель, знаменатель, grain, единица и граничные случаи.
CPA
| Элемент | Значение |
|---|---|
| Числитель | spend (рекламные расходы за period + channel) |
| Знаменатель | Число уникальных customer_id с первым paid заказом в данном period + channel |
| Grain | Одна строка на (period, channel) |
| Единица | руб. / новый клиент |
| Граничный случай | Нет новых клиентов → NULL/NaN |
ROAS
| Элемент | Значение |
|---|---|
| Числитель | net_revenue (gross_revenue минус возвраты на дату среза) за period + channel |
| Знаменатель | spend за тот же period + channel |
| Grain | Одна строка на (period, channel) |
| Единица | безразмерная (руб. / руб.) |
| Граничный случай | spend = 0 или отсутствует → NULL/NaN |
Паспорт описывает два разных граничных случая, которые важно уметь различать на практике.
Случай 1: spend = 0 (наблюдаемое нулевое значение). Канал organic, январь 2024:
spend = 0 руб.
new_customers = 10
net_revenue = 192 712 руб.
CPA = 0 / 10 = 0 руб. — расходов нет, знаменатель ненулевой
ROAS = 192 712 / 0 → NULL — деление на ноль, результат не определён
CPA = 0 здесь технически корректен: формула отработала, знаменатель положителен. Но экономически этот ноль означает отсутствие платной рекламы, а не «бесплатное привлечение». Сравнивать его с CPA платных каналов бессмысленно: у organic другая природа трафика. ROAS не вычисляется — это ожидаемое поведение, зафиксированное в паспорте как граничный случай.
Случай 2: spend отсутствует (строки в ad_spend нет совсем). Если запись о расходах по каналу за период не попала в таблицу — это не то же самое, что spend = 0. При LEFT JOIN между orders и ad_spend такая строка даст spend = NULL, и оба показателя будут NULL: ни CPA, ни ROAS вычислить нельзя. Это сигнал пропуска данных, а не нулевых расходов.
Различие важно для диагностики: если в сводной таблице CPA и ROAS для канала за период оба равны NULL, нужно проверить, есть ли строка в ad_spend — возможно, данные по расходам просто не загружены.
Оба показателя опираются на одни и те же поля spend из ad_spend и агрегированные данные из orders с refunds, сведённые по (period, channel). Как именно собрать этот JOIN — тема следующего урока.

Январь 2024, синтетические данные «Маяка». CPA и ROAS используют разные знаменатели и единицы. У organic наблюдаемый spend=0 при 10 новых клиентах: CPA=0, ROAS не определён (тире). Этот ноль не означает бесплатное привлечение; отношения не доказывают причинный эффект рекламы или результат изменения бюджета.
Задача 1
Отдельный условный канал:spend36000руб.,24новых клиента,40paid-заказов. Менеджер разделил расходы на число заказов и сообщил900руб. Как исправить отчёт оCPA?
Задача 2
Отдельный условный канал:spend48000руб.;16новых клиентов;23paid-заказа;net_revenue134400руб. ВычислитеROAS. Введите безразмерное число, точность один знак после десятичного разделителя (точка или запятая).
Задача 3
Используйте этот отдельный условный срез:spend48000руб.;16уникальных клиентов с первымpaid-заказом в периоде иchannel этого заказа;23paid-заказа. НайдитеCPA, целое число рублей за нового клиента.
Задача 4
Сопоставьте элементы формул с источниками; одну метку можно использовать несколько раз.
(1)ЧислительCPA
(2)ЗнаменательCPA
(3)ЧислительROAS
(4)ЗнаменательROAS
A=spend изad_spend за тот жеperiod+channel; B=net_revenue поpaid-заказам выбранногоperiod+channel с возвратами, известными на дату среза; C=COUNT DISTINCTcustomer_id с первымpaid-заказом в выбранномperiod иchannel этого первого заказа; D=число всехpaid-заказов в срезе.
Введите1<метка>;2<метка>;3<метка>;4<метка>. Повторение меток разрешено. Допустимы запятые и один необязательный пробел после каждого разделителя.
Задача 5
Отдельные условные срезы трёх каналов за один полный месяц. new_customers — уникальные клиенты с первым paid-заказом по всей истории; net_revenue относится к paid-заказам того же месяца и канала, с возвратами на общий cutoff. Все приведённые суммы выручки и числа клиентов известны.
A: spend = 0 подтверждён полным учётом расходов; new_customers = 4; net_revenue = 10 000 руб.
B: строка расходов отсутствует, сумма spend неизвестна; new_customers = 4; net_revenue = 10 000 руб.
C: spend = 12 000 руб., расходы известны; new_customers = 0; net_revenue = 0 руб.
Сначала самостоятельно запишите таблицу CPA и ROAS для всех трёх случаев и письменно объясните: где известен нулевой результат, где неизвестен вход и где деление невозможно из-за нулевого знаменателя. Укажите единицы известных результатов; не заменяйте неизвестное значение нулём и не приписывайте этим данным причинный эффект рекламы.
Затем выберите согласованную таблицу. «Не определён» означает невозможность деления на ноль, «неизвестен» — отсутствие нужного входного значения. Платформа проверяет выбранный вариант; собственную таблицу и объяснения сверяете самостоятельно.
Задача 6
Заполните семь пропусков, метки можно повторять.
CPA=(1)/(2). Единица — рублей на одного___(3). ROAS=(4)/(5). Результат показывает рублей(6)___ на рубль___(7)___.
A=net_revenue;B=расходов на рекламу;C=расходы на рекламу(spend);D=нового клиента;E=выручки;F=число новых клиентов;G=числоpaid-заказов. Введите семь меток через ; или запятую. После каждого разделителя допустим один пробел.
Продолжить с сохранением прогресса
Перейдите к уроку в учебном кабинете.
