Пропуски в таблице: обнаружение, dropna, fillna и nullable dtype
Содержание курса
Почему fillna не заменяет пустую строку
fillna заменяет распознанные пропуски, а "" и строка из пробелов — обычный текст. Если таблица пришла из формы и пустые ответы записались строками, сначала приведите их к pd.NA.
import pandas as pd
s = pd.Series(["Москва", "", " ", None], dtype="string")
print(s.isna().tolist()) # [False, False, False, True]
clean = s.replace(r"^\s*$", pd.NA, regex=True)
result = clean.fillna("Не указан")
print(result.tolist())
# ['Москва', 'Не указан', 'Не указан', 'Не указан']
Здесь пустые и состоящие только из пробелов ответы считаются отсутствующим городом. Непустые названия регулярное выражение не меняет. Для числовых столбцов такое преобразование не требуется: ниже отдельно разобраны NaN и nullable-типы.
В числовых массивах NumPy пропуск в рассмотренном float-сценарии представлен как np.nan, и там его проверяют np.isnan(). Pandas работает с таблицами, где в одном DataFrame могут соседствовать числа, строки и булевы значения — и каждый тип хранит «отсутствие данных» по-своему: np.nan, None или pd.NA. Поэтому pandas предлагает единый интерфейс — isna/notna — который скрывает эти различия и даёт одинаковую маску вне зависимости от dtype столбца.
Обнаружение пропусков: isna, notna и подсчёт через sum
Первый шаг при работе с любыми реальными данными — понять, где именно чего-то не хватает. df.isna() возвращает DataFrame той же формы, но заполненный булевыми значениями: True там, где значение пропущено, False — где данные есть. df.notna() — прямая инверсия: True означает наличие данных.
Оба метода работают одинаково для DataFrame и для Series — форма результата всегда совпадает с формой исходного объекта.
Возьмём таблицу с явными пропусками в разных столбцах:
import pandas as pd
df = pd.DataFrame({
'age': pd.array([25, None, 32], dtype='Int64'),
'salary': [50000.0, float('nan'), 75000.0],
'dept': ['IT', None, 'HR']
})
print(df.isna())
Результат:
age salary dept 0 False False False 1 True True True 2 False False False
Здесь вся вторая строка пропущена. Но в реальных данных пропуски рассеяны, и смотреть на полную матрицу неудобно. Чтобы получить сводку по столбцам, достаточно применить .sum() — булевые значения трактуются как 0/1, и сумма даёт количество пропусков в каждом столбце:
print(df.isna().sum())
age 1 salary 1 dept 1 dtype: int64
Если хочется знать долю пропусков, а не абсолютное число, достаточно разделить на длину таблицы:
print(df.isna().sum() / len(df))
Это стандартный способ быстро оценить «засорённость» данных до того, как принимать решение — удалять строки или заполнять.
Ещё один полезный приём — df.isna().any(): возвращает Series с True по тем столбцам, где есть хоть один пропуск. Удобно для быстрой проверки, а не полного подсчёта:
print(df.isna().any())
age True salary True dept True dtype: bool
На уровне отдельного столбца series.isna() возвращает обычную булеву Series, которую можно сразу использовать для фильтрации. Например, чтобы посмотреть только строки, где salary пропущен:
print(df[df['salary'].isna()])
В результате получаем только строки с пропуском в salary — это удобно для ручной проверки или для отладки логики заполнения.
Диагностика через isna().sum() всегда должна быть первым шагом: она показывает масштаб проблемы и помогает решить, какой инструмент применять дальше — удалять записи или заполнять пропуски.
