Продажи упали из-за отсутствия товара? Проверяем ассортимент в pandas
У двух магазинов одинаковые продажи, но один торговал весь месяц, а второй половину времени стоял без товара. Заказ на пополнение им нужен разный. Для проверки спроса объединим ежедневные продажи с журналом наличия и сравним количество проданных единиц на день, когда товар был доступен.
Что означает день без продаж
Отсутствие чека само по себе не говорит, что покупатели отказались от товара. Нужна независимая таблица наличия: магазин, артикул, день и признак доступности. Нулевые продажи при положительном остатке и нулевые продажи при пустой полке требуют разных решений.
В примере наличие измеряется на начало дня. Такая модель полезна как первый контроль, но пропускает внутридневные stockout. Если товар закончился к обеду, целый день нельзя считать полностью доступным: для точного планирования понадобятся часы доступности.
Строим сопоставимую базу
Сначала берём календарь из журнала наличия, затем добавляем продажи. Благодаря этому в расчёт попадают дни без чеков. Пропущенное количество можно заполнить нулём только после проверки, что выгрузка продаж за этот день действительно поступила. Пропуск самого признака наличия нулём не заменяем: это неизвестное состояние.
import pandas as pd
stock = pd.DataFrame({"shop":["A"]*4+["B"]*4, "day":[1,2,3,4]*2, "available":[1,1,1,1,1,0,0,1]})
sales = pd.DataFrame({"shop":["A","A","A","A","B","B"], "day":[1,2,3,4,1,4], "qty":[5,5,5,5,10,10]})
d = stock.merge(sales, on=["shop","day"], how="left", validate="one_to_one")
d["qty"] = d.qty.fillna(0)
assert not ((d.available == 0) & (d.qty > 0)).any()
r = d.groupby("shop").agg(units=("qty","sum"), available_days=("available","sum"))
r["units_per_available_day"] = r.units / r.available_days
print(r.to_string())
assert r.loc["A","units_per_available_day"] == 5
assert r.loc["B","units_per_available_day"] == 10
Оба магазина продали по 20 единиц. На доступный день у A приходится 5, у B — 10. Разница меняет разговор о пополнении: B заслуживает проверки доступности и повышенного запаса, хотя обычный рейтинг продаж поставил бы магазины рядом.
Умножить 10 на два потерянных дня и объявить 20 упущенных продаж было бы следующим, отдельным допущением. Спрос в эти дни мог отличаться из-за выходных, погоды или акции. Для оценки потерь сначала сопоставьте дни недели и магазины с похожим трафиком. Этот отчёт надёжно показывает ограничение доступностью, а оценка недополученной выручки потребует модели спроса.
Что проверить перед заказом на пополнение
Результат помогает выбрать объект расследования, но не задаёт автоматически одинаковый запас всем магазинам. У B может быть вдвое больше посетителей, другая выкладка или соседство с популярным товаром. Чтобы сравнить эффективность полки, дополните отчёт трафиком и числом торговых часов. Чтобы выбрать запас, важнее ожидаемые продажи за срок поставки и частота пополнения. Один показатель обслуживает только часть этого решения.
Учитывайте правило снимка остатков. Остаток на начало дня может быть нулевым, а поставка приехала утром и товар продавался до вечера. Тогда проверка на продажи при available=0 обнаружит противоречие, которое нужно объяснить временем снимка. Если журнал хранит движения, можно восстановить интервалы наличия и считать продажи на час доступности. Если такой детализации нет, в отчёте лучше прямо назвать показатель «продажи на день с положительным утренним остатком».
Проверка нулевых продаж должна опираться на подтверждённую поставку данных. Например, магазин B мог не отправить кассовый файл за второй день. В этом случае fillna(0) создал бы ложный день без спроса. Удобно иметь отдельный реестр закрытия кассовых дней и допускать к анализу только закрытые дни. Таблица наличия тоже проходит проверку полноты: один магазин и артикул должны иметь ровно один снимок на каждую анализируемую дату.
Для акции используйте дополнительный разрез. День со скидкой и обычный день могут различаться сильнее, чем магазины между собой. Если B торговал только в выходные со скидкой, скорость 10 единиц в день нельзя напрямую переносить на будни. Следующий вариант отчёта группирует доступные дни по дню недели и наличию промо. Внутри сопоставимых групп уже можно понять, какой запас поддерживал бы обычный спрос.
Полезный итог для закупщика — список магазин–артикул с числом дней отсутствия, продажами на доступный день и датой следующей поставки. По нему сначала восстанавливают наличие в точках с высоким темпом продаж. Затем повторяют расчёт за следующий период и проверяют, выросли ли продажи после устранения ограничений. Такой цикл связывает таблицу с действием, а заодно показывает, где первоначальная оценка спроса была завышена.
Для проверки доступности полезно дополнить анализ расчётом резерва и свободного остатка: Остаток есть, продавать нельзя: как рассчитать доступный товар в SQL.
Как проверить гипотезу о пустой полке
Возьмите несколько дней отсутствия B и сопоставьте их с накладными поступления и журналом выкладки. Если товар физически был в магазине, проблема может быть в доступности на полке или в учёте остатков. После устранения причины сравните новые доступные дни с похожими днями прошлого периода. Это даст более полезный ответ, чем немедленное увеличение закупки всей сети.
Следующая рабочая задача
На курсе «Практическая аналитика данных» вы переходите от такого наблюдения к проверяемому бизнес-вопросу: анализируете продажи, сравниваете периоды и объясняете рекомендацию в записке. Учебные данные и контрольные результаты позволяют повторить анализ новым месяцем. Откройте программу курса «Практическая аналитика данных».
Продолжить на практике
- Практическая аналитика данных ↗
Превратите знания SQL или Python в аналитическую работу: проверяйте данные, считайте бизнес-метрики, стройте дашборды и объясняйте выводы на трёх прикладных кейсах.
