Как очистить DataFrame в Pandas: удаление строк с NaN в столбцах
Для кого эта статья:
- Аналитики данных и специалисты по обработке данных
- Студенты и начинающие практики в области анализа данных и программирования
Люди, интересующиеся обучением Python и использованием библиотеки Pandas для работы с данными
Работа с реальными данными — это почти всегда борьба с "грязью". Пропущенные значения, нулевые ячейки, странные символы... И в какой-то момент каждый аналитик оказывается перед дилеммой: как поступить с NaN в ключевых столбцах? Удалять всё подряд — слишком радикально. Оставлять — рискованно для дальнейшего анализа. В библиотеке Pandas есть элегантное решение: точечное удаление строк с пропусками только в тех столбцах, которые критичны для вашего анализа. 📊 Это как хирургическая операция на данных — вместо ампутации конечности вы удаляете только проблемный участок. Давайте разберёмся, как это делать правильно.
Хотите избежать ошибок при работе с пропущенными значениями и научиться эффективно использовать библиотеку Pandas? На курсе Обучение Python-разработке от Skypro вы не только изучите очистку данных, но и погрузитесь в практику работы с реальными датасетами. Наши студенты формируют прочный навык решения типичных проблем с данными и создают готовое портфолио проектов уже во время обучения.
Проблема NaN в данных и методы удаления строк в Pandas
Пропущенные значения в данных — это не просто эстетический недостаток. Они могут радикально исказить результаты анализа, привести к ошибкам в расчётах и некорректным выводам. В Pandas эти "призраки" представлены специальным значением NaN (Not a Number), которое появляется в наших DataFrame по множеству причин: от проблем при сборе данных до ошибок импорта или объединения датасетов.
Когда мы сталкиваемся с NaN, у нас есть три стратегических варианта:
- Удалить строки или столбцы с пропусками
- Заполнить пропуски (средним, медианой, константой и т.д.)
- Игнорировать их, если ваш алгоритм анализа способен с ними работать
В этой статье мы сосредоточимся на первом подходе, который часто является наиболее предпочтительным для подготовки "чистых" данных к моделированию или визуализации.
Библиотека Pandas предоставляет мощный метод dropna(), позволяющий выполнять точечное удаление строк на основе заданных условий. Основное его преимущество — возможность указывать конкретные столбцы, в которых наличие NaN критично для вашего анализа.
Александр Петров, ведущий аналитик данных
Однажды я работал над прогнозной моделью для розничной сети. Датасет содержал сотни тысяч строк с информацией о продажах, и примерно 15% записей имели пропуски в различных столбцах. Полное удаление строк с хотя бы одним NaN лишило бы нас слишком большого объема данных. Однако для построения модели были абсолютно необходимы два столбца: "Цена" и "Объем продаж". Я использовал dropna с параметром subset именно для этих столбцов:
PythonСкопировать кодdf.dropna(subset=['price', 'sales_volume'], inplace=True)В результате мы сохранили около 92% исходных данных, удалив только те строки, где отсутствовали критически важные значения. Эта "хирургическая" операция позволила сохранить статистическую значимость выборки и при этом обеспечить качество входных данных для модели.
Для решения проблемы NaN в определенных столбцах библиотека Pandas предоставляет несколько основных методов:
| Метод | Описание | Когда использовать |
|---|---|---|
| df.dropna() | Удаляет строки или столбцы с NaN | Общее удаление строк с любыми NaN |
| df.dropna(subset=[...]) | Удаляет строки с NaN в указанных столбцах | Точечное удаление по конкретным столбцам |
| df[df['column'].notna()] | Фильтрация строк через булев индекс | Когда нужен точный контроль над фильтрацией |
| df.query('column.notna()') | Запрос для фильтрации строк | Для лаконичных проверок на NaN |
Каждый метод имеет свои преимущества и ограничения, которые мы подробнее рассмотрим в следующих разделах. Выбор подхода зависит от структуры ваших данных, требований к производительности и специфики вашего анализа. 🧐

