pivot не строится: когда повтор строки означает ошибку данных
Нужно вывести магазины по строкам, даты по столбцам и остаток в ячейках. pivot сообщает о повторных индексах. Замена на pivot_table с усреднением убирает ошибку, но может создать остаток, которого в магазине никогда не было.
Повторный ключ требует предметного решения
pivot ожидает одно значение на пересечение ключей. Остаток 10 утром и 2 вечером нельзя сложить до 12, если нужен запас на конец дня. Простое среднее снимков тоже зависит от частоты измерений. Сначала выведите все конфликтные пары и определите, что означает каждая строка.
Возможны разные действия: удалить повтор доставки одной записи, добавить момент в ключ, суммировать отдельные операции или выбрать последний снимок. Само сообщение pandas не позволяет выбрать между ними. Для нашего отчёта нужен последний известный остаток каждого магазина за день.
Выбрать состояние до разворота
Время taken_at задаёт порядок снимков. Проверяем уникальность магазина и времени, выбираем последнюю запись дня, затем выполняем pivot. Если два разных состояния имеют одинаковое время, понадобится номер версии либо очередь конфликтов. Порядок строк в файле не должен решать, какой остаток считать верным.
import pandas as pd
x = pd.DataFrame({"store": ["A", "A", "B"],
"taken_at": ["2026-10-01 09:00", "2026-10-01 18:00", "2026-10-01 12:00"],
"stock": [10, 2, 7]})
x["taken_at"] = pd.to_datetime(x["taken_at"], format="%Y-%m-%d %H:%M")
assert not x.duplicated(["store", "taken_at"]).any()
x["day"] = x["taken_at"].dt.normalize()
conflicts = x[x.duplicated(["store", "day"], keep=False)]
last = x.sort_values("taken_at").drop_duplicates(["store", "day"], keep="last")
report = last.pivot(index="store", columns="day", values="stock")
print(report)
assert len(conflicts) == 2
assert report.loc["A"].iloc[0] == 2 and report.loc["B"].iloc[0] == 7У A осталось 2, у B — 7. Исходные снимки сохраняются, поэтому спорную ячейку можно объяснить. Отчёт описывает последнее измерение дня, а не средний запас. Если магазин закрылся позже последнего замера, это именно последнее известное состояние.
Когда агрегирование уместно
Если строки описывают отдельные чеки, несколько чеков магазина за день законно суммируются. Тогда pivot_table с явно заданным aggfunc="sum" соответствует задаче. Для температуры может требоваться среднее равномерных измерений, для номера документа — отсутствие повторов. Функцию выбирают после определения показателя.
Удобно сначала получить длинную агрегированную таблицу и проверить её ключ. Широкая форма служит просмотру или экспорту. Когда расчёт и разворот разделены, контрольные суммы относятся к расчёту, а проверка формы — к представлению.
Не заполняйте отсутствующие ячейки нулями без правила. Нулевой остаток означает известное отсутствие товара; пустая ячейка может означать непришедший снимок. Ложные нули создадут ложные сигналы о дефиците.
Испытайте единственную запись, два последовательных снимка и повтор доставки. Эти случаи должны приводить к разным действиям до pivot. Тогда ошибка разворота помогает обнаружить несогласованную единицу строки, вместо того чтобы маскировать её произвольным средним.
Закрепить на практике
В бесплатном уроке преобразования таблиц вы сравните melt и pivot_table. Возьмите правило из статьи и объясните смысл выбранного агрегата до запуска разворота. Откройте бесплатный урок и задачи. Полная последовательность тем — в бесплатном курсе.
Продолжить на практике
- NumPy и pandas: практический тренажёр ↗
Бесплатный тренажёр NumPy и pandas для тех, кто знает основы Python. Научитесь очищать таблицы, объединять данные и собирать отчёты: 31 урок и 124 задачи с кодом и автоматической проверкой.
