Ежемесячный отчёт в pandas: договор о данных до построения графиков
Отчёт работал три месяца, а в новой выгрузке дата стала текстом, сумма пришла со знаком минус и появились повторные номера заказов. График всё равно построился. Сделаем приёмку данных отдельным шагом: файл либо проходит понятные проверки, либо оставляет таблицу причин, с которой можно вернуться к источнику.
Проверка начинается с единицы строки
Для таблицы заказов зададим четыре правила: order_id уникален, дата читается, сумма положительна, канал заполнен. Это правила конкретной поставки. Возвраты в нашей модели приходят отдельной таблицей, поэтому отрицательный заказ здесь действительно является ошибкой. Если источник хранит операции разных типов вместе, правило суммы придётся изменить.
Уникальность проверяется до любых группировок. Иначе два одинаковых заказа могут раствориться в выручке месяца. Для даты используем errors="coerce": непонятные значения превращаются в NaT и попадают в отчёт ошибок. Они не должны незаметно исчезнуть при фильтрации периода.
Вместо одной ошибки собираем все причины
Проверки удобнее хранить как столбцы булевой таблицы. Тогда за один прогон видны повторный ключ, неверная дата и отсутствующий канал. Маска bad.any(axis=1) выделяет строки, у которых нарушено хотя бы одно правило. В регулярной задаче рядом сохраняют имя входного файла и время приёмки, чтобы исправление можно было связать с конкретной поставкой.
import pandas as pd
raw = pd.DataFrame({"order_id":[1,2,2,4], "date":["2026-10-01","bad","2026-10-02","2026-10-03"], "amount":[100,200,200,-50], "channel":["web","web","web",None]})
d = raw.copy()
d["date"] = pd.to_datetime(d.date, errors="coerce")
bad = pd.DataFrame({
"duplicate_id": d.order_id.duplicated(keep=False),
"invalid_date": d.date.isna(),
"invalid_amount": d.amount.le(0) | d.amount.isna(),
"missing_channel": d.channel.isna()
})
issues = raw.loc[bad.any(axis=1)].join(bad)
print(issues.to_string(index=False))
assert len(issues) == 3
assert int(bad.duplicate_id.sum()) == 2
В карантине окажутся обе строки с order_id=2 и заказ 4. Дублирующую пару сохраняем целиком: по одному оставшемуся экземпляру трудно понять, какой правильный. Пропуск канала в заказе 4 не исправляется названием «другое», пока не выяснено, означает ли он реальную категорию или сбой выгрузки.
Отдельно полезно проверять полный объём поставки: минимальную и максимальную дату, число заказов, сумму и число файлов. Проверки отдельных строк не заметят, что целая неделя не выгрузилась. Для рабочего запуска выбирают политику: критическое нарушение останавливает обновление, допустимые строки временно исключаются с явной суммой потерь. Решение фиксируют до очередного закрытия месяца.
Как встроить приёмку в ежемесячную работу
Удобно разделить правила на ошибки строки и ошибки всей поставки. Строка с невозможной датой может отправиться в карантин. Отсутствие обязательного столбца или целого дня означает, что обновление отчёта целиком недостоверно. В таком случае сохраняют последнюю принятую версию и отдельный отчёт о причине остановки. Это лучше, чем публиковать график, где спад выручки вызван половиной недоставленного файла.
Правило уникальности требует объяснения бизнесом. Иногда order_id повторяется законно, потому что одна строка соответствует товару заказа. Тогда ключом будет order_id вместе с line_id, а сумма заказа не должна дублироваться в каждой товарной строке. Перед написанием проверок возьмите два реальных заказа и восстановите их вручную. По этому примеру обычно видно, что именно считается строкой и какие суммы разрешено складывать.
Когда источник исправляет файл, исходную поставку стоит сохранить вместе с протоколом ошибок. Иначе через неделю будет трудно объяснить, почему опубликованный итог изменился. Для локального процесса достаточно папок по дате получения и небольшого JSON с контрольными суммами, числом строк и статусом. В системном процессе ту же функцию выполняет журнал загрузок. Важно, чтобы принятый отчёт можно было воспроизвести из конкретного входа.
Отдельный вопрос — допустимость автоматического исправления. Удалить пробелы по краям кода обычно можно, сохранив исходное значение. Превратить неизвестный канал в «органику» нельзя без подтверждения. Удалить дубликат также нельзя только потому, что совпал номер заказа: иногда ошибочно именно поле ключа, а суммы относятся к разным покупкам. Автоисправления должны быть перечислены в договоре поставки и проверяться теми же контрольными примерами.
Перед запуском на новом месяце полезно сформировать короткий протокол для владельца данных: сколько строк принято, сколько отклонено, какие правила нарушены и на какую сумму приходятся отклонения. Тогда разговор идёт о конкретных заказах, а не о том, что «pandas не читает файл». После исправления повторяют приёмку на всём файле. Следующий слой аналитики получает только принятую версию и не должен снова угадывать, что означали пропуски.
После приёмки новой поставки можно проверить совпадение результатов двух отчётов: Как сверить два SQL-отчёта: одинаковые суммы могут скрывать ошибки.
Контрольный пример для владельца источника
К протоколу удобно приложить две строки с order_id=2 и попросить подтвердить ожидаемый ключ. Одновременно заказ 4 показывает другое нарушение: отрицательную сумму и пустой канал. Разные причины не стоит объединять одной просьбой «исправить файл». По отдельным примерам владелец источника быстрее установит, какая часть экспорта требует изменения.
Следующая рабочая задача
В «Практической аналитике данных» качество таблиц связано с дальнейшими выводами: вы проверяете дубли, пропуски и последствия объединений, затем рассчитываете бизнес-метрики на учебном магазине. В финале повторяете работу на новом периоде и готовите короткую записку. Откройте программу курса «Практическая аналитика данных».
Продолжить на практике
- Практическая аналитика данных ↗
Превратите знания SQL или Python в аналитическую работу: проверяйте данные, считайте бизнес-метрики, стройте дашборды и объясняйте выводы на трёх прикладных кейсах.