Метод dropna() с параметром subset: синтаксис и применение
Метод dropna() — это мощный инструмент для очистки DataFrame от пропущенных значений, который становится особенно полезным благодаря параметру subset. Этот параметр позволяет точно указать, в каких столбцах NaN-значения действительно проблематичны и требуют удаления соответствующих строк.
Базовый синтаксис метода выглядит следующим образом:
DataFrame.dropna(axis=0, how='any', thresh=None, subset=None, inplace=False)
Давайте разберем ключевые параметры:
- axis — определяет, удалять ли строки (axis=0, значение по умолчанию) или столбцы (axis=1) с NaN
- how — задает условие удаления: 'any' (если есть хотя бы один NaN) или 'all' (если все значения — NaN)
- thresh — минимальное количество непустых значений, необходимое для сохранения строки/столбца
- subset — список столбцов, которые следует проверять на наличие NaN
- inplace — если True, изменения применяются к исходному DataFrame; если False, возвращается новый DataFrame
Параметр subset — настоящая находка для аналитиков. Он позволяет сосредоточиться только на столбцах, которые действительно важны для анализа. Например, если у вас есть DataFrame с данными о клиентах, возможно, вам не нужно удалять строки с отсутствующей информацией о номере телефона, но критично важно иметь данные о возрасте и доходе.
Пример использования метода dropna() с параметром subset:
# Удаление строк с NaN только в столбцах 'age' и 'income'
clean_df = df.dropna(subset=['age', 'income'])
# То же самое, но с изменением исходного DataFrame
df.dropna(subset=['age', 'income'], inplace=True)
Если требуется более сложная логика, можно комбинировать параметры. Например, удалить строки, где отсутствуют значения хотя бы в одном из указанных столбцов:
# Удаление строк, где все значения в указанных столбцах — NaN
df.dropna(subset=['age', 'income'], how='all', inplace=True)
Или оставить только те строки, где заполнено не менее определенного количества столбцов:
# Сохранить строки, где заполнено минимум 2 из 3 указанных столбцов
df.dropna(subset=['age', 'income', 'education'], thresh=2, inplace=True)
Для более наглядного понимания работы dropna() с параметром subset, рассмотрим конкретный пример с данными:
import pandas as pd
import numpy as np
# Создаем тестовый DataFrame
data = {
'name': ['Alice', 'Bob', 'Charlie', 'David', 'Emma'],
'age': [25, np.nan, 30, np.nan, 22],
'salary': [50000, 60000, np.nan, 75000, 45000],
'department': ['HR', 'IT', 'Marketing', np.nan, 'Finance']
}
df = pd.DataFrame(data)
print("Исходный DataFrame:")
print(df)
# Удаляем строки, где age содержит NaN
clean_df = df.dropna(subset=['age'])
print("\nПосле удаления строк с NaN в столбце 'age':")
print(clean_df)
# Удаляем строки, где есть NaN в столбцах age или salary
clean_df2 = df.dropna(subset=['age', 'salary'])
print("\nПосле удаления строк с NaN в столбцах 'age' или 'salary':")
print(clean_df2)
Результат выполнения этого кода будет демонстрировать, как метод dropna() с параметром subset фокусирует очистку данных только на указанных столбцах, сохраняя остальную информацию нетронутой. 🧹
Практические примеры удаления строк с NaN в заданном столбце
Теория без практики — как нож без лезвия. Давайте рассмотрим несколько реальных сценариев, где удаление строк с NaN в определенных столбцах решает конкретные аналитические задачи.
Марина Соколова, дата-инженер
В проекте по анализу клиентского оттока для телеком-компании мы столкнулись с типичной проблемой: большой датасет с многочисленными переменными, содержащий около 7% пропущенных значений в различных комбинациях. Особенно критичным для нас был столбец 'churn_date' (дата ухода клиента), поскольку от него зависело вычисление длительности пользования услугами.
Мы пробовали разные подходы: заполнение средними, медианами, даже применили несколько алгоритмов для предсказания пропущенных дат. Но в итоге любые "искусственные" даты вносили смещение в расчет ключевого показателя LTV (пожизненной ценности клиента).
Решение пришло неожиданно просто:
PythonСкопировать кодdf_clean = df.dropna(subset=['churn_date'])После такой фокусированной очистки мы потеряли всего 3.2% строк, но получили абсолютно надежные данные для анализа оттока. Интересно, что если бы мы удалили все строки с любыми пропусками, мы бы лишились почти 25% данных, что было неприемлемо.
Давайте рассмотрим несколько практических примеров использования dropna() с параметром subset в различных контекстах анализа данных.
Пример 1: Очистка финансовых данных
Представьте, что у вас есть DataFrame с финансовыми транзакциями, и для дальнейшего анализа критично иметь заполненный столбец с суммой транзакции:
import pandas as pd
import numpy as np
# Создаем DataFrame с финансовыми транзакциями
transactions = pd.DataFrame({
'transaction_id': range(1001, 1011),
'date': pd.date_range(start='2023-01-01', periods=10),
'customer_id': [101, 102, 103, np.nan, 105, 106, 107, 108, np.nan, 110],
'amount': [150\.75, np.nan, 75.20, 500.00, 1200.50, np.nan, 95.40, 45.00, 320.75, 60.25],
'category': ['food', 'electronics', np.nan, 'travel', 'housing', 'food', 'entertainment', np.nan, 'clothing', 'food']
})
# Для финансового анализа нам необходим столбец с суммой
clean_transactions = transactions.dropna(subset=['amount'])
print(f"Исходное количество транзакций: {len(transactions)}")
print(f"После очистки: {len(clean_transactions)}")
print(clean_transactions)
Пример 2: Подготовка данных для машинного обучения
Допустим, вы готовите данные для модели машинного обучения, которая прогнозирует стоимость недвижимости. Для корректной работы модели необходимы все признаки, кроме необязательного поля "описание":
# Создаем DataFrame с информацией о недвижимости
real_estate = pd.DataFrame({
'property_id': ['P001', 'P002', 'P003', 'P004', 'P005'],
'area_sqm': [85\.5, 120.0, np.nan, 65.0, 95.5],
'bedrooms': [2, 3, 2, np.nan, 3],
'location': ['downtown', 'suburb', 'downtown', 'suburb', np.nan],
'price': [250000, 320000, 180000, np.nan, 275000],
'description': ['Cozy apartment', np.nan, 'Modern house', 'Garden view', np.nan]
})
# Удаляем строки с NaN в столбцах, важных для модели, но сохраняем те,
# где просто отсутствует описание
features_for_model = ['area_sqm', 'bedrooms', 'location', 'price']
model_data = real_estate.dropna(subset=features_for_model)
print(model_data)
Пример 3: Анализ временных рядов
При работе с временными рядами часто критично иметь полные данные для определенных периодов или метрик:
# Создаем DataFrame с данными временного ряда
time_series = pd.DataFrame({
'date': pd.date_range(start='2023-01-01', periods=8),
'temperature': [5\.2, 4.8, np.nan, 6.1, 7.2, 8.5, np.nan, 7.9],
'humidity': [68, np.nan, 72, 65, np.nan, 70, 75, 69],
'wind_speed': [12, 15, 10, np.nan, 8, 14, 12, np.nan]
})
# Для анализа корреляции между температурой и влажностью
# нужны строки, где есть оба значения
correlation_data = time_series.dropna(subset=['temperature', 'humidity'])
print(correlation_data)
Пример 4: Комбинированные условия с использованием thresh
Иногда требуется более гибкая логика удаления, например, сохранить строки, где заполнено не менее определенного количества столбцов из заданного набора:
# Создаем DataFrame с данными о студентах
students = pd.DataFrame({
'student_id': [1, 2, 3, 4, 5],
'name': ['Alex', 'Beth', 'Charlie', 'Diana', 'Ethan'],
'math_score': [85, np.nan, 92, np.nan, 78],
'physics_score': [np.nan, 88, 90, 75, np.nan],
'chemistry_score': [90, 82, np.nan, np.nan, 85],
'biology_score': [np.nan, np.nan, 85, 80, 90]
})
# Мы хотим включить студентов, у которых есть оценки как минимум по двум предметам
subjects = ['math_score', 'physics_score', 'chemistry_score', 'biology_score']
students_with_enough_scores = students.dropna(subset=subjects, thresh=2)
print(students_with_enough_scores)
Эти примеры демонстрируют гибкость параметра subset в методе dropna() и его применение в различных сценариях анализа данных. Такой подход позволяет максимально сохранить полезную информацию, удаляя только те строки, где отсутствуют критически важные данные. 💡
Эффективная очистка DataFrame: inplace и другие параметры
Эффективная работа с данными — это не только правильные решения, но и оптимальные исполнения этих решений. При использовании метода dropna() с параметром subset важно понимать нюансы, которые могут существенно повлиять на производительность и удобство вашего кода.
Одним из ключевых параметров, влияющих на эффективность работы с DataFrame, является inplace. Он определяет, будет ли операция удаления применена непосредственно к исходному DataFrame или будет создана его копия с внесенными изменениями.
| Параметр | Значение | Преимущества | Недостатки |
|---|---|---|---|
| inplace=False | Создает новый DataFrame (по умолчанию) | Сохраняет оригинальные данные, безопасен | Требует дополнительной памяти, требует явного присвоения |
| inplace=True | Изменяет исходный DataFrame | Экономит память, более краткий код | Исходные данные теряются, необратимо |
Рассмотрим примеры использования параметра inplace:
# Без inplace (создание нового DataFrame)
df_clean = df.dropna(subset=['important_column'])
# Теперь у нас два DataFrame: исходный df и очищенный df_clean
# С inplace=True (изменение исходного DataFrame)
df.dropna(subset=['important_column'], inplace=True)
# Исходный df теперь изменен, пропущенные строки удалены
Когда выбирать какой подход?
- Используйте
inplace=True, когда вам не нужен исходный DataFrame после очистки, что экономит память. - Выбирайте
inplace=False(значение по умолчанию), если вам может понадобиться исходный DataFrame или вы хотите создать несколько версий очищенных данных с разными параметрами.
Помимо inplace, существуют и другие параметры, влияющие на эффективность очистки данных:
Параметр how
Когда вы указываете несколько столбцов в subset, параметр how определяет логику удаления:
# Удалить строки, где ЛЮБОЙ из указанных столбцов содержит NaN
df.dropna(subset=['col1', 'col2'], how='any', inplace=True)
# Удалить строки, где ВСЕ указанные столбцы содержат NaN
df.dropna(subset=['col1', 'col2'], how='all', inplace=True)
Параметр thresh
Этот параметр позволяет указать минимальное количество непустых значений для сохранения строки:
# Сохранить строки, где минимум 2 из указанных столбцов имеют непустые значения
df.dropna(subset=['col1', 'col2', 'col3'], thresh=2, inplace=True)
Производительность и объем данных
При работе с большими объемами данных важно учитывать влияние операций на производительность:
- Для очень больших DataFrame используйте фильтрацию с логическими индексами вместо
dropna(), что может быть эффективнее:
# Вместо
df.dropna(subset=['important_column'], inplace=True)
# Используйте
df = df[df['important_column'].notna()]
- Предварительно фильтруйте ваш DataFrame, чтобы работать только с необходимыми столбцами:
# Выбираем только нужные столбцы перед очисткой
df_subset = df[['id', 'important_column1', 'important_column2']]
df_clean = df_subset.dropna(subset=['important_column1'])
Цепочки методов для элегантного кода
Для более читаемого и лаконичного кода используйте цепочки методов вместо параметра inplace:
# Вместо использования inplace=True
df_clean = (df
.dropna(subset=['column1'])
.reset_index(drop=True)
.assign(new_column=lambda x: x['column1'] * 2)
)
Такой подход делает код более понятным и позволяет легко добавлять дополнительные преобразования в цепочку.
Совет по эффективности: если вам нужно удалить строки с NaN в нескольких наборах столбцов по разным критериям, лучше делать это последовательными операциями, а не одной сложной:
# Более понятно и гибко:
df_clean = df.copy()
df_clean = df_clean.dropna(subset=['col1', 'col2'], how='all')
df_clean = df_clean.dropna(subset=['col3'], how='any')
# Вместо сложной комбинации условий
Правильное использование inplace и других параметров поможет вам не только эффективнее очищать данные, но и писать более понятный и оптимизированный код. 🚀
Альтернативные способы обработки отсутствующих значений
Удаление строк с пропущенными значениями — не единственный и не всегда оптимальный способ обработки NaN в DataFrame. В зависимости от характера данных, объема выборки и целей анализа стоит рассмотреть и другие подходы. Расширим наш инструментарий альтернативными методами, которые в ряде случаев могут оказаться более эффективными.
1. Заполнение пропущенных значений (импутация)
Вместо удаления строк можно заполнить пропуски различными способами:
# Заполнение константой
df['column'].fillna(0, inplace=True)
# Заполнение статистическими значениями
df['column'].fillna(df['column'].mean(), inplace=True) # среднее
df['column'].fillna(df['column'].median(), inplace=True) # медиана
df['column'].fillna(df['column'].mode()[0], inplace=True) # мода
# Заполнение методом прямого и обратного заполнения
df['column'].fillna(method='ffill', inplace=True) # использует предыдущее значение
df['column'].fillna(method='bfill', inplace=True) # использует следующее значение
2. Использование логических индексов вместо dropna()
Для точного контроля над фильтрацией строк можно использовать логические индексы:
# Вместо df.dropna(subset=['column'])
df_clean = df[df['column'].notna()]
# Комбинирование нескольких условий
mask = df['column1'].notna() & (df['column2'] > 0)
df_clean = df[mask]
3. Метод query() для лаконичной фильтрации
Для более читаемой фильтрации можно использовать метод query():
# Вместо df[df['column'].notna()]
df_clean = df.query('column.notna()')
# Комбинирование условий
df_clean = df.query('column1.notna() and column2 > 0')
4. Интерполяция для числовых данных
Для временных рядов или последовательных данных интерполяция может дать более точные оценки пропущенных значений:
# Линейная интерполяция
df['column'].interpolate(method='linear', inplace=True)
# Другие методы интерполяции
df['column'].interpolate(method='polynomial', order=2, inplace=True) # полиномиальная
df['column'].interpolate(method='spline', order=2, inplace=True) # сплайн
5. Использование специализированных алгоритмов импутации
Для сложных случаев можно использовать более продвинутые алгоритмы из scikit-learn:
from sklearn.impute import SimpleImputer, KNNImputer
# Простая импутация с использованием стратегии
imputer = SimpleImputer(strategy='mean') # или 'median', 'most_frequent', 'constant'
df[['column1', 'column2']] = imputer.fit_transform(df[['column1', 'column2']])
# Импутация на основе k ближайших соседей
knn_imputer = KNNImputer(n_neighbors=5)
df[['column1', 'column2']] = knn_imputer.fit_transform(df[['column1', 'column2']])
6. Создание флагов для отсутствующих данных
Иногда сам факт отсутствия значения содержит полезную информацию:
# Создаем столбец-флаг для отслеживания пропущенных значений
df['column_is_missing'] = df['column'].isna().astype(int)
# Заполняем пропуски и сохраняем информацию о них
df['column'].fillna(df['column'].mean(), inplace=True)
7. Использование GroupBy для контекстного заполнения
Если данные имеют категориальную структуру, можно заполнять пропуски на основе групп:
# Заполнение средним значением по группам
df['column'] = df.groupby('category')['column'].transform(
lambda x: x.fillna(x.mean())
)
Чтобы правильно выбрать метод обработки пропущенных значений, учитывайте следующие факторы:
- Объем данных — если у вас большой датасет, потеря нескольких строк может быть несущественна
- Механизм пропусков — важно понять, случайны ли пропуски или систематичны
- Тип анализа — для разных видов анализа требуется разная степень чистоты данных
- Тип переменных — числовые, категориальные или временные ряды требуют разных подходов
Помните, что часто наилучший подход — это комбинация нескольких методов: удаление критически важных пропусков и заполнение менее значимых. 🔍
Правильное обращение с NaN-значениями в Pandas — это не просто техническое умение, а настоящее искусство, влияющее на качество всего вашего анализа данных. Используя метод dropna() с параметром subset, вы приобретаете хирургическую точность в очистке данных. Вместо грубой ампутации целых строк или столбцов вы выполняете точечные удаления только там, где это действительно необходимо. Эта стратегия сохраняет максимум полезной информации и минимизирует смещение в ваших данных. Когда в следующий раз вы столкнетесь с NaN в критических столбцах, не спешите удалять все подряд — используйте фокусированный подход, который сделает ваши выводы более надежными и обоснованными.