Как стать автором
Обновить
1195.29
OTUS
Цифровые навыки от ведущих экспертов

Методы очистки данных в Pandas

Уровень сложностиПростой
Время на прочтение5 мин
Количество просмотров19K

Привет, Хабр!

Аналитики данных часто сталкиваются с грязными данными, которые могут существенно замедлить процесс анализа. Грязны данные – это пропущенные значения, дубликаты, неконсистентные данные. Пропущенные значения заставляют нас гадать, что же было замыслено нашим коллегой; дубликаты вводят в заблуждение, умножая одно и то же на количество их копий, а неконсистентные данные заставляют нас сомневаться в каждой цифре.

Очищать грязные данные можно c Pandas. Рассмотрим основные методы.

Пропущенные значения

Пропущенные значения могут возникать по разным причинам. Вне зависимости от причины, сначала их нужно обнаружить.

isnull() в Pandas возвращает DataFrame или Series с булевыми значениями, где True указывает на пропущенное значение. Аналогично, notnull() возвращает True для элементов, где значение присутствует:

# удаляем строки, где есть хотя бы одно пропущенное значение
cleaned_df = df.dropna()

# заполняем все пропущенные значения нулями
filled_df = df.fillna(0)

print("Очищенный DataFrame:\n", cleaned_df)
prit("DataFrame с заполненными пропусками:\n", filled_df)

Код выведет таблицу, где True обозначает пропущенные значения.

- Что с ними делать дальше? Один из подходов – удаление строк или столбцов с пропусками, но это может привести к потере какой-то информации. Другой подход – заполнение пропусков.

Для заполнения пропусков используем метод fillna():

# заполняем пропуски средним значением по столбцам
df.fillna(df.mean(), inplace=True)

print("df с заполненными пропусками средним:\n", df)

Для категориальных данных может быть разумно заполнить пропуски наиболее часто встречающимся значением:

# заполнение пропусков в зависимости от условий
df['A'].fillna(value=df['A'].mean(), inplace=True)
df['B'].fillna(value=df['B'].median(), inplace=True)

# кастомная функция для заполнения
def custom_fill(series):
    return series.fillna(series.mean())

df.apply(custom_fill)

Удаление с помощью dropna()

По умолчанию dropna() удаляет строки, которые содержат хотя бы одно пропущенное значение:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'Name': ['ivan', 'artem', np.nan, 'diana', 'eva'],
    'Age': [25, np.nan, np.nan, 27, 28],
    'City': ['New York', 'Los Angeles', 'Chicago', np.nan, 'Miami']
})

# удаляем строки с пропущенными значениями
df_cleaned = df.dropna()
print(df_cleaned)

dropna() удаляет все строки, где есть хотя бы одно NaN.

Чтобы удалить столбцы, содержащие пропущенные значения можно юзать параметр axis=1:

df_cleaned_columns = df.dropna(axis=1)
print(df_cleaned_columns)

Можно удалить строки, где все значения пропущены с помощью параметра how='all':

# добавляем строку, полностью состоящую из NaN
df.loc[5] = [np.nan, np.nan, np.nan]

# удаляем строки, где все значения являются NaN
df_cleaned_all = df.dropna(how='all')
print(df_cleaned_all)

Можно указать столбцы, для которых должна быть применена проверка на NaN, используя параметр subset.

# удаляем строки, где пропущены значения в определенных столбцах
df_cleaned_subset = df.dropna(subset=['Name', 'City'])
print(df_cleaned_subset)

В этом случае будут удалены только те строки, в которых отсутствуют значения в столбцах Name или City

thresh позволяет указать минимальное количество непропущенных значений, при котором строка или столбец сохраняется:

# удаляем строки, где менее двух непропущенных значений
df_cleaned_thresh = df.dropna(thresh=2)
print(df_cleaned_thresh)

Кастомные варианты

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

Ср. значение:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'A': [1, 2, np.nan, 4, 5],
    'B': [np.nan, 2, 3, np.nan, 5],
    'C': [1, 2, 3, 4, np.nan]
})

# заполняем пропущенные значения в столбце A средним значением по этому столбцу
df['A'].fillna(df['A'].mean(), inplace=True)

Медиана

Медиана может быть предпочтительнее среднего значения в случаях, когда данные содержат выбросы, которые могут исказить среднее:

# фулим пропущенные значения в столбце B медианой
df['B'].fillna(df['B'].median(), inplace=True)

Мода

Для категориальных данных или данных с дискретными значениями можно использовать моду:

# C - категориальный столбец, заполняем пропущенные значения модой
df['C'].fillna(df['C'].mode()[0], inplace=True)

