Число не распарсилось: сохраняем очередь ошибок вместо тихих NaN

В количестве встречаются «12», « 7 » и «ошибка». После to_numeric с errors="coerce" последняя строка становится NaN. Расчёт работает, но теперь трудно отличить отсутствие числа от неудачного преобразования.

Сохранить сырой столбец

errors="coerce" помогает собрать диагностику. Сначала сохраните исходный текст, определите разрешённые пустые значения, затем преобразуйте подготовленную копию. Иначе причина потери исчезнет вместе с исходной записью.

Не удаляйте все нецифровые символы. Записи «1,5», «1.500» и «1 500» зависят от формата поставки, а минус может означать возврат. Агрессивная очистка способна превратить ошибку в правдоподобное число. Поддерживайте согласованный формат и сохраняйте остальные случаи для разбора.

Разделить причины отсутствующего числа

Пробелы по краям разрешены, пустота означает отсутствие количества, посторонний текст — ошибку. Дополнительно запрещаем бесконечность: парсер может её прочитать, но количество заказа должно быть конечным. Для каждой причины строим маску, сохраняя row_id.

Числовое поле живёт рядом с сырым. По маске можно показать владельцу выгрузки точное исходное значение, не восстанавливая его из NaN.

import numpy as np
import pandas as pd
x = pd.DataFrame({"row_id": [1, 2, 3, 4, 5], "qty_raw": ["12", " 7 ", "", "ошибка", "inf"]})
s = x["qty_raw"].astype("string").str.strip()
missing = s.isna() | s.eq("")
x["qty"] = pd.to_numeric(s.mask(missing), errors="coerce")
invalid_parse = ~missing & x["qty"].isna()
invalid_finite = x["qty"].notna() & ~np.isfinite(x["qty"])
x["reason"] = pd.Series("", index=x.index, dtype="string")
x.loc[invalid_parse, "reason"] = "not_numeric"
x.loc[invalid_finite, "reason"] = "not_finite"
issues = x.loc[invalid_parse | invalid_finite]
print(issues.to_string(index=False))
assert issues["row_id"].tolist() == [4, 5]
assert missing.sum() == 1 and x.loc[[0, 1], "qty"].sum() == 19

Строка 4 имеет ошибку парсинга, строка 5 — недопустимую бесконечность. Строка 3 неизвестна, но не нарушает формат. Допустима ли она целиком, зависит от обязательности количества.

Проверить смысл успешно прочитанного

После парсинга проверьте знак, диапазон и целочисленность. Число 1.5 допустимо для весового товара, но может быть запрещено для штучного. Эти правила возникают из предметной области, а не из возможностей to_numeric.

Идентификатор из цифр не обязательно является числом. Ведущие нули и точность длинного кода могут быть частью значения. Для номера договора сохраняйте строковый тип при загрузке: успешная арифметика над ним не нужна.

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

Закрепите контрольные входы: пустота, пробелы, отрицательное и дробное число, бесконечность, посторонний текст. Изменение правил очистки должно затрагивать только согласованные случаи. Сырой столбец и причины позволяют проверить это на каждой поставке.

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

В бесплатном уроке read_csv вы научитесь задавать типы при загрузке. Затем сохраните сырой столбец по примеру статьи и превратите неудачные преобразования в отдельную очередь проверки. Откройте бесплатный урок и задачи. Полная последовательность тем — в бесплатном курсе.

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

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

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