Сверка отчётов в pandas: какие строки изменились после новой выгрузки
Вчерашний отчёт показывал 600 тысяч выручки, новая выгрузка — 650. Сравнение общих сумм подтверждает разницу, но не объясняет её. Нужно понять, какие строки добавились, какие исчезли и какие были исправлены. Соберём такую сверку в pandas с устойчивым ключом и мостом изменения итоговой суммы.
Сначала определяем ключ и сравнимые поля
Одна строка примера соответствует заказу, ключом служит order_id. В обоих файлах он уникален и заполнен. Если выгрузка товарная, нужен ключ строки заказа; использование только order_id размножит совпадения. Перед merge полезно отдельно проверить уникальность ключей и остановиться при нарушении, чтобы отчёт расхождений не стал результатом неправильного объединения.
Сравниваем сумму и статус. Другие поля могут быть техническими: дата формирования файла закономерно меняется при каждом экспорте и не должна делать все заказы изменёнными. Список значимых полей фиксируют заранее. Для текста согласуют пробелы и регистр, для денег — единицы и точность. При этом нормализация не должна скрывать реальное изменение, например разные артикулы, отличающиеся значимым нулём.
Пропуски требуют отдельного правила. Два отсутствующих значения одного поля считаем одинаковыми, а замену пропуска на заполненное значение — изменением. Обычное сравнение NaN с NaN не выражает это правило, поэтому явно добавляем условие одновременных пропусков. В примере есть заказ с пустым статусом в обеих версиях, чтобы проверить эту границу.
Сохраняем все типы различий
Outer merge с indicator=True делит ключи на left_only, right_only и both. Это удалённые, добавленные и присутствующие в обеих версиях заказы. Для both проверяем значимые поля. validate="one_to_one" защищает сверку от неожиданного размножения строк.
Чтобы объяснить изменение суммы, добавленным строкам приписываем новую сумму, удалённым — минус старую, изменённым — новую минус старую. Для неизменённых вклад равен нулю. Пропуск суммы в существующей записи нельзя автоматически считать нулём: сначала решите, допустим ли он. В нашем примере все денежные значения заполнены, поэтому fillna(0) после outer join означает именно отсутствие заказа в соответствующей версии.
Такой мост помогает сверить не только перечень расхождений, но и общую арифметику. Сумма вкладов должна совпасть с разностью итогов двух исходных выгрузок. Если нет, потерялся тип изменения или часть строк не вошла в расчёт.
import pandas as pd
old = pd.DataFrame({"order_id":[1,2,3,5],"amount":[100,200,300,50],"status":["paid","paid","paid",None]})
new = pd.DataFrame({"order_id":[1,2,4,5],"amount":[100,250,400,50],"status":["paid","paid","paid",None]})
assert old.order_id.notna().all() and new.order_id.notna().all()
d = old.merge(new,on="order_id",how="outer",suffixes=("_old","_new"),indicator=True,validate="one_to_one")
same_amount = d.amount_old.eq(d.amount_new) | (d.amount_old.isna() & d.amount_new.isna())
same_status = d.status_old.eq(d.status_new) | (d.status_old.isna() & d.status_new.isna())
d["kind"] = "unchanged"
d.loc[d._merge.eq("left_only"),"kind"] = "removed"
d.loc[d._merge.eq("right_only"),"kind"] = "added"
d.loc[d._merge.eq("both") & ~(same_amount & same_status),"kind"] = "changed"
d["delta"] = d.amount_new.fillna(0)-d.amount_old.fillna(0)
print(d[["order_id","kind","amount_old","amount_new","delta"]].to_string(index=False))
assert d.delta.sum() == new.amount.sum()-old.amount.sum() == 150
assert d.set_index("order_id").loc[5,"kind"] == "unchanged"
assert set(d.kind) == {"unchanged","removed","added","changed"}Разбираем изменение итога
Заказ 2 вырос на 50, заказ 3 исчез и забрал 300, заказ 4 добавился и дал 400. Общий вклад равен 150 — столько же, сколько разница итогов 800 и 650. Заказы 1 и 5 неизменны, причём пропуск статуса у 5 не создаёт ложного изменения. Числа примера специально невелики: каждую строку можно восстановить вручную.
Для рабочего отчёта сохраните оба значения поля и тип различия. Тогда владелец источника увидит, что именно изменилось. Сводка по kind удобна для оценки масштаба, но сама по себе не позволяет проверить отдельный заказ.
Когда изменение правомерно
Поздняя оплата, отмена или исправление цены могут законно менять прошлый период. Поэтому сверка не объявляет новую выгрузку ошибочной, а показывает объект для объяснения. Сопоставьте время изменения заказа и правила закрытия периода. Возможно, оперативный отчёт должен обновляться, а закрытая финансовая версия — храниться отдельно.
Сохраняйте исходные файлы, дату получения и правило фильтрации. Если новая выгрузка исключила архивные заказы, массовые removed будут следствием изменившегося экспорта. После подтверждения причин можно принять новую версию и записать краткое объяснение разницы для пользователей дашборда. Это избавляет команду от ручного поиска строк при каждом изменении итогов.
Если прошлый день изменяется после новых поступлений данных, полезно разобрать правила времени учёта: Поздние события в SQL: почему вчерашний отчёт меняется сегодня.
Порядок строк не должен создавать изменения
Экспорт часто меняет сортировку, поэтому сравнение строк по номеру ненадёжно. В нашем расчёте связь определяется order_id, и перестановка строк не влияет на итог. При этом тип ключа должен совпадать: строковый код с ведущими нулями нельзя бездумно приводить к числу. Нормализуйте ключ по согласованному правилу и проверьте, что после этого он остаётся уникальным.
Для большого файла сначала можно сохранить сводку типов изменений и несколько примеров, а подробный результат — отдельной таблицей. Ограничение вывода не должно сокращать сам расчёт: мост суммы строится по всем строкам. Когда причины подтверждены, оставьте краткий журнал принятых изменений. Он объяснит пользователям, почему обновился уже просмотренный показатель, и поможет повторить сверку при следующем экспорте.
Продолжить практику
В «Практической аналитике данных» вы проверяете таблицы и последствия объединений, считаете бизнес-метрики и объясняете результат. Курс предлагает маршруты pandas и SQL на учебном магазине. В финале вы повторяете расчёты на данных нового месяца, обновляете дашборд DataLens и готовите записку с рекомендацией. Откройте программу курса.
Продолжить на практике
- Практическая аналитика данных ↗
Превратите знания SQL или Python в аналитическую работу: проверяйте данные, считайте бизнес-метрики, стройте дашборды и объясняйте выводы на трёх прикладных кейсах.
