5 методов обнаружения NaN в Pandas: от isna() до тепловых карт
Для кого эта статья:
- Аналитики данных и специалисты по обработке данных
- Студенты и аспиранты, изучающие язык программирования Python и библиотеки для анализа данных
Профессионалы, стремящиеся развить навыки в области машинного обучения и статистики
Обработка данных — как диагностика здоровья: важно вовремя заметить "патологию". NaN-значения — настоящие невидимки в ваших датасетах, способные полностью исказить результаты анализа. Представьте: вы строите модель прогнозирования продаж, а 15% ваших данных — пустые ячейки, которые вы даже не заметили! 📊 В Pandas существует целый арсенал инструментов для выявления этих "дыр" в данных, и сегодня я поделюсь пятью наиболее мощными методами, позволяющими безошибочно находить и обрабатывать NaN с хирургической точностью.
Хотите стать экспертом в обнаружении скрытых проблем в данных? Программа Профессия аналитик данных от Skypro погрузит вас в реальные кейсы по очистке и преобразованию данных. Вы научитесь не просто находить NaN-значения, но и принимать стратегические решения по их обработке, что критически важно для построения надёжных моделей. Более 87% выпускников уже через 3 месяца после курса успешно применяют эти навыки в коммерческих проектах.
Что такое NaN и почему важно их обнаруживать в данных
NaN (Not a Number) — специальное значение в Pandas и NumPy, обозначающее отсутствие данных. В реальных датасетах NaN-значения возникают по множеству причин: ошибки при сборе данных, технические сбои, пропуски в анкетах, неприменимость параметра к конкретному объекту и даже намеренное утаивание информации.
Игнорирование NaN может привести к катастрофическим последствиям для аналитики:
- Искажение статистических показателей (среднее, медиана, дисперсия)
- Некорректная работа алгоритмов машинного обучения
- Ложные корреляции между переменными
- Невозможность выполнения определенных математических операций
- Ошибки при визуализации данных
Особенность NaN в том, что это "заразное" значение — любая операция с участием NaN дает результат NaN. Например, 5 + NaN = NaN, NaN * 10 = NaN. Поэтому даже несколько пропущенных значений способны испортить весь анализ. 🧪
Михаил Горшков, ведущий аналитик данных
Однажды наша команда разрабатывала модель оценки кредитоспособности клиентов. После месяца работы мы заметили странные аномалии в прогнозах — модель необъяснимо занижала рейтинги определенной группы клиентов. После тщательной проверки мы обнаружили, что 8% записей содержали NaN в поле "срок работы на последнем месте". Алгоритм интерпретировал их как нули, что создавало ложную корреляцию между небольшим стажем и низкой платежеспособностью. Простая предварительная проверка на NaN сэкономила бы нам недели работы и потенциальные миллионные убытки от неверных решений.
В Python NaN представлен как float('nan') или numpy.nan. Важное свойство NaN — его нельзя проверить с помощью обычного сравнения. Вот что происходит при попытке сравнения:
import numpy as np
# Это всегда дает False!
np.nan == np.nan # False
np.nan > 0 # False
np.nan < 0 # False
np.nan == 0 # False
Именно поэтому Pandas предлагает специальные методы для выявления пропущенных значений, которые мы сейчас рассмотрим.

