Исследование и подготовка данных — это фундамент, на котором строятся любые проекты в Data Science. Новички часто привыкают к "стерильным" учебным датасетам, где все гладко и чисто. Реальность же больше похожа на археологические раскопки: данные почти всегда неполные, с ошибками, неточностями и, конечно, пропусками.
Пропуски появляются по тысяче причин: сломался датчик, оператор поленился внести данные, произошел сбой при передаче, или в старой базе просто не было такого поля. Игнорировать их нельзя: большинство алгоритмов машинного обучения с пропусками не работают.
В зависимости от источника данных пропуски могут иметь разные обозначения. Наиболее часто используется NaN (Not a Number), но также можно встретить «NA», «None», «-999», «0», « », «-», «?» и другие варианты.
Если в датафрейме отсутствуют данные и они обозначены не как NaN, то их необходимо преобразовать в NaN. Например, можно использовать следующий код:
df = df.replace('', np.nan)
Первая реакция любого, кто работает с Pandas, — вызвать df.isna().sum(). Мы получаем аккуратную табличку с количеством пропусков по каждому столбцу и чувствуем, что контролируем ситуацию. Но это иллюзия. Знать сколько данных пропущено — это лишь первый шаг. Гораздо важнее понять, как они пропущены. Есть ли в этих пропусках система? Связаны ли они между собой? Являются ли они случайным шумом или сигналом о серьезных проблемах в данных?
Простой подсчет NaN не ответит на эти вопросы. Чтобы перейти от поверхностного взгляда к глубокому пониманию, нам нужен специализированный инструмент. И здесь на сцену выходит missingno.
Pandas — очень хороший инструмент. И для первичной оценки пропусков у него есть всё необходимое. Прежде чем мы погрузимся в missingno, быстро вспомним арсенал Pandas, чтобы понять его границы.
Возьмем для примера датасет с данными каротажа по скважинам в Норвежском море. Они содержат серию электрических измерений, которые были получены с помощью инструментов для каротажа скважин. Измерения используются для характеристики геологии недр и определения подходящих залежей углеводородов. Это реальные данные, а значит, они неидеальны.
import pandas as pd
import numpy as np
# Загружаем данные
df = pd.read_csv('https://github.com/obulygin/content/raw/refs/heads/main/xeek_data/xeek_train_subset.csv')
df
1. Метод .info()
Это наш первый взгляд на здоровье датафрейма.
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 133198 entries, 0 to 133197
Data columns (total 22 columns):
 #   Column       Non-Null Count   Dtype  
---  ------       --------------   -----  
 0   WELL         133198 non-null  object 
 1   DEPTH_MD     133198 non-null  float64
 2   X_LOC        125805 non-null  float64
 3   Y_LOC        125805 non-null  float64
 4   Z_LOC        125805 non-null  float64
 5   GROUP        133198 non-null  object 
 6   FORMATION    111632 non-null  object 
 7   CALI         133006 non-null  float64
 8   RSHA         62039 non-null   float64
 9   RMED         125556 non-null  float64
 10  RDEP         125805 non-null  float64
 11  RHOB         108053 non-null  float64
 12  GR           133198 non-null  float64
 13  NPHI         91725 non-null   float64
 14  PEF          100840 non-null  float64
 15  DTC          132635 non-null  float64
 16  SP           93680 non-null   float64
 17  ROP          130454 non-null  float64
 18  DTS          12184 non-null   float64
 19  DCAL         56200 non-null   float64
 20  DRHO         105539 non-null  float64
 21  LITHOFACIES  133198 non-null  int64  
