Обработка пропусков: isnull(), dropna(), fillna()

Обработка пропусков: isnull(), dropna(), fillna() Pandas

Привет! В реальных данных почти никогда не бывает идеально. Всегда найдутся пропуски: кто-то не заполнил анкету, датчик не сработал, данные потерялись при передаче. Что делать с этими пропусками? Игнорировать? Удалять? Заполнять?

В этой статье мы разберём:

  • Как находить пропуски с помощью isnull()
  • Как удалять строки и колонки с пропусками через dropna()
  • Как заполнять пропуски через fillna()
  • Стратегии заполнения: среднее, медиана, предыдущее значение
Содержание
  1. Что нужно знать перед началом
  2. Основная часть
  3. Подготовка данных
  4. Поиск пропусков: isnull()
  5. Сумма пропусков по колонкам
  6. Процент пропусков
  7. notnull() — поиск заполненных значений
  8. Удаление пропусков: dropna()
  9. Удаление строк с любым пропуском
  10. Удаление строк, где все значения пропущены
  11. Удаление колонок с пропусками
  12. Удаление строк с пропусками в конкретных колонках
  13. Удаление строк с минимальным количеством пропусков
  14. Заполнение пропусков: fillna()
  15. Заполнение константой
  16. Заполнение пропусков в конкретных колонках
  17. Заполнение средним значением
  18. Заполнение медианой
  19. Заполнение предыдущим значением (ffill)
  20. Заполнение следующим значением (bfill)
  21. inplace — изменение исходного DataFrame
  22. Комбинированный подход
  23. Проверка после обработки
  24. Задачи для закрепления
  25. Нюансы и подводные камни
  26. Бесконечные значения (inf)
  27. Разные типы NaN
  28. inplace=True и цепочки операций
  29. Частые ошибки и как их избежать
  30. Ошибка 1: Забыл проверить пропуски после обработки
  31. Ошибка 2: Неправильный выбор стратегии
  32. Ошибка 3: Удаление слишком многих данных
  33. Шпаргалка
  34. Заключение
  35. КВИЗ
  36. Что дальше?

Что нужно знать перед началом

Для этого урока тебе понадобится:

  • Установленный Pandas (pip install pandas)
  • Базовое понимание DataFrame
  • Желание научиться работать с «грязными» данными

Совет: Обработка пропусков — это не просто технический шаг. Это критически важный этап, который влияет на качество всей модели.

Основная часть

Подготовка данных

Создадим DataFrame с пропусками для практики:

import pandas as pd
import numpy as np

np.random.seed(42)

# Создаём данные с пропусками
data = {
    'Имя': ['Анна', 'Иван', 'Мария', 'Пётр', 'Ольга', 'Сергей', None, 'Дмитрий'],
    'Возраст': [25, 30, np.nan, 35, 28, 40, 33, np.nan],
    'Город': ['Москва', 'СПб', 'Казань', None, 'Екатеринбург', 'Москва', 'СПб', 'Казань'],
    'Зарплата': [50000, 60000, 45000, 70000, np.nan, 80000, 62000, 48000],
    'Оценка': [4.5, 3.8, 4.2, 4.0, 4.8, 3.9, np.nan, 4.1]
}

df = pd.DataFrame(data)
print("Исходные данные:")
print(df)

Результат:

      Имя  Возраст         Город   Зарплата  Оценка
0    Анна     25.0        Москва    50000.0     4.5
1    Иван     30.0           СПб    60000.0     3.8
2   Мария      NaN        Казань    45000.0     4.2
3    Пётр     35.0          None    70000.0     4.0
4   Ольга     28.0  Екатеринбург        NaN     4.8
5  Сергей     40.0        Москва    80000.0     3.9
6    None     33.0           СПб    62000.0     NaN
7  Дмитрий      NaN        Казань    48000.0     4.1

Совет: В Pandas пропуски обозначаются как NaN (Not a Number) для чисел и None для объектов. Оба типа обрабатываются одинаково.

Поиск пропусков: isnull()

isnull() возвращает булеву маску (True — пропуск, False — не пропуск):

# Проверяем пропуски во всём DataFrame
print(df.isnull())

Результат:

    Имя  Возраст  Город  Зарплата  Оценка
0  False    False  False     False   False
1  False    False  False     False   False
2  False     True  False     False   False
3  False    False   True     False   False
4  False    False  False      True   False
5  False    False  False     False   False
6   True    False  False     False    True
7  False     True  False     False   False

Сумма пропусков по колонкам

# Количество пропусков в каждой колонке
print(df.isnull().sum())

Результат:

Имя         1
Возраст     2
Город       1
Зарплата    1
Оценка      1
dtype: int64

Процент пропусков

# Процент пропусков в каждой колонке
print(df.isnull().sum() / len(df) * 100)

notnull() — поиск заполненных значений

# Проверяем, где нет пропусков
print(df.notnull().sum())

Совет: isnull().sum() — твой первый шаг при анализе данных. Он показывает, сколько данных потеряно и в каких колонках.

