Привет, Хабр!
Аналитики данных часто сталкиваются с грязными данными, которые могут существенно замедлить процесс анализа. Грязны данные – это пропущенные значения, дубликаты, неконсистентные данные. Пропущенные значения заставляют нас гадать, что же было замыслено нашим коллегой; дубликаты вводят в заблуждение, умножая одно и то же на количество их копий, а неконсистентные данные заставляют нас сомневаться в каждой цифре.
Очищать грязные данные можно c Pandas. Рассмотрим основные методы.
Пропущенные значения
Пропущенные значения могут возникать по разным причинам. Вне зависимости от причины, сначала их нужно обнаружить.
isnull() в Pandas возвращает DataFrame или Series с булевыми значениями, где True указывает на пропущенное значение. Аналогично, notnull() возвращает True для элементов, где значение присутствует:
Для начала объединим два датафрейма в единый, если нам это необходимо:
df = pd.concat([df1, df2], axis=0) #d pd.set_option('display.max_columns', None)
df.isnull().sum() df['Ключевые навыки'] = df['Ключевые навыки'].fillna('Не указано') df['Название вакансии'] = df['Название вакансии'].str.lower()#s
def edit_date(date): old_date = date.split() new_date = old_date[0] return new_dates df['Дата публикации'] = df['Дата публикации'].apply(edit_date)
df.to_csv('vacancies_a.csv', index=False) df.to_excel('vacancies_a.xlsx', index=False)
# удаляем строки, где есть хотя бы одно пропущенное значение cleaned_df = df.dropna()# заполняем все пропущенные значения нулями filled_df = df.fillna(0) print("Очищенный DaaFrame:\n", cleaned_df) print("DataFrame с заполненными пропусками:\n", filled_df)
# удаляем строки, где есть хотя бы одно пропущенное значение 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)
# добавляем строку, полностью состоящую из 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)
ссылка на оригинал статьи https://habr.com/ru/articles/882588/
Добавить комментарий