Аналитика28 августа 2026 г.

Категориальные значения pandas: как проверить неизвестные статусы в выгрузке

Автор
Ненашев А.
На чтение
7 мин
Дата обновления
28.08.2026
Старт:В любой момент
Срок обучения:5 часов
Pandas для анализа данных: практический курс
Курс предназначен для разработчиков с базовым знанием Python, желающих освоить библиотеку Pandas для решения реальных рабочих задач. Вы научитесь загружать, очищать и анализировать табличные данные, а также подготавливать датасеты для машинного обучения и автоматизировать отчетность.
Подробнее
1 950 ₽1 950 ₽
#COURSE##INNER#

Категориальные значения pandas: как проверить неизвестные статусы в выгрузке

Если в выгрузке появился новый статус, его легко потерять двумя разными способами. Первый — привести колонку к dtype="category" и решить, что справочник уже проверен. Но при таком приведении pandas по умолчанию просто соберет категории из самих данных, то есть новый статус станет обычной категорией и не будет считаться ошибкой. Второй способ опаснее: задать CategoricalDtype со своим списком статусов и сразу привести колонку к нему. Тогда значения вне справочника превратятся в NaN и смешаются с настоящими пропусками.

Поэтому на вопрос «как проверить неизвестные статусы в выгрузке, не превращая их молча в пропуски?» ответ такой: сначала хранить исходное значение, затем сделать аккуратную нормализацию, после этого отдельно вычислить маски missing и unknown, и только потом приводить подтвержденные значения к категориальному типу. Такой порядок дает проверяемый отчет и не стирает сигнал о новых статусах.

Почему простое приведение к category не решает задачу

У категориального типа в pandas есть полезное свойство: колонка хранит ограниченный набор допустимых значений и может иметь явный порядок. Но сама по себе категория не равна валидации справочника. Если написать astype("category"), pandas выведет список категорий из тех значений, которые уже встретились в колонке. Для сжатия памяти это удобно, для контроля качества — нет. Статус возврат, которого нет в вашем процессе, просто окажется еще одной категорией рядом с новый и оплачен.

Обратная крайность — заранее задать список допустимых статусов через CategoricalDtype. Это уже ближе к справочнику, потому что вы фиксируете разрешенные значения. Но здесь есть другой риск: все, что не входит в список категорий, станет NaN. После такого шага невозможно по самой категориальной колонке понять, где в исходнике было пусто, а где пришел новый или ошибочный статус. Для контроля выгрузки это слишком поздно.

Практический вывод простой: категориальный тип стоит использовать после проверки, а не вместо нее. Валидация отвечает на вопрос «что пришло и что с этим не так», а category — на вопрос «как удобно хранить и использовать уже проверенные значения».

Справочник статусов: задайте его отдельно от текущей выгрузки

Справочник должен жить отдельно от данных за день или за неделю. Если вы строите его из unique() по текущей колонке, вы не проверяете выгрузку, а просто переписываете ее состав в более компактном виде. Кроме того, уникальные значения показывают только то, что встретилось в этом файле, а не полный набор допустимых статусов. Поэтому опираться на них как на эталон неудобно: отсутствующий сегодня, но допустимый статус выпадет из такого «справочника» без всякой причины.

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

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

Нормализация входа без молчаливой подмены смысла

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

Учебный пример. Ниже — условная выгрузка в среде Python с pandas. В ней есть корректные статусы, пустые значения, опечатка и новый статус, которого нет в справочнике. Мы специально оставляем колонку status_raw как есть, чтобы потом показать нарушение в исходном виде, а не только после очистки.

import pandas as pd
from pandas.api.types import CategoricalDtype

orders = pd.DataFrame({
    "order_id": [101, 102, 103, 104, 105, 106, 107, 108, 109],
    "status_raw": [
        " Новый ",
        "ОПЛАЧЕН",
        "отгружен",
        "возврат",
        "",
        None,
        "оплочен",
        "отменён",
        "Оплата   получена",
    ],
})