Удаление пропусков: dropna()

dropna() удаляет строки (по умолчанию) или колонки с пропусками.

Удаление строк с любым пропуском

# Удаляем все строки, где есть хотя бы один пропуск
df_clean = df.dropna()
print(df_clean)

Результат:

    Имя  Возраст   Город  Зарплата  Оценка
0  Анна     25.0  Москва   50000.0     4.5
1  Иван     30.0     СПб   60000.0     3.8
5 Сергей     40.0  Москва   80000.0     3.9

Удаление строк, где все значения пропущены

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

Удаление колонок с пропусками

# Удаляем колонки, где есть хотя бы один пропуск
df_clean = df.dropna(axis=1)
print(df_clean.columns)

Удаление строк с пропусками в конкретных колонках

# Удаляем строки, где пропуск в колонке 'Возраст' или 'Зарплата'
df_clean = df.dropna(subset=['Возраст', 'Зарплата'])
print(df_clean)

Результат:

    Имя  Возраст         Город   Зарплата  Оценка
0  Анна     25.0        Москва    50000.0     4.5
1  Иван     30.0           СПб    60000.0     3.8
3  Пётр     35.0          None    70000.0     4.0
5 Сергей     40.0        Москва    80000.0     3.9
6  None     33.0           СПб    62000.0     NaN

Удаление строк с минимальным количеством пропусков

# Удаляем строки, где больше 2 пропусков
df_clean = df.dropna(thresh=len(df.columns) - 2)
print(df_clean)

Совет: thresh означает минимальное количество непропущенных значений. Например, thresh=3 — оставляет строки, где есть хотя бы 3 непустых значения.

Заполнение пропусков: fillna()

fillna() заменяет пропуски на указанные значения.

Заполнение константой

# Заполняем все пропуски значением 0
df_filled = df.fillna(0)
print(df_filled)

Результат:

      Имя  Возраст         Город   Зарплата  Оценка
0    Анна     25.0        Москва    50000.0     4.5
1    Иван     30.0           СПб    60000.0     3.8
2   Мария      0.0        Казань    45000.0     4.2
3    Пётр     35.0             0    70000.0     4.0
4   Ольга     28.0  Екатеринбург        0.0     4.8
5  Сергей     40.0        Москва    80000.0     3.9
6       0     33.0           СПб    62000.0     0.0
7  Дмитрий      0.0        Казань    48000.0     4.1

Заполнение пропусков в конкретных колонках

# Заполняем пропуски в разных колонках разными значениями
df_filled = df.fillna({
    'Возраст': 0,
    'Город': 'Неизвестно',
    'Зарплата': 0,
    'Оценка': 0
})
print(df_filled)

Заполнение средним значением

# Заполняем возраст средним значением
mean_age = df['Возраст'].mean()
df['Возраст'] = df['Возраст'].fillna(mean_age)
print(df[['Имя', 'Возраст']])

Результат:

      Имя     Возраст
0    Анна   25.000000
1    Иван   30.000000
2   Мария   31.833333  # ← среднее
3    Пётр   35.000000
4   Ольга   28.000000
5  Сергей   40.000000
6    None   33.000000
7  Дмитрий   31.833333  # ← среднее

Заполнение медианой

# Медиана менее чувствительна к выбросам, чем среднее
median_age = df['Возраст'].median()
df['Возраст'] = df['Возраст'].fillna(median_age)

Заполнение предыдущим значением (ffill)

# Заполняем пропуски предыдущим значением (forward fill)
df_sorted = df.sort_values('Возраст')
df_filled = df_sorted.fillna(method='ffill')
print(df_filled)

Результат:

      Имя  Возраст         Город   Зарплата  Оценка
0    Анна     25.0        Москва    50000.0     4.5
2   Мария     25.0        Казань    45000.0     4.2  # ← взято от Анны
...

Заполнение следующим значением (bfill)

# Заполняем пропуски следующим значением (backward fill)
df_filled = df.fillna(method='bfill')
print(df_filled)

Совет: ffill и bfill особенно полезны для временных рядов, где данные имеют логическую последовательность.

inplace — изменение исходного DataFrame

По умолчанию dropna() и fillna() возвращают новый DataFrame. Чтобы изменить исходный, используй inplace=True:

# Изменяем исходный DataFrame
df.fillna(0, inplace=True)
print(df)

Совет: inplace=True удобен для цепочек операций, но будь осторожен — он изменяет данные без возможности восстановления.

Комбинированный подход

import pandas as pd
import numpy as np

np.random.seed(42)

# Данные с пропусками
data = {
    'Товар': ['Ноутбук', 'Телефон', 'Планшет', 'Ноутбук', 'Телефон', 'Планшет'],
    'Продажи': [5, np.nan, 8, 3, 7, np.nan],
    'Цена': [50000, 30000, np.nan, 50000, np.nan, 15000],
    'Город': ['Москва', 'Москва', 'Москва', 'СПб', 'СПб', 'СПб']
}
df = pd.DataFrame(data)