Можно применять произвольные функции для более сложных заполнений. Например, можно заполнить пропуски значениями, основанными на других столбцах данных:

# Заполняем пропущенные значения в столбце A, используя кастомную логику
df['A'].fillna(df.apply(lambda row: row['B'] if pd.isnull(row['A']) else row['A'], axis=1), inplace=True)

Когда заполнение должно учитывать группировку по какому-либо признаку, можно использовать apply() или transform() совместно с groupby():

# допустим, есть столбец группы, и мы хотим заполнить пропуски средним значением внутри каждой группы
df['group'] = ['X', 'X', 'Y', 'Y', 'Z']
df['A'] = df.groupby('group')['A'].transform(lambda x: x.fillna(x.mean()))

А что насчет дупликатов?

Дубликаты могут могут быть как полностью идентичными записями, так и частичными дубликатами, когда совпадают только некоторые поля. В любом случае, дубликаты вносят шум в данные, увеличивают их объем и могут привести к неверным аналитическим выводам.

drop_duplicates() позволяет не только удалять полные дубликаты строк, но и предоставляет настройки для работы с частичными дубликатами, удалим полные дупликаты:

import pandas as pd

df = pd.DataFrame({
    'A': [1, 2, 2, 3, 3, 3],
    'B': ['a', 'b', 'b', 'c', 'c', 'c'],
    'C': [100, 200, 200, 300, 300, 300]
})

# удаляем дубликаты
df_unique = df.drop_duplicates()

print(df_unique)

Код удалит все полные дубликаты строк, оставив уникальные комбинации значений по всем столбцам.

Часто нужно удалять дубликаты, основываясь не на всех столбцах, а только на некоторых. Для этого в drop_duplicates() есть параметр subset:

# удаляем дубликаты, основываясь только на столбцах 'A' и 'B'
df_unique_subset = df.drop_duplicates(subset=['A', 'B'])

print(df_unique_subset)

keep позволяет контролировать, какие дубликаты будут удалены: первый, последний или все:

# Удаляем все дубликаты, кроме последнего вхождения
df_keep_last = df.drop_duplicates(keep='last')

print(df_keep_last)

Можно использовать комбинацию методов Pandas для предварительной обработки данных перед удалением дубликатов. Например, можно привести все строки к нижнему регистру, удалить пробелы, преобразовать даты в единый формат и т.д:

# преобразуем все строки в нижний регистр и удаляем пробелы
df['B'] = df['B'].str.lower().str.replace(' ', '')

# теперь удаляем дубликаты
df_preprocessed = df.drop_duplicates()

print(df_preprocessed)

Категориальные данные

get_dummies() используется для преобразования категориальных переменных в фиктивные/индикаторные переменные, кстати в ml юзается довольно часто:

import pandas as pd

df = pd.DataFrame({
    'Color': ['Red', 'Green', 'Blue', 'Green']
})

# преобразуем категориальные данные в индикаторные переменные
dummies = pd.get_dummies(df['Color'])
print(dummies)

В результате каждое уникальное значение в столбце Color превращается в отдельный столбец с индикаторными переменными (0 или 1), указывающими на присутствие или отсутствие данной категории в каждой строке.

factorize() используется для получения числового представления категориальных данных, присваивая каждой уникальной категории целочисленный идентификатор:

# получаем числовое представление категориальных данных
codes, uniques = pd.factorize(df['Color'])

print(codes)  # массив кодов
print(uniques)  # массив уникальных значений

str-методы позволяют выполнять такие операции, как преобразование регистра, поиск и замена подстрок, разбиение строк и т.п:

df_text = pd.DataFrame({
    'Text': ['This is a test.', 'Hello, World!', 'Otus!']
})

# преобразуем все тексты в нижний регистр
df_text['Text_lower'] = df_text['Text'].str.lower()

# удаляем знаки пунктуации
df_text['Text_clean'] = df_text['Text'].str.replace('[^\w\s]', '', regex=True)

print(df_text)

Иногда можно использовать str.replace(), str.findall().

Преобразование текстовых данных в числовые часто требуется для аналитических и ml моделей. Например, после использования factorize() для категориальных данных можно обратно преобразовать числовые коды в текстовые категории, используя массив uniques:

# преобразование кодов обратно в текстовые категории
df['Color_restored'] = codes
df['Color_restored'] = df['Color_restored'].map(lambda x: uniques[x])

print(df)

Какие методы очистки знаете вы?

В преддверии старта курса Аналитик данных хочу пригласить вас на бесплатные вебинары про подготовку данных в Pandas, а также про SQL в реалиях начинающего дата-аналитика.

Теги:
Хабы:
Всего голосов 11: ↑9 и ↓2+9
Комментарии2

Публикации

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS