Привет! В реальных данных почти никогда не бывает идеально. Всегда найдутся пропуски: кто-то не заполнил анкету, датчик не сработал, данные потерялись при передаче. Что делать с этими пропусками? Игнорировать? Удалять? Заполнять?
В этой статье мы разберём:
- Как находить пропуски с помощью
isnull() - Как удалять строки и колонки с пропусками через
dropna() - Как заполнять пропуски через
fillna() - Стратегии заполнения: среднее, медиана, предыдущее значение
- Что нужно знать перед началом
- Основная часть
- Подготовка данных
- Поиск пропусков: isnull()
- Сумма пропусков по колонкам
- Процент пропусков
- notnull() — поиск заполненных значений
- Удаление пропусков: dropna()
- Удаление строк с любым пропуском
- Удаление строк, где все значения пропущены
- Удаление колонок с пропусками
- Удаление строк с пропусками в конкретных колонках
- Удаление строк с минимальным количеством пропусков
- Заполнение пропусков: fillna()
- Заполнение константой
- Заполнение пропусков в конкретных колонках
- Заполнение средним значением
- Заполнение медианой
- Заполнение предыдущим значением (ffill)
- Заполнение следующим значением (bfill)
- inplace — изменение исходного DataFrame
- Комбинированный подход
- Проверка после обработки
- Задачи для закрепления
- Нюансы и подводные камни
- Бесконечные значения (inf)
- Разные типы NaN
- inplace=True и цепочки операций
- Частые ошибки и как их избежать
- Ошибка 1: Забыл проверить пропуски после обработки
- Ошибка 2: Неправильный выбор стратегии
- Ошибка 3: Удаление слишком многих данных
- Шпаргалка
- Заключение
- КВИЗ
- Что дальше?
Что нужно знать перед началом
Для этого урока тебе понадобится:
- Установленный 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для временных рядов - Проверять результат после обработки






![Выбор колонок и строк: [] vs loc vs iloc](https://imi-ds.ru/wp-content/uploads/2026/08/vybor-kolonok-i-strok-vs-loc-vs-iloc-335x220.webp)

