Урок курса

Метрики рекламы: 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;

Что здесь происходит:

  1. CTE first_orders нумерует все paid заказы каждого клиента, начиная с самого раннего. При одинаковой дате порядок определяется order_id — меньший идёт первым.
  2. Фильтр WHERE rn = 1 оставляет ровно одну строку на клиента — его единственный «первый заказ».
  3. Затем агрегация считает количество таких клиентов по (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 не определён (тире). Этот ноль не означает бесплатное привлечение; отношения не доказывают причинный эффект рекламы или результат изменения бюджета.

Январь 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-заказов. Введите семь меток через ; или запятую. После каждого разделителя допустим один пробел.

Без регистрации · результат не сохраняется

Продолжить с сохранением прогресса

Перейдите к уроку в учебном кабинете.

Перейти к интерактивному уроку