PandasJuniorCoding

Как обрабатывать пропущенные значения в Pandas — isnull(), dropna(), fillna()?

isnull() обнаруживает пропуски (возвращает булеву маску), dropna() удаляет строки/столбцы с пропусками, fillna() заполняет их константой, статистикой или методом ffill/bfill.

Работа с пропущенными значениями в Pandas

Пропущенные значения (NaN, None, pd.NA) — одна из самых частых проблем в реальных данных. Pandas предоставляет три основных инструмента: обнаружение (isnull()/notnull()), удаление (dropna()) и заполнение (fillna()).

Обнаружение: isnull() и notnull()

isnull() (псевдоним: isna()) возвращает булеву маску той же формы, где True означает пропуск. notnull() — инверсия.

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'name':   ['Alice', 'Bob', None,    'Diana'],
    'age':    [25,      np.nan, 30,      22],
    'salary': [50000,   60000,  np.nan,  np.nan],
})

# булева маска
print(df.isnull())
#     name    age  salary
# 0  False  False   False
# 1  False   True   False
# 2   True  False    True
# 3  False  False    True

# количество пропусков по столбцам
print(df.isnull().sum())
# name      1
# age       1
# salary    2

# процент пропусков
print(df.isnull().mean().round(2))
# name      0.25
# age       0.25
# salary    0.50

# строки, где хотя бы одно значение пропущено
print(df[df.isnull().any(axis=1)])

# строки без единого пропуска
print(df[df.notnull().all(axis=1)])

Удаление: dropna()

dropna() удаляет строки или столбцы с пропусками. Ключевые параметры: axis, how ('any' / 'all'), thresh (минимум непустых значений), subset (проверять только эти столбцы).

# удалить строки, где хотя бы один пропуск (по умолчанию)
df_clean = df.dropna()
print(df_clean)
#     name   age   salary
# 0  Alice  25.0  50000.0

# удалить строки, где ВСЕ значения пропущены
df_no_all_nan = df.dropna(how='all')

# удалить строки с пропуском только в конкретных столбцах
df_need_age = df.dropna(subset=['age'])

# оставить строки, где непустых значений >= 2
df_thresh = df.dropna(thresh=2)

# удалить столбцы с любым пропуском
df_no_empty_cols = df.dropna(axis=1)

Заполнение: fillna()

fillna() заменяет пропуски константой, словарём значений или результатом метода интерполяции (method='ffill', 'bfill').

# заполнить все пропуски одним значением
df_zero = df.fillna(0)

# разные значения для разных столбцов
df_specific = df.fillna({'age': df['age'].median(), 'salary': df['salary'].mean()})

# forward fill — заполнить предыдущим значением (актуально для временных рядов)
ts = pd.DataFrame({'value': [1.0, np.nan, np.nan, 4.0, np.nan]})
ts_ffill = ts.fillna(method='ffill')   # или ts['value'].ffill()
print(ts_ffill)
# 0    1.0
# 1    1.0
# 2    1.0
# 3    4.0
# 4    4.0

# backward fill
ts_bfill = ts.fillna(method='bfill')   # или ts['value'].bfill()

# interpolate — линейная интерполяция (лучше для числовых рядов)
ts_interp = ts.interpolate(method='linear')
print(ts_interp)
# 0    1.000000
# 1    2.000000
# 2    3.000000
# 3    4.000000
# 4    4.000000

# ограничить количество последовательных заполнений
ts_limited = ts['value'].ffill(limit=1)

Проверка после обработки

result = df.fillna({'age': df['age'].median(), 'salary': df['salary'].mean()}).dropna()
assert result.isnull().sum().sum() == 0, "Остались пропуски!"
print(f"Итог: {len(result)} строк, пропусков нет")

Подводные камни

  • inplace=True не работает в цепочках: df.fillna(0, inplace=True) возвращает None, и если вы присвоите результат переменной — потеряете данные. Используйте df = df.fillna(0).
  • ffill/bfill не заполняет первый/последний NaN: ffill не заполнит NaN в начале ряда (нет предыдущего значения), bfill — в конце. Комбинируйте оба метода: .ffill().bfill().
  • dropna() с thresh считает непустые, не пустые: thresh=2 означает «оставить строки, где хотя бы 2 значения непустые», а не «удалить строки с более чем 2 пропусками» — легко перепутать.
  • Заполнение медианой/средним до сплита: при ML-пайплайне нельзя вычислять median/mean по всему датасету до train/test split — это data leakage. Считайте статистики только на train.
  • isnull() не ловит строки 'nan', 'NULL', '': при чтении CSV грязные данные часто приходят как строка 'nan' или пустая строка. Используйте pd.read_csv(..., na_values=['nan', 'NULL', '', 'N/A']).
  • method='ffill' устарел в fillna(): начиная с Pandas 2.2 параметр method= в fillna() помечен как deprecated — используйте df.ffill() и df.bfill() напрямую.

Common mistakes

  • Объяснять missing values только синтаксисом без shape, dtype, состояния или режима выполнения.
  • Игнорировать leakage, воспроизводимость, пустые входы и скрытые копии данных.
  • Не проверять production-симптомы: latency, память, ретраи, дрейф качества и несовпадение версий.

What the interviewer is testing

  • Может ли связать missing values с реальным контрактом входов и выходов.
  • Упоминает ли тесты, метрики, reproducibility и диагностику ошибок.
  • Видит ли различие между demo-кодом в ноутбуке и production-пайплайном.

Sources

Related topics