dtypes: float64(18), int64(1), object(3)
memory usage: 22.4+ MB
Вывод .info() уже дает нам важную подсказку. Общее число записей (RangeIndex) — 133,198. Но Non-Null Count у многих столбцов (X_LOC, FORMATION, CALI и т.д.) заметно меньше. Это прямое указание на наличие пропусков.
2. Метод .isna().sum()
Самый прямой способ подсчитать пропуски:
df.isna().sum()
WELL                0
DEPTH_MD            0
X_LOC            7393
Y_LOC            7393
Z_LOC            7393
GROUP               0
FORMATION       21566
CALI              192
RSHA            71159
RMED             7642
RDEP             7393
RHOB            25145
GR                  0
NPHI            41473
PEF             32358
DTC               563
SP              39518
ROP              2744
DTS            121014
DCAL            76998
DRHO            27659
LITHOFACIES         0
dtype: int64
Метод isna() возвращает логический массив с True (если значение является пропуском) и False - если нет. А sum() суммирует элементы в этом массиве (где True будут единицами, а False — нулями), так мы и получим количество пропусков. Теперь у нас есть точные цифры. Мы видим, что в некоторых столбцах (X_LOC, Y_LOC, Z_LOC, RDEP) одинаковое количество пропусков — 7393. Это уже наводит на мысль о неслучайности. Столбец DTS пропущен более чем в ста тысячх строк — он почти пустой. DCAL, RSHA тоже выглядят плохо.
В чем же проблема?
Мы знаем что и сколько. Но мы не видим картину целиком.
X_LOC, пропущено ли там и Y_LOC? (Судя по одинаковому числу — да, но это лишь гипотеза).RHOB с пропусками в CALI?DTS свойством определенных скважин или они случайны?Ответы на эти и другие вопросы определяют стратегию работы с данными. Простое удаление строк (dropna()) может уничтожить ценную информацию, а наивная замена средним — исказить распределение и связи между признаками.
Нам нужен инструмент, который превратит эту сухую таблицу чисел в наглядную карту. И этот инструмент — missingno.
missingno — это не просто библиотека для красивых графиков. Это философия визуального аудита данных. Она предоставляет четыре мощных инструмента, каждый из которых отвечает на свой класс вопросов о структуре пропусков.
Установка стандартная:
pip install missingno
А теперь давайте проведем настоящее расследование, используя наш датасет.
msno.bar(): Быстрая оценка полнотыСтолбчатая диаграмма — это аналог isna().sum(), но в графическом виде. Она показывает, какая доля данных присутствует в каждом столбце.
import missingno as msno
import matplotlib.pyplot as plt
msno.bar(df)
plt.show() 
Как читать этот график:
[!TIP] Когда использовать
msno.bar()? Это ваш первый шаг. Используйте его сразу после загрузки данных, чтобы получить "общую температуру по больнице" и мгновенно выявить самые проблемные столбцы, которые потребуют пристального внимания.
Этот график полезен, но он все еще не показывает расположение пропусков. Чтобы заглянуть внутрь структуры датафрейма, нам нужен следующий инструмент.
msno.matrix(): рентген вашего датафреймаМатричный график — это, пожалуй, самый информативный инструмент в арсенале missingno. Он позволяет буквально заглянуть внутрь датафрейма и увидеть точное расположение каждого пропущенного значения.
msno.matrix(df)
plt.show()
Как читать этот график:
NaN).Какие выводы можно сделать:
X_LOC, Y_LOC, Z_LOC и RDEP. Белые горизонтальные черточки в них появляются и исчезают синхронно. Это визуальное подтверждение нашей гипотезы, сделанной на основе isna().sum(): если пропущено одно из этих значений, скорее всего, пропущены и остальные.[!INFO] Почему матрица так важна? Матричный график переводит наш анализ с уровня "сколько" на уровень "где и как". Он незаменим для временных рядов и любых упорядоченных данных (например, геологических разрезов по глубине), так как позволяет увидеть, являются ли пропуски изолированными событиями или системными сбоями на протяжении определенных периодов/участков.
msno.heatmap(): Поиск скрытых связейМы уже подозреваем, что пропуски в некоторых столбцах связаны. Тепловая карта создана, чтобы измерить эту связь количественно. Она вычисляет корреляцию отсутствия данных (nullity correlation) между столбцами.
msno.heatmap(df)
plt.show()
Как читать этот график:
<1: Если вы видите ячейку со значением, которое не равно 1, но меньше него (например, 0.8), это означает, что не все, но многие пропуски совпадают.Какие выводы можно сделать:
RDEP, RMED, X_LOC, Y_LOC, Z_LOC. Корреляция между ними очень высока. Это означает, что данные в этих столбцах почти всегда пропадают вместе. Вероятно, они собирались в рамках одного процесса или одним набором инструментов.DTS и RSHA. Они не показывают сильной корреляции с другими кластерами. Их пропуски — это отдельная история. При этом между собой они тоже не коррелируют, что интересно.FORMATION и RHOB, например, скорее всего, никак не связаны.msno.dendrogram(): Карта родства пропусковДендрограмма предлагает еще один взгляд на корреляцию отсутствия данных, группируя столбцы с похожими паттернами пропусков с помощью иерархической кластеризации. Это как генеалогическое древо для ваших столбцов, где близкие родственники — это столбцы, чьи пропуски ведут себя похоже.
msno.dendrogram(df)
plt.show()
Как читать этот график:
Какие выводы можно сделать:
LITHOFACIES, GR, GROUP, WELL, DEPTH_MD), объединенную на уровне 0. Это наши полностью заполненные столбцы. Их паттерн пропусков идентичен — пропусков нет.X_LOC, Y_LOC, Z_LOC и RDEP на очень низком уровне, подтверждая их тесную связь. RMED присоединяется к этой группе чуть выше, что говорит о сильной, но не идеальной связи — именно то, что мы видели на тепловой карте.DTS, RSHA и CALI присоединяются к общему дереву очень высоко. Это значит, что их паттерны пропусков уникальны и не похожи ни друг на друга, ни на другие столбцы. Они — "дальние родственники" для остального набора данных.Ваша поддержка — это энергия для новых статей и проектов. Спасибо, что читаете!
Мы прошли путь от простого подсчета NaN до глубокого структурного анализа. Библиотека missingno не заполняет пропуски за вас. Она делает нечто более важное — дает вам исчерпывающую информацию для принятия осознанного решения.
На основе нашего анализа можно наметить конкретный план действий:
DTS: Пропущено более 85% данных. Пытаться восстановить его — крайне рискованная затея, которая может привнести больше шума, чем сигнала. Наиболее разумная стратегия — удалить этот столбец.RSHA: Пропущено почти 50% данных. Ситуация пограничная. Можно рассмотреть его удаление или попытаться восстановить значения, но только для тех моделей, которые устойчивы к подобным манипуляциям.X_LOC, Y_LOC, Z_LOC, RDEP, RMED: Эти столбцы теряют данные синхронно. Это значит, что если мы решим удалить строки с пропусками (dropna), мы должны делать это с учетом всего кластера. Удаление строк, где пропущен X_LOC, почти наверняка приведет к удалению тех же строк, где пропущены Y_LOC и Z_LOC.FORMATION, CALI, RHOB): Их пропуски не сильно коррелируют друг с другом. Здесь можно применять более тонкие методы импутации (заполнения): для каждого столбца подбирать свою стратегию (например, заполнение модой для категориального FORMATION и медианой/средним или даже модельным предсказанием для числовых CALI и RHOB).Простой вызов df.isna().sum() никогда не дал бы нам такой глубины понимания. Визуальный анализ с missingno превращает проблему пропусков из технической неприятности в интересный исследовательский квест, по итогам которого вы знаете о своих данных гораздо больше. А хорошее знание данных — это и есть ключ к построению качественных моделей.