Цена или объём: разложение изменения продаж в pandas

Выручка выросла, но закупщик утверждает, что товаров продали меньше. Оба могут быть правы: повышение цены перекрыло падение количества. Соберём в pandas товарный мост выручки, где изменение точно складывается из ценового и количественного вкладов, а итог можно проверить вручную.

Выбираем правило разложения до расчёта

Для одного товара выручка равна цене p, умноженной на количество q. Между двумя периодами меняются оба значения. Используем последовательный мост: сначала меняем цену при старом количестве, затем количество при новой цене. Вклад цены равен (p1−p0)×q0, вклад количества — p1×(q1−q0). Их сумма в точности равна p1q1−p0q0.

При обратном порядке формулы будут другими: количество изменится при старой цене, а цена — при новом количестве. Итоговое изменение выручки сохранится, но взаимодействие цены и количества окажется в другой части. Поэтому правило нужно назвать в отчёте, особенно если вклады используются для оценки коммерческой команды. Нельзя выбирать порядок после расчёта в зависимости от того, какой результат удобнее показать.

Под ценой здесь понимаем фактическую среднюю цену проданной единицы после скидок. Если прайс равен 100, но половина продаж прошла по 80, использовать прайс вместо фактической выручки на единицу неверно. Сначала агрегируют выручку и количество товара за период, затем вычисляют среднюю цену. Все суммы должны относиться к одной валюте и одной базе учёта налогов.

Сопоставляем одинаковые товары

В учебном примере ассортимент одинаков: A и B продаются в обоих периодах. Перед объединением получаем одну строку на артикул и период. validate="one_to_one" останавливает merge, если агрегат случайно содержит несколько строк одного товара. Это защищает мост от размножения выручки.

Для нового товара нет наблюдавшейся старой цены, для исчезнувшего — новой. Лучше выделить их отдельным вкладом изменения ассортимента, а формулы применять к сопоставимым товарам. Подстановка нуля вместо отсутствующей цены математически даст число, но оно будет описывать выбранную условность, а не реальное повышение цены. В управленческом отчёте новые и выбывшие позиции заслуживают отдельного объяснения.

Если внутри артикула меняется состав упаковок, проблема возникает ещё раньше: количество может быть несопоставимым. Продажа десяти коробок по шесть единиц и десяти коробок по двенадцать — одинаковое число коробок, но разный объём. Приведите количество к общей базовой единице и сохраните коэффициент пересчёта в справочнике.

import pandas as pd
old = pd.DataFrame({"sku":["A","B"],"p0":[100,200],"q0":[10,5]})
new = pd.DataFrame({"sku":["A","B"],"p1":[120,220],"q1":[8,6]})
d = old.merge(new,on="sku",validate="one_to_one")
d["revenue0"] = d.p0*d.q0
d["revenue1"] = d.p1*d.q1
d["price_effect"] = (d.p1-d.p0)*d.q0
d["volume_effect"] = d.p1*(d.q1-d.q0)
d["change"] = d.revenue1-d.revenue0
assert (d.price_effect+d.volume_effect).equals(d.change)
print(d[["sku","change","price_effect","volume_effect"]].to_string(index=False))
totals = d[["change","price_effect","volume_effect"]].sum()
print(totals.to_dict())
assert totals.to_dict() == {"change":280,"price_effect":300,"volume_effect":-20}

A потерял 40 выручки: рост цены дал 200, уменьшение количества забрало 240. B прибавил 320: 100 от цены и 220 от объёма. Общий рост 280 раскладывается на 300 ценового эффекта и −20 количественного. Закупщик видит отрицательный совокупный количественный вклад, коммерческий директор — положительный итог выручки. Отчёт объясняет, как эти наблюдения сочетаются.

Следующее действие — посмотреть, что произошло с маржой. Цена продажи могла увеличиться вслед за закупочной, а объём мог снизиться из-за отсутствия товара. Добавьте себестоимость и доступность по тем артикулам, которые дали основной вклад. Так разложение помогает выбрать несколько содержательных проверок вместо просмотра всего ассортимента подряд.

Мост описывает арифметику изменения, но не оценивает эластичность спроса. Чтобы сказать, что количество упало именно из-за цены, нужно учитывать акции, сезонность, наличие и состав покупателей. Для планирования цены потребуется более сильный дизайн сравнения. Первым результатом остаётся проверяемая сверка: сумма всех вкладов должна совпасть с фактической разницей выручки между двумя закрытыми периодами.

На уровне заказов похожую задачу решает декомпозиция по числу покупок и чеку: Выручка выросла на 2%: как разделить вклад заказов и среднего чека.

Проверяем масштаб и знак вклада

Старая выручка A равна 1000, новая — 960; старая выручка B — 1000, новая — 1320. Значит, весь отчёт вырос с 2000 до 2280. Эти четыре исходных числа полезно оставить рядом с мостом: пользователь сможет проверить результат без чтения формул.

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

Следующая рабочая задача

В «Практической аналитике данных» вы анализируете продажи, сравниваете периоды и объясняете изменение показателей на данных интернет-магазина. Курс включает маршрут pandas или SQL, графики в DataLens и аналитическую записку. В финале вы повторяете анализ нового месяца и формулируете рекомендацию. Откройте программу курса «Практическая аналитика данных».

Продолжить на практике

  • Практическая аналитика данных ↗

    Превратите знания SQL или Python в аналитическую работу: проверяйте данные, считайте бизнес-метрики, стройте дашборды и объясняйте выводы на трёх прикладных кейсах.