Метод isna() и isnull(): базовые способы поиска пропусков
Pandas предлагает два идентичных метода для обнаружения NaN-значений: isna() и isnull(). Функционально они абсолютно равнозначны и могут использоваться взаимозаменяемо — выбор между ними лишь вопрос личных предпочтений в стиле кода. 🔄
Эти методы возвращают булевы маски, где True соответствует NaN-значению, а False — любому другому значению:
import pandas as pd
import numpy as np
# Создаем DataFrame с пропусками
df = pd.DataFrame({
'A': [1, np.nan, 3, np.nan],
'B': [np.nan, 5, 6, 7],
'C': [8, 9, np.nan, 0]
})
# Проверяем на NaN с помощью isna()
mask_isna = df.isna()
print("Результат isna():")
print(mask_isna)
# Проверяем на NaN с помощью isnull()
mask_isnull = df.isnull()
print("\nРезультат isnull():")
print(mask_isnull)
Оба метода вернут идентичный результат — булеву матрицу того же размера, что и исходный DataFrame:
| A | B | C | |
|---|---|---|---|
| 0 | False | True | False |
| 1 | True | False | False |
| 2 | False | False | True |
| 3 | True | False | False |
Для проверки наличия нормальных (не-NaN) значений можно использовать противоположные методы notna() и notnull().
Преимущества этих методов:
- Корректно распознают различные типы пропущенных значений (numpy.nan, None, pd.NA)
- Работают как с отдельными сериями, так и с целыми DataFrame
- Могут применяться к конкретным столбцам:
df['column_name'].isna() - Позволяют быстро проверить наличие любых пропусков:
df.isna().any().any()
Важные случаи применения:
| Проверка наличия хотя бы одного NaN в DataFrame | df.isna().any().any() |
|---|---|
| Проверка наличия NaN в конкретном столбце | df['column'].isna().any() |
| Проверка строк, содержащих хотя бы один NaN | df.isna().any(axis=1) |
| Проверка полностью заполненных строк | ~df.isna().any(axis=1) |
| Проверка полностью пустых строк | df.isna().all(axis=1) |
Подсчет и визуализация NaN с помощью sum() и heatmap
Обнаружение NaN — только первый шаг. Для эффективного анализа необходимо понимать масштаб проблемы: сколько пропусков, где они концентрируются и существуют ли шаблоны их появления. 🔍
Самый распространенный способ подсчета NaN — комбинирование isna() с агрегирующими функциями:
# Количество NaN в каждом столбце
nan_count_columns = df.isna().sum()
print("NaN по столбцам:")
print(nan_count_columns)
# Процент NaN в каждом столбце
nan_percent_columns = df.isna().mean() * 100
print("\nПроцент NaN по столбцам:")
print(nan_percent_columns)
# Количество NaN в каждой строке
nan_count_rows = df.isna().sum(axis=1)
print("\nNaN по строкам:")
print(nan_count_rows)
Эти операции дают четкую статистику распределения пропусков, но для больших датасетов визуализация может быть гораздо информативнее. Тепловые карты (heatmap) — идеальный инструмент для этой задачи:
import matplotlib.pyplot as plt
import seaborn as sns
# Создаем матрицу пропусков (True где NaN)
plt.figure(figsize=(10, 6))
sns.heatmap(df.isna(), cbar=False, cmap='viridis')
plt.title('Карта пропущенных значений')
plt.tight_layout()
plt.show()
# Корреляционная тепловая карта пропущенных значений
# Показывает, как пропуски в разных столбцах связаны друг с другом
plt.figure(figsize=(10, 8))
nan_corr = df.isna().corr()
sns.heatmap(nan_corr, annot=True, cmap='coolwarm')
plt.title('Корреляция пропущенных значений')
plt.tight_layout()
plt.show()
Тепловая карта позволяет мгновенно визуально определить:
- Столбцы с наибольшим количеством пропусков
- Строки, содержащие много NaN
- Паттерны пропущенных значений (например, если определенные поля обычно пропущены вместе)
- Корреляции между пропусками в разных переменных
Высокая корреляция пропусков между столбцами часто указывает на систематическую проблему в данных или логическую взаимосвязь между переменными. Например, если клиент не указал свой доход, вероятно, он не укажет и данные о кредитной истории. 📈
Елена Соколова, руководитель отдела аналитики
В проекте анализа клиентских данных медицинской клиники мы столкнулись с проблемой отсутствия информации у пациентов старшей возрастной группы. Построив тепловую карту пропусков, мы обнаружили удивительную закономерность: пропуски в полях "email", "мобильный телефон" и "предпочитаемый способ связи" образовывали четкие кластеры. Визуализация позволила понять, что около 78% пациентов старше 65 лет не указывали электронную почту, что привело к систематической ошибке в маркетинговых кампаниях. Мы скорректировали стратегию коммуникации для этого сегмента, что увеличило отклик на 43%. Без визуализации NaN этот паттерн мог бы остаться незамеченным среди тысяч записей.
Для полной картины полезно также использовать библиотеку missingno, специализирующуюся на визуализации пропущенных данных:
import missingno as msno
# Матричная визуализация
msno.matrix(df)
# Визуализация в виде полос
msno.bar(df)
# Дендрограмма пропущенных значений (кластеризация NaN)
msno.dendrogram(df)
Фильтрация DataFrame на основе пропущенных значений
После обнаружения NaN часто требуется отфильтровать данные для дальнейшего анализа или обработки. Pandas предлагает элегантные способы фильтрации строк или столбцов, содержащих (или не содержащих) пропущенные значения. 🧹
Булевые маски, создаваемые методами isna() и notna(), — отличная основа для построения фильтров:
# Выбрать строки, где в столбце A есть пропуски
rows_with_nan_in_A = df[df['A'].isna()]
# Выбрать строки, где в столбце A нет пропусков
rows_without_nan_in_A = df[df['A'].notna()]
# Выбрать строки, где есть пропуски в любом столбце
rows_with_any_nan = df[df.isna().any(axis=1)]
# Выбрать строки без единого пропуска
complete_rows = df[~df.isna().any(axis=1)]
# Выбрать строки, полностью состоящие из пропусков
all_nan_rows = df[df.isna().all(axis=1)]
# Выбрать столбцы, содержащие пропуски
columns_with_nan = df.loc[:, df.isna().any()]
# Выбрать столбцы без пропусков
columns_without_nan = df.loc[:, ~df.isna().any()]
Pandas также предлагает встроенные методы для быстрой фильтрации по NaN-значениям:
dropna()— удаляет строки или столбцы с NaNfillna()— заменяет NaN указанным значениемinterpolate()— заполняет NaN интерполированными значениями
Метод dropna() особенно гибок благодаря множеству параметров:
# Удалить строки, содержащие хотя бы один NaN
df_clean_rows = df.dropna()
# Удалить столбцы, содержащие хотя бы один NaN
df_clean_columns = df.dropna(axis=1)
# Удалить строки, где все значения — NaN
df_no_empty_rows = df.dropna(how='all')
# Удалить строки, где менее 2 непропущенных значений
df_min_values = df.dropna(thresh=2)
# Удалить строки с NaN только в указанных столбцах
df_specific_columns = df.dropna(subset=['A', 'B'])
При работе с временными рядами полезно изучить распределение NaN во времени:
# Для временных рядов: количество пропусков по дням недели
time_series = pd.DataFrame({
'date': pd.date_range(start='2023-01-01', periods=100),
'value': [np.nan if i % 7 == 0 else i for i in range(100)]
})
# Количество NaN по дням недели
nan_by_weekday = time_series.set_index('date')['value'].isna().groupby(
time_series['date'].dt.day_name()
).mean() * 100
print("Процент NaN по дням недели:")
print(nan_by_weekday)
Такой анализ может выявить периодичность в пропусках данных, например, если сбор данных менее эффективен в выходные.
Практические приемы обработки NaN в аналитических проектах
Обнаружение NaN — только половина дела. Аналитику требуется принять стратегическое решение, как поступить с обнаруженными пропусками. Идеальная стратегия зависит от типа данных, цели анализа и механизма возникновения пропусков. 🧩
Основные стратегии обработки NaN можно разделить на несколько категорий:
| Стратегия | Метод в Pandas | Когда применять |
|---|---|---|
| Удаление строк/столбцов | df.dropna() | Мало пропусков, большой датасет |
| Заполнение константой | df.fillna(value) | Значение по умолчанию имеет смысл |
| Заполнение статистикой | df.fillna(df.mean()) | Непрерывные данные, случайные пропуски |
| Заполнение предыдущим/следующим | df.fillna(method='ffill') | Временные ряды, последовательные измерения |
| Интерполяция | df.interpolate() | Непрерывные данные с трендом |
При работе с машинным обучением может потребоваться более сложная стратегия:
from sklearn.impute import KNNImputer, SimpleImputer
# Простая импутация средним
imputer = SimpleImputer(strategy='mean')
df_imputed = pd.DataFrame(
imputer.fit_transform(df),
columns=df.columns
)
# KNN-импутация (использует соседние точки)
knn_imputer = KNNImputer(n_neighbors=5)
df_knn_imputed = pd.DataFrame(
knn_imputer.fit_transform(df),
columns=df.columns
)
Практические рекомендации по работе с NaN в аналитических проектах:
- Всегда исследуйте причины появления NaN перед их обработкой — это может дать ценную информацию
- Создайте отдельные флаги-индикаторы пропусков перед заполнением — сам факт пропуска может быть информативным
- Проверяйте распределение данных до и после обработки NaN, чтобы избежать искажений
- Для критически важных данных рассмотрите несколько стратегий импутации и сравните результаты
- При обучении моделей используйте кросс-валидацию, чтобы убедиться, что обработка NaN не привела к переобучению
Продвинутый подход — применение методов множественной импутации для учета неопределенности при заполнении пропусков:
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
import numpy as np
# MICE (Multiple Imputation by Chained Equations)
mice_imputer = IterativeImputer(random_state=0)
df_mice = pd.DataFrame(
mice_imputer.fit_transform(df),
columns=df.columns
)
Не менее важно документировать все решения по обработке NaN и их обоснование — это повышает воспроизводимость анализа и облегчает поиск ошибок в случае аномальных результатов.
Работа с NaN в Pandas — искусство баланса между сохранением данных и обеспечением их качества. Пять рассмотренных методов: базовое обнаружение с isna(), подсчет с sum(), визуализация через heatmap, фильтрация DataFrame и стратегическая обработка пропусков — формируют полноценный арсенал для работы с любыми датасетами. Помните: правильно обработанные пропуски могут превратить ненадежные данные в надежную основу для принятия решений. NaN — не приговор, а возможность продемонстрировать свое мастерство в обработке данных.