Приведение типов, переименование и замена
После загрузки данные почти всегда нужно привести к нужным типам и привести в порядок имена и значения.
Приведение типов превращает строки в числа и даты; rename/replace/map чистят имена столбцов и сами значения.
astype: прямое приведение типа
astype — самый прямой способ сменить тип, когда вы уверены, что данные чистые:
df["id"] = df["id"].astype("Int64") # к nullable-целому
df["флаг"] = df["флаг"].astype("boolean") # к nullable-булеву
df["город"] = df["город"].astype("category")
df["цена"] = df["цена"].astype("float64")
Минус astype — он не прощает грязи: astype(int) на столбце, где затесалась строка "—" или пустое значение, упадёт с ошибкой. Для устойчивого приведения «с мусором» есть специальные функции с параметром errors.
to_numeric и to_datetime: устойчивое приведение
Ключевой параметр — errors="coerce": то, что не удалось распознать, становится пропуском вместо падения. Это рабочая лошадка очистки реальных данных.
# строки → числа; нераспознанное → NaN
df["цена"] = pd.to_numeric(df["цена"], errors="coerce")
# строки → даты; нераспознанное → NaT
df["дата"] = pd.to_datetime(df["дата"], errors="coerce")
# с явным форматом — быстрее и надёжнее
df["дата"] = pd.to_datetime(df["дата"], format="%d.%m.%Y", errors="coerce")
Стратегия очистки обычно такая: «привести через coerce → посчитать, сколько стало пропусков (isna().sum()) → решить, заполнять их или удалять». Так нечисловой мусор сначала становится явным пропуском, а уже потом обрабатывается стратегией из предыдущего урока.
Покажем идею coerce на чистом Python: пробуем превратить каждое значение в число, а что не вышло — помечаем как пропуск:
raw = ["100", "2 500", "—", "18000", "", "300"]
def to_num(x):
s = x.replace(" ", "") # убираем разделители тысяч
try:
return int(s)
except ValueError:
return None # "coerce": не распарсилось → пропуск
cleaned = [to_num(x) for x in raw]
print(cleaned)
valid = [v for v in cleaned if v is not None]
print("распознано:", len(valid), "из", len(raw))
print("сумма:", sum(valid))
Вывод:
[100, 2500, None, 18000, None, 300] распознано: 4 из 6 сумма: 20900
Значения "—" и пустая строка стали None (аналог NaN), остальные превратились в числа. Так pd.to_numeric(errors="coerce") и спасает от падения на грязных данных.
rename: имена столбцов и индекса
Сырые данные часто приходят с неудобными именами столбцов — пробелы, регистр, кириллица вперемешку с латиницей. rename исправляет это по словарю «старое имя → новое»:
df = df.rename(columns={"Общая Сумма": "summa", "Дата Заказа": "order_date"})
df = df.rename(index={0: "первая"}) # переименовать метку строки
df.columns = df.columns.str.strip().str.lower() # массово: обрезать и в нижний регистр
Последняя строка — частый приём: str-методы по df.columns разом нормализуют все имена столбцов (убрать пробелы, привести к нижнему регистру).
replace: замена значений
replace меняет сами значения — по одному, списком или по словарю. Удобно для приведения разнобоя к единому виду:
df["пол"] = df["пол"].replace({"М": "мужской", "Ж": "женский"})
df["статус"] = df["статус"].replace(["-", "н/д", ""], np.nan) # мусор → NaN
df = df.replace({"цена": {-1: np.nan}}) # по конкретному столбцу
map: перекодировка Series по словарю
map применяется к Series и заменяет каждое значение по словарю или функции. В отличие от replace, значения, которых нет в словаре, превращаются в NaN — это удобно как «строгая перекодировка», ловящая неожиданные категории:
codes = ["A", "B", "A", "C", "B", "X"]
mapping = {"A": "эконом", "B": "стандарт", "C": "премиум"}
# map по словарю: ключа нет → None (аналог NaN)
result = [mapping.get(code) for code in codes]
print(result)
unknown = [c for c, v in zip(codes, result) if v is None]
print("неизвестные коды:", unknown)
Вывод:
['эконом', 'стандарт', 'эконом', 'премиум', 'стандарт', None] неизвестные коды: ['X']
Код "X" отсутствует в словаре и превратился в None — так map подсвечивает категории, о которых вы не знали. replace в той же ситуации оставил бы "X" как есть. Это и есть ключевое различие: map — строгая полная перекодировка, replace — точечная правка отдельных значений.
Порядок шагов очистки имеет значение
Очистка — это конвейер, и порядок операций влияет на результат. Разумная последовательность для типичного «грязного» CSV выглядит так:
- Нормализовать имена столбцов (
strip, нижний регистр) — чтобы дальше обращаться к ним предсказуемо. - Заменить маркеры пропуска (
"-","н/д", пустые строки) на настоящийNaNчерезreplace— иначе они помешают приведению типов. - Привести типы через
to_numeric/to_datetimeсcoerce— теперь нечисловой мусор станетNaN. - Обработать пропуски осознанной стратегией (заполнить/удалить/интерполировать).
- Убрать дубликаты после нормализации ключей.
Если поменять шаги местами — например, приводить типы до замены маркеров пропуска — astype упадёт, а to_numeric молча превратит в NaN больше, чем вы ожидали. Продумывание порядка превращает хаотичную правку в воспроизводимый пайплайн, который можно применить и к следующей выгрузке.
Подводные камни
- astype падает на грязи. Для реальных данных предпочитайте
to_numeric/to_datetimeсerrors="coerce". - Парсинг дат без формата медленный и угадывает. На больших данных задавайте
format=явно — это и быстрее, и без сюрпризов «месяц перепутан с днём». - map превращает непокрытые значения в NaN. Если это нежелательно, используйте
replaceилиmapс последующимfillna. - rename без присваивания не меняет df. Либо
df = df.rename(...), либо результат уйдёт «в никуда».
Лучшие практики
- Приводите типы как можно раньше — в идеале при чтении файла через
dtype=/parse_dates=. - Для грязных данных —
to_numeric/to_datetimeсcoerce, затем осознанная обработка получившихся пропусков. - Нормализуйте имена столбцов сразу после загрузки — это упростит весь дальнейший код.
map— для полной перекодировки категорий,replace— для точечной замены значений.
Итог
astype— прямое приведение для чистых данных; падает на мусоре.to_numeric/to_datetimeсerrors="coerce"превращают нераспознанное в пропуск.renameчистит имена столбцов и меток индекса.replace— точечная замена значений,map— строгая перекодировка по словарю.