groupby потерял часть суммы: учитываем пустые ключи

Общая выручка выгрузки — 600, а в отчёте по каналам осталось 400. Возвратов нет, фильтр периода верный. Две сотни исчезли при группировке: у одного заказа не указан канал. По умолчанию groupby не создаёт группу для пропущенного ключа.

Пропущенный ключ не делает заказ ненастоящим

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

Для первого решения нужен бизнес-смысл. Отменённый заказ иногда действительно исключают. Заказ без атрибуции обычно оставляют. Для второго решения pandas позволяет сохранить NA как отдельный ключ через dropna=False. Не нужно заранее подменять пропуск строкой «прочее»: она может совпасть с настоящей категорией справочника.

Сначала сохранить неизвестную группу, потом дать ей подпись

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

У неизвестного канала остаётся отдельная строка с 200, а у web — 400. По этим суммам легко сверить, какие заказы вошли в каждую группу.

import pandas as pd
orders = pd.DataFrame({"channel": ["web", None, "web"], "amount": [100, 200, 300]})
report = orders.groupby("channel", dropna=False).agg(
    revenue=("amount", "sum"), orders=("amount", "size"))
print(report)
assert report["revenue"].sum() == orders["amount"].sum() == 600
unknown = report.loc[report.index.isna()]
assert unknown["revenue"].iloc[0] == 200
assert report["orders"].sum() == len(orders)

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

Если группировка составная

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

Подпись «Канал не определён» лучше добавлять в представлении отчёта, сохраняя исходный признак пропуска в расчётной таблице. Тогда можно отдельно измерять долю неатрибутированной выручки и отличать её от существующего канала с похожим названием. Если справочник поменяется, не придётся угадывать, какая строка возникла из NA.

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

Когда неизвестная группа растёт, передайте владельцу данных конкретные идентификаторы заказов, сумму и долю потерь атрибуции. Заполнение самым популярным каналом улучшит внешний вид таблицы, но изменит вывод о том, откуда приходят покупатели. Сохраняемый NA позволяет продолжить расчёт общего бизнеса и отдельно исправлять качество детализации.

Закрепить на практике

В бесплатном уроке groupby и agg вы закрепите группировку и именованные агрегаты. После основных задач добавьте в свой контрольный вход пропущенный ключ и сравните число строк и сумму до группировки с итогом. Откройте бесплатный урок и задачи.

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

  • NumPy и pandas: практический тренажёр ↗

    Бесплатный тренажёр NumPy и pandas для тех, кто знает основы Python. Научитесь очищать таблицы, объединять данные и собирать отчёты: 31 урок и 124 задачи с кодом и автоматической проверкой.