Пропуски в Pandas: шпаргалка для собеседования
Зачем аналитику уметь чистить NaN
Реальные данные почти всегда с пропусками. Если не обрабатывать NaN — агрегаты врут, merge ломается, графики рисуются странно.
На собесе проверяют знание трёх базовых приёмов: isna, fillna, dropna — и умение выбрать, какой применить в конкретной задаче.
Проверка на NaN
# Есть ли NaN в колонке
df['col'].isna() # Series bool
df['col'].notna() # обратное
# Количество NaN
df['col'].isna().sum()
df.isna().sum() # по всем колонкам
df.isna().sum().sum() # всего NaN в таблице
# Процент NaN
df['col'].isna().mean() * 100Удаление пропусков — dropna
# Удалить строки с NaN в любой колонке
df.dropna()
# Удалить только где NaN в конкретной колонке
df.dropna(subset=['email'])
# Удалить строки, где ВСЕ колонки NaN
df.dropna(how='all')
# Удалить строки, где >50% колонок NaN
df.dropna(thresh=len(df.columns) // 2)
# Удалить колонки с NaN
df.dropna(axis=1)На собесе: часто спрашивают «когда dropna опасно». Ответ: когда вы теряете строки с важной информацией из-за NaN в одной второстепенной колонке. Всегда явно указывайте subset.
Заполнение — fillna
# Константой
df['amount'].fillna(0)
df['status'].fillna('unknown')
# Средним / медианой
df['amount'].fillna(df['amount'].mean())
df['amount'].fillna(df['amount'].median())
# Forward fill (последнее известное значение)
df['price'].fillna(method='ffill')
# Backward fill
df['price'].fillna(method='bfill')
# Разные значения для разных колонок
df.fillna({'amount': 0, 'status': 'unknown', 'email': ''})Если хочется сразу закрепить тему на практике — открой тренажёр в Telegram. 10 минут в день — и синтаксис в пальцах.
Заполнение по группе — transform + fillna
# Заполнить NaN средним по городу
df['amount'] = df.groupby('city')['amount'].transform(
lambda x: x.fillna(x.mean())
)Это частый приём в продуктовой аналитике — заполнить пропуски «похожими» значениями.
Интерполяция — временные ряды
# Линейная интерполяция
df['price'].interpolate()
# По времени (нужен datetime index)
df.set_index('date')['price'].interpolate(method='time')
# Более сложные
df['price'].interpolate(method='polynomial', order=2)Нужно для восстановления пропущенных точек в time-series, когда ffill выглядит некорректно.
NaN в агрегатах
# Агрегаты pandas по умолчанию игнорируют NaN
df['amount'].sum() # игнорирует NaN
df['amount'].mean() # игнорирует NaN (и в делителе)
df['amount'].count() # считает не-NaN
# Принудительно считать NaN
df['amount'].sum(skipna=False) # NaN → результат NaNНа собесе: «Что вернёт sum([1, 2, NaN])?» Ответ: 3 (игнорирует). sum(skipna=False) → NaN.
NaN vs None vs NaT
np.nan/pd.NA— числовой NaN.None— Python None, pandas часто преобразует в NaN.NaT(Not a Time) — пропуск для datetime.pd.NA— новый универсальный пропуск (pandas 1.0+).
Все обрабатываются одинаково через isna/fillna.
NaN в merge
# NaN не матчится даже с NaN (как в SQL)
pd.merge(df1, df2, on='key')
# Если key = NaN в одной из таблиц — эта строка выпадетЧтобы не только читать теорию, но и решать реальные задачи — загляните в бот Карьерника. Там по каждой теме подборка вопросов с разборами.
NaN в DataFrame с пустыми строками
# Пустая строка — не NaN
df['email'] = df['email'].replace('', np.nan)
# Теперь fillna их покроет
# Или наоборот: NaN → пустая
df['email'].fillna('')Ловушка: '' != NaN. Фильтр df[df['email'] != ''] не уберёт NaN — нужен isna().
10 задач с собесов
1. Показать колонки с процентом NaN
(df.isna().mean() * 100).sort_values(ascending=False).head(10)2. Удалить строки, где email = NaN или ''
df = df[df['email'].notna() & (df['email'] != '')]3. Заполнить NaN средним по группе
df['amount'] = df.groupby('category')['amount'].transform(
lambda x: x.fillna(x.mean())
)4. Forward fill для цены
df.sort_values('date').groupby('ticker')['price'].ffill()5. Количество строк без ни одного NaN
df.dropna().shape[0]6. Процент NaN в каждой колонке визуально
import seaborn as sns
sns.heatmap(df.isna(), cbar=False)7. Заменить NaN в числах на 0, в строках на 'unknown'
num_cols = df.select_dtypes(include='number').columns
str_cols = df.select_dtypes(include='object').columns
df[num_cols] = df[num_cols].fillna(0)
df[str_cols] = df[str_cols].fillna('unknown')8. Флаг «была ли NaN до заполнения»
df['amount_was_nan'] = df['amount'].isna()
df['amount'] = df['amount'].fillna(0)Полезно для ML, чтобы модель видела, что значение искусственное.
9. Интерполировать пропущенные дни в time-series
df = df.set_index('date').resample('D').asfreq() # добавит NaN для отсутствующих дней
df['value'] = df['value'].interpolate()10. NaN в определённых колонках → помечаем проблемной строкой
df['has_issue'] = df[['col_a', 'col_b']].isna().any(axis=1)Как тренироваться
Пропуски — это 20% всей работы аналитика в реальности. В учебниках об этом говорят мало, на собесе — всё чаще.
Тренажёр Карьерник содержит блок задач на NaN: проверка, удаление, интерполяция, группировочные заполнения. С разборами типичных ошибок.
Совет: прежде чем делать fillna(0), всегда думайте — а действительно ли 0 корректно? Если это missing на уровне бизнес-логики (пользователь не сделал N событий), 0 может быть правильным. Если это missing на уровне данных (не дошло до трекера) — 0 врёт.
Читайте также
- Pandas шпаргалка для аналитика
- Пропуски в данных pandas
- Groupby в Pandas: шпаргалка
- NULL в SQL: шпаргалка
FAQ
NaN и None — в чём разница?
В pandas почти нет — None обычно конвертируется в NaN. NaN — это float, поэтому любая колонка с NaN становится float. Для integer-колонок с пропусками pandas предлагает Int64 (с большой I) — nullable integer.
fillna или dropna?
Зависит от задачи. dropna когда пропуски случайны и строк много — можно потерять. fillna когда важно сохранить строку. Если >10% строк имеет NaN — обычно лучше fillna или interpolate.
ffill или bfill?
ffill — когда данные «тянутся» от прошлого (курсы валют, цены). bfill — когда от будущего (редко в аналитике). Для time-series обычно ffill.
Что такое pd.NA?
Новый универсальный пропуск (pandas 1.0+). В отличие от np.nan, работает с любым типом (int, bool, string). Используется в nullable integer/boolean/string dtypes.