Очистка данных в аналитике: рабочие приёмы без потери смысла
Пробелы, дубли и неверные даты портят отчёты не меньше, чем ошибки в формулах. Разбираем рабочий порядок очистки данных в pandas: что нормализовать, что считать пропуском и как не удалить из набора важные наблюдения.

Очистка начинается не с удаления строк
В аналитическом отчёте одна и та же дата может оказаться текстом, числом и пропуском, а две строки с одинаковым идентификатором — как дублем, так и двумя разными событиями. Если сразу вызвать удаление пустых значений и дублей, таблица станет аккуратнее, но метрика может измениться незаметно. Ниже — практический порядок для pandas 2.2.3: нормализовать поля, зафиксировать ошибки преобразования, а затем решать, какие записи исключать.
Примеры рассчитаны на таблицу событий с колонками event_id, email, date, amount и updated_at. Исходный файл лучше хранить отдельно: очищайте копию и записывайте число строк и пропусков до и после каждого этапа.
1. Приведите значения к одному виду
Начните с полей, где разные записи должны означать одно и то же: пробелы в адресе, регистр, пустая строка вместо пропуска, числовая сумма, записанная текстом. Здесь важно отделить изменение формата от исправления смысла. Пробел по краям можно убрать автоматически; нельзя без проверки решить, что «1,200» означает 1200, а не 1,2.
В примере ожидается, что даты в источнике записаны строго как ГГГГ-ММ-ДД, а запятые в суммах не используются как десятичный разделитель.
import pandas as pd
# Пустые текстовые маркеры приводим к пропускам, затем нормализуем email.
df["email"] = (
df["email"]
.astype("string")
.str.strip()
.str.lower()
.replace({"": pd.NA, "—": pd.NA})
)
# Ожидаемый формат даты задан явно: неожиданные значения станут пропусками.
df["date"] = pd.to_datetime(
df["date"],
format="%Y-%m-%d",
errors="coerce"
)
# Ошибки преобразования суммы не теряются бесследно: ниже их можно посчитать.
raw_amount = df["amount"].copy()
df["amount"] = pd.to_numeric(raw_amount, errors="coerce")
amount_parse_failed = raw_amount.notna() & df["amount"].isna()
print("Сумм не удалось разобрать:", int(amount_parse_failed.sum()))Проверьте результат по доле новых пропусков и нескольким исходным значениям. Если после преобразования даты пропусками стали тысячи строк, это не повод их удалить: вероятно, фактический формат отличается от ожидаемого. Для дат вида 31.12.2024 задайте соответствующий формат, а неоднозначные записи вроде 03/04/2024 сначала сверьте с правилами источника.
2. Не заполняйте пропуски одним правилом
Пропуск в сумме может означать сбой выгрузки, а может — что событие не предполагает оплаты. Среднее значение не восстанавливает утраченную информацию: оно лишь подставляет удобное число. Такая подстановка меняет среднее, сумму и распределение, а в отчёте может выглядеть как реальная транзакция.
Сначала зафиксируйте пропуски и выясните, какие метрики от них зависят. Если для конкретного расчёта нужна сумма, исключайте только строки без суммы из этого расчёта, а не обязательно из всей таблицы.
# Сохраняем признак пропуска до фильтрации или заполнения.
df["amount_missing"] = df["amount"].isna()
# Для метрики выручки исключаем записи без суммы, но не удаляем их из df.
revenue_rows = df.loc[df["amount"].notna()]
revenue_total = revenue_rows["amount"].sum()
print("Строк без суммы:", int(df["amount_missing"].sum()))
print("Строк в расчёте выручки:", len(revenue_rows))Заполнять пропуск допустимо, если известна бизнес-логика: например, источник определяет пустое количество как ноль. Зафиксируйте это правило и проверьте, что оно действует именно для этого поля и периода. Не заменяйте пропуски нулём только потому, что так проще выполнить агрегацию.
3. Удаляйте дубли по смысловому ключу
drop_duplicates() без аргументов удаляет только полностью совпадающие строки. Это не всегда находит повторную доставку одного события: у повторов могут различаться время загрузки или технические поля. Но и удаление по email или дате опасно — один человек может совершить несколько событий в один день.
Выберите ключ, который по контракту источника идентифицирует событие. Если источник присылает исправленные версии записи, заранее определите, какая из них считается актуальной. Например, оставлять запись с последним updated_at допустимо, только если поле надёжно отражает время изменения.
# Допущение: event_id уникален для события, updated_at уже разобран как дата.
df = (
df.sort_values("updated_at")
.drop_duplicates(subset=["event_id"], keep="last")
)
print("Осталось строк:", len(df))
print("Идентификаторы уникальны:", df["event_id"].is_unique)Сравните число удалённых строк с источником и проверьте несколько примеров, где один идентификатор встречался несколько раз. Если повторы на самом деле отражают отдельные попытки или статусы, выбранный ключ неверен — удаление исказит воронку и число событий.
Сравнение решений: что они меняют
| Ситуация | Рабочий приём | Цена и риск |
|---|---|---|
| Пробелы и разный регистр | Нормализовать строки по правилам поля | Можно склеить значения, которые различаются по смыслу, если правило выбрано неверно |
| Неразобранные даты или суммы | Преобразовать с заданным форматом и посчитать ошибки | Ошибки становятся пропусками; без счётчика их легко принять за исходные пустые значения |
| Пропуск в метрике | Исключить строку только из конкретного расчёта | Меньше данных в метрике, зато остальные поля и записи сохранены |
| Повторные события | Удалить повторы по обоснованному ключу и правилу выбора версии | Неверный ключ удалит реальные события |
Что ломается чаще всего
- Ожидание, что ошибки преобразования равны исходным пропускам. Храните отдельный счётчик для значений, которые были заполнены, но не распарсились.
- Автоматическая замена экстремумов. Большая сумма может быть ошибкой, но может быть крупной реальной покупкой. Сначала задайте допустимые границы по бизнес-правилам, затем изучите записи за пределами диапазона.
- Удаление всех строк с любым пустым полем. Фильтр по всей таблице способен убрать запись из-за необязательного комментария, хотя для выручки в ней есть сумма и дата.
- Сравнение отчётов после смены правил. Если метод очистки изменился, пересчитайте предыдущие периоды тем же способом или явно отметьте разрыв в динамике.
Чистый набор — не тот, где нет пропусков и выбросов. Это набор, где для каждого изменения можно объяснить, какую ошибку оно исправляет и какую информацию может потерять.
Когда выбирать каждый приём
Нормализуйте автоматически только значения с однозначным правилом: пробелы по краям, регистр email, документированный формат даты. Неоднозначные значения отправляйте на проверку, а не угадывайте.
Если поле обязательно для конкретной метрики, исключайте пропуски только в её расчёте. Если строка повторяет событие, удаляйте её по идентификатору события и подтверждённому правилу актуальности. Если значение выглядит необычно, но не нарушает известное ограничение, пометьте его для разбора вместо автоматического удаления.
Что делать дальше
Добавьте к отчёту небольшой контроль качества: число строк, число пропусков по ключевым колонкам, количество ошибок преобразования и число повторяющихся идентификаторов. Сохраняйте эти показатели по каждой загрузке. Если они резко изменились, сначала проверьте схему и формат источника, а не обновляйте дашборд как обычно. Правила заполнения пропусков и поиск систематических выбросов — отдельные задачи; здесь достаточно не смешивать их с механической нормализацией.
Сноска: ключ события — поле или набор полей, по которым можно однозначно отличить одно событие от другого. Например, email сам по себе обычно не подходит, если один пользователь может совершать несколько действий.
Аналитик звонков: Разбор записи звонка по этапам сделки: где сорвалось, что упущено, три исправляемые рекомендации с цитатами.
Спросить за 90 ₽Источники: pandas 2.2: Working with missing data, pandas 2.2: pandas.to_datetime
Частые вопросы
Можно ли сразу удалить все строки с пропусками?
Что делать, если после преобразования дат появилось много пропусков?
Как понять, что две строки — дубли?
Нужно ли заменять пропущенную сумму нулём?
Эти примеры работают в другой версии pandas?
Что делать, если очистка уменьшила выручку или число событий?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.