print("=== Исходные данные ===")
print(df)
print("\n=== Пропуски ===")
print(df.isnull().sum())

# 1. Удаляем строки с пропусками в критичных колонках
df_clean = df.dropna(subset=['Товар', 'Цена'])

# 2. Заполняем оставшиеся пропуски
df_clean['Продажи'] = df_clean['Продажи'].fillna(df_clean['Продажи'].median())
df_clean['Цена'] = df_clean['Цена'].fillna(df_clean['Цена'].mean())

print("\n=== После обработки ===")
print(df_clean)

Результат:

=== Исходные данные ===
     Товар  Продажи    Цена  Город
0  Ноутбук      5.0  50000.0  Москва
1  Телефон      NaN  30000.0  Москва
2  Планшет      8.0      NaN  Москва
3  Ноутбук      3.0  50000.0    СПб
4  Телефон      7.0      NaN    СПб
5  Планшет      NaN  15000.0    СПб

=== Пропуски ===
Товар      0
Продажи    2
Цена       2
Город      0
dtype: int64

=== После обработки ===
     Товар  Продажи    Цена  Город
0  Ноутбук      5.0  50000.0  Москва
1  Телефон      5.5  30000.0  Москва
2  Планшет      8.0  36250.0  Москва
3  Ноутбук      3.0  50000.0    СПб
4  Телефон      7.0  36250.0    СПб
5  Планшет      5.5  15000.0    СПб

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

# Проверяем, что пропусков больше нет
print("\n=== Оставшиеся пропуски ===")
print(df_clean.isnull().sum())

# Проверяем тип данных (не должно быть None)
print(df_clean.dtypes)

Задачи для закрепления

Задача 1. Найди количество пропусков в каждой колонке DataFrame.

Задача 2. Удали все строки с любыми пропусками.

Задача 3. Заполни пропуски в колонке Возраст средним значением.

Задача 4. Удали строки, где есть пропуск в колонке Зарплата.

Задача 5. Заполни пропуски в колонке Город значением 'Неизвестно'.

Ответы:

Задача 1.

df.isnull().sum()

Задача 2.

df_clean = df.dropna()

Задача 3.

mean_age = df['Возраст'].mean()
df['Возраст'] = df['Возраст'].fillna(mean_age)

Задача 4.

df_clean = df.dropna(subset=['Зарплата'])

Задача 5.

df['Город'] = df['Город'].fillna('Неизвестно')

Нюансы и подводные камни

Бесконечные значения (inf)

isnull() не обнаруживает бесконечные значения:

# Бесконечные значения не считаются NaN
df = pd.DataFrame({'x': [1, np.inf, -np.inf]})
print(df.isnull())  # False, False, False

# Используй np.isinf() для обнаружения
print(np.isinf(df))

Разные типы NaN

В Pandas пропуски могут быть NaN или None. Оба обрабатываются одинаково.

inplace=True и цепочки операций

При использовании inplace=True будь осторожен — это изменяет данные и может сломать цепочки операций.

Частые ошибки и как их избежать

Ошибка 1: Забыл проверить пропуски после обработки

# Всегда проверяй результат
df_clean = df.fillna(0)
print(df_clean.isnull().sum())  # Должно быть 0

Ошибка 2: Неправильный выбор стратегии

# Заполнять пропуски средним, если данные временные
df['Цена'].fillna(df['Цена'].mean())

# Для временных рядов лучше использовать ffill
df['Цена'].fillna(method='ffill')

Ошибка 3: Удаление слишком многих данных

# Удаляем 80% данных из-за одного пропуска
df.dropna()

# Заполни пропуски вместо удаления
df.fillna(df.median())

Шпаргалка

Что нужноКак пишется
Найти пропускиdf.isnull()
Сумма пропусков по колонкамdf.isnull().sum()
Удалить строки с пропускамиdf.dropna()
Удалить колонки с пропускамиdf.dropna(axis=1)
Удалить строки с пропусками в конкретных колонкахdf.dropna(subset=['Колонка'])
Заполнить пропускиdf.fillna(значение)
Заполнить разные колонки по-разномуdf.fillna({'Кол1': 0, 'Кол2': 'текст'})
Заполнить среднимdf['Кол'].fillna(df['Кол'].mean())
Заполнить медианойdf['Кол'].fillna(df['Кол'].median())
Заполнить предыдущим значениемdf.fillna(method='ffill')
Заполнить следующим значениемdf.fillna(method='bfill')

Заключение

Сегодня мы научились:

  • Находить пропуски с помощью isnull()
  • Удалять строки и колонки с пропусками через dropna()
  • Заполнять пропуски через fillna() (константами, средним, медианой)
  • Использовать ffill и bfill для временных рядов
  • Проверять результат после обработки

КВИЗ

Что дальше?

Оцените статью
IMI-DS - PYTHON LERNEN
Содержание
Оглавление ×