reference = pd.Index(["новый", "оплачен", "отгружен", "отменен"])
aliases = {"оплата получена": "оплачен"}

def normalize_status(s: pd.Series) -> pd.Series:
    out = s.astype("string")
    out = out.str.strip().str.lower()
    out = out.str.replace(r"\s+", " ", regex=True)
    out = out.str.replace("ё", "е", regex=False)
    out = out.mask(out.eq(""), pd.NA)
    out = out.replace(aliases)
    return out

orders["status_norm"] = normalize_status(orders["status_raw"])

Здесь нормализация делает четыре проверяемых вещи. " Новый " становится новый, ОПЛАЧЕН — оплачен, отменён — отменен, а пустая строка превращается в настоящий пропуск. Отдельно задан один согласованный алиас: оплата получена переводится в оплачен. При этом возврат и оплочен никуда не исчезают: они сохраняются как подозрительные значения, которые и нужно проверить.

Как найти неизвестные значения до приведения к категориальному типу

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

is_missing = orders["status_norm"].isna()
is_known = orders["status_norm"].isin(reference)
is_unknown = (~is_missing) & (~is_known)

orders["problem"] = "ok"
orders.loc[is_missing, "problem"] = "missing"
orders.loc[is_unknown, "problem"] = "unknown_status"

violations = orders.loc[
    orders["problem"] != "ok",
    ["order_id", "status_raw", "status_norm", "problem"]
].copy()

summary = (
    violations.groupby(["problem", "status_norm"], dropna=False)
    .size()
    .reset_index(name="rows")
    .sort_values(["problem", "rows"], ascending=[True, False])
)

cat_type = CategoricalDtype(categories=reference.tolist(), ordered=False)
orders["status_checked"] = orders["status_norm"].where(is_known, pd.NA)
orders["status_cat"] = orders["status_checked"].astype(cat_type)

В этом учебном наборе результат легко проверить вручную. Корректных строк после нормализации получилось 5: новый, оплачен, отгружен, отменен и алиас оплата получена → оплачен. Неизвестных статусов 2: возврат и оплочен. Пропусков тоже 2: пустая строка и None. Итого 5 + 2 + 2 = 9 строк, арифметика сходится.

Теперь видно, почему прямое приведение к CategoricalDtype без промежуточной проверки неудобно. Если бы мы сразу сделали orders["status_norm"].astype(cat_type), то получили бы 4 значения NaN: два настоящих пропуска и два неизвестных статуса. Число строк с проблемой сохранилось бы, а вот причина проблемы — нет. Разделение на missing и unknown_status нужно сделать раньше.

ПроверкаРезультат в примереЧто это значит
Корректные статусы5Их можно безопасно приводить к category
Неизвестные статусы2Нужна проверка источника или справочника
Пропуски2Поле не заполнено, это отдельный тип нарушения
NaN после прямого astype(cat_type)4Неизвестные и пустые значения смешались бы

Отчет о нарушениях: что сохранять и как использовать дальше

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

В нашем примере список нарушений покажет четыре строки: две с missing, одну с unknown_status = возврат и одну с unknown_status = оплочен. Это уже практически полезный результат: по возврат вы решаете, действительно ли процесс изменился и нужно расширить справочник; по оплочен видно, что это, скорее всего, опечатка или ошибка источника; по пропускам можно отдельно считать долю незаполненных статусов и следить за ней во времени.

Рабочее правило для пайплайна такое: храните status_raw, создавайте status_norm, отмечайте problem, и только затем формируйте status_cat для дальнейшей аналитики. Тогда группировки, фильтры и сводные таблицы опираются на чистый категориальный столбец, а диагностика не теряется. Если позже справочник расширится, вы сможете пересчитать только этап валидации и увидеть, какие вчерашние ошибки стали допустимыми значениями, а какие нет.

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

Курсы

166871
0,0
0 отзывов
Pandas для анализа данных: практический курс
  • В любой момент
  • 5 часов
  • самостоятельно
  • сертификат
1 950 ₽1 950 ₽
Перейти на сайт