Мира · маркетолог магазина настолок «Кубик»
В ноябре заказов стало в полтора раза больше, а продажи выросли только на четверть. Финдиректор говорит, что MEGA30 раздал всё скидками. Так сколько мы потеряли на скидке?
В прошлом уроке мы разложили выручку на дерево и смотрели, какие ветки выросли, а какие просели. Мира спрашивает строже: не «что двигалось», а «сколько рублей изменения принёс каждый множитель». Это факторный разбор. Работаем с базой boardshop: MEGA30 мы уже встречали у второго босса, там он дал худшую маржу. Сегодня считаем, что он сделал с продажами.
Продажи месяца здесь это сумма qty * price по позициям заказов, кроме отменённых; возвраты оставим отдельной веткой. С октября на ноябрь 2025 продажи выросли с 1 356 166 до 1 699 001 рубля, на 342 835.
Прямоугольник
Продажи это заказы × средний чек. Нарисуй прямоугольник: ширина это заказы, высота это чек, площадь это продажи. В октябре 380 заказов по 3569 рублей, в ноябре 590 заказов по 2880.
3569 ₽ ┌────────────────────┬ ─ ─ ─ ─ ─ ┐
│ срезал чек │ угол 689 ₽
2880 ₽ ├────────────────────┼───────────┤
│ │ добавил │
│ общая часть │ объём │
│ │ │
└────────────────────┴───────────┘
380 заказов +210
Октябрь это прямоугольник 380 × 3569, ноябрь это 590 × 2880. Справа добавилась полоса 210 × 2880: это объём. Сверху срезалась полоса 380 × 689: это чек. А пунктирный угол 210 × 689 не принадлежит ни одному месяцу, но любой расчёт «по одному фактору» его задевает: объём по старому чеку считает его своим, чек по новым заказам его вычитает. Кому его отдать?
Цепные подстановки
Классический способ называется цепными подстановками: меняешь факторы по одному. Сначала заказы при старом чеке, потом чек при новых заказах. Или наоборот.
WITH monthly AS (
SELECT date_trunc('month', o.created_at)::date AS month,
count(DISTINCT o.id) AS orders,
sum(oi.qty * oi.price) AS revenue
FROM orders o
JOIN order_items oi ON oi.order_id = o.id
WHERE o.status <> 'cancelled'
AND o.created_at >= '2025-10-01' AND o.created_at < '2025-12-01'
GROUP BY month
),
pair AS (
SELECT max(orders) FILTER (WHERE month = '2025-10-01') AS n0,
max(orders) FILTER (WHERE month = '2025-11-01') AS n1,
max(revenue) FILTER (WHERE month = '2025-10-01')
/ max(orders) FILTER (WHERE month = '2025-10-01') AS a0,
max(revenue) FILTER (WHERE month = '2025-11-01')
/ max(orders) FILTER (WHERE month = '2025-11-01') AS a1
FROM monthly
)
SELECT round((n1 - n0) * a0) AS volume_first,
round(n1 * (a1 - a0)) AS check_second,
round(n0 * (a1 - a0)) AS check_first,
round((n1 - n0) * a1) AS volume_second
FROM pair;
volume_first | check_second | check_first | volume_second
--------------+--------------+-------------+---------------
749460 | -406625 | -261894 | 604729
Оба порядка сходятся с итогом: 749 460 − 406 625 и 604 729 − 261 894 дают те же 342 835. Но ответы разные. Если заказы идут первыми, объём принёс 749 тысяч, а чек отнял 407. Если первым чек, объём принёс 605 тысяч, а чек отнял 262. Разница в 145 тысяч это тот самый угол: (n1 - n0) * (a1 - a0) = 210 × (−689) ≈ −144 731. Кто идёт вторым, тот и забирает угол.
Как не выбирать порядок
Первый способ: посчитать оба порядка и взять среднюю. Для двух факторов это делит угол пополам:
volume_effect = (n1 - n0) * (a0 + a1) / 2
check_effect = (a1 - a0) * (n0 + n1) / 2
Объём даёт +677 095, чек −334 260, сумма снова 342 835. Для двух множителей это то же, что вектор Шепли: средняя по всем порядкам. Но при четырёх множителях порядков уже 24, и считать их все утомительно.
Второй способ: логарифмическое разложение. Логарифм превращает произведение в сумму: ln(R1/R0) = ln(n1/n0) + ln(a1/a0). Каждый множитель получает такую долю изменения, какую его логарифм составляет в сумме:
effect = (R1 - R0) * ln(v1 / v0) / ln(R1 / R0)
Объём даёт +669 232, чек −326 397. Почти как средняя, но этот способ работает для любого числа множителей одной формулой. В PostgreSQL это функция ln.
Оба способа отвечают на вопрос Миры одинаково: продажи выросли за счёт заказов, а чек отнял около трети этого роста. Осталось понять, почему упал чек.
урок из курса
Урок из курса «Аналитика данных с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.