"Анализ данных"
Общие задачи очистки данных в повседневной работе специалиста по данным/аналитика в Python
Памятка по очистке данных
Очистка данных — неотъемлемая часть вашей жизни, если вы специалист по данным, аналитик данных или инженер по машинному обучению. В реальной жизни очень сложно найти абсолютно чистые и готовые к использованию данные. Прежде чем погрузиться в анализ, визуализацию или машинное обучение, очистка данных является очень распространенной задачей почти для всех реальных проектов.
В этой статье речь пойдет об общих и очень распространенных задачах очистки данных. Мы будем использовать несколько разных наборов данных, чтобы продемонстрировать различные процессы очистки данных.
Больше никаких вступлений. Давайте приступим к делу!
Во-первых, я начну с набора данных olympics.csv. Это действительно распространенный набор данных для использования на практике. Первый набор данных, который я буду использовать, — это набор данных Олимпийских игр. Я нашел этот набор данных из курса Coursera. Приходилось чистить перед использованием. Вот ссылка на набор данных:
Здесь я импортирую необходимые пакеты, считываю набор данных в формат DataFrame с помощью функции read_csv и отображаю первые пять строк данных с помощью функции .head().
import pandas as pd import numpy as np olympics = pd.read_csv("olympics.csv") olympics.head()
Поиск правильного заголовка
В DataFrame выше верхняя строка явно не является заголовком. Заголовок на самом деле является следующей строкой, где индекс строки равен 1. Таким образом, для этого можно использовать дополнительный параметр «header=1» в методе read_csv. Здесь я снова читаю набор данных:
olympics = pd.read_csv("olympics.csv", header=1) olympics.head()
Переименование столбцов
Названия столбцов здесь не очень чистые и понятные. Первый столбец содержит все названия стран. Но у него странное имя! Там, где написано «01!», «02!» или «03!», должно быть «Золото», «Серебро» и «Бронза». Итак, давайте переименуем столбцы с помощью функции «переименовать». Функция «переименовать» принимает словарь, где ключ — это имя исходного столбца, а значение — имя нового столбца.
col_names = {'Unnamed: 0': 'Country', '? Summer': 'Summer Olympics', '01 !': 'Gold', '02 !': 'Silver', '03 !': 'Bronze', '? Winter': 'Winter Olympics', '01 !.1': 'Gold.1', '02 !.1': 'Silver.1', '03 !.1': 'Bronze.1', '? Games': '# Games', '01 !.2': 'Gold.2', '02 !.2': 'Silver.2', '03 !.2': 'Bronze.2'} olympics.rename(columns=col_names, inplace=True) #Checking the olympics DataFrame with the new column names olympics.head()
Имена столбцов исправлены!
Избавление от лишних хвостов в текстах
Посмотрите на названия стран в первом столбце. После названия страны в скобках стоит аббревиатура. Иногда бывает даже дважды. В этом нет необходимости. Это не будет хорошо выглядеть в визуализации или любом анализе. Это нужно убрать.
Для этого мы будем использовать функцию «applymap». Функция «applymap» принимает функцию, которую следует применить к столбцу или столбцам, в которые мы намерены внести изменения. Здесь мы определяем функцию, которая будет принимать строку в качестве параметра и удалять все после того, как найдет первую скобку ‘(‘.
def get_country(st): if ' (' in st: return st[:st.find(' (')] else: return st
Мы будем использовать эту функцию в качестве параметра в функции «applymap».
pd.DataFrame(olympics['Country']).applymap(get_country)
Как видите, в столбце страны у нас сейчас только название страны. Если бы у нас было более одного столбца с одинаковыми требованиями к очистке, мы могли бы использовать «applymap» для всех столбцов одновременно.
Этот набор данных теперь готов для анализа или визуализации!
Мой следующий набор данных будет набором данных по артриту. Я получил этот набор данных в качестве задания на одном из моих курсов во время моей магистерской программы. Это в формате «arff». Вот ссылка на этот набор данных.
Если вы не знакомы с этим форматом, не беспокойтесь об этом. Наше внимание сосредоточено на очистке данных. Вот как загрузить этот форматированный набор данных «arff» в фрейм данных pandas:
from scipy.io import arff d = arff.loadarff("project-2018-BRFSS-arthritis.arff") df = pd.DataFrame(d[0])
Этот набор данных имеет 108 столбцов и 11933 строки. Итак, довольно большой набор данных!
Преобразование данных байтового типа в числовые данные
Первый враг в этом наборе данных — данные типа b’2’. Это данные байтового типа, которые не подходят для любого проекта, связанного с данными. Нам нужны простые числовые данные. Даже если мы получаем текстовые данные, мы каким-то образом преобразуем их в числовые данные, верно? Таким образом, эти данные байтового типа необходимо преобразовать в какой-либо более удобный формат.
Сначала я расшифрую их в строковую форму.
Следующая функция принимает DataFrame в качестве параметра, проверяет, есть ли столбцы с данными байтового типа, а затем декодирует их в строковый тип.
def convert_str(df): for i in df.columns: if type(df[i][0]) == bytes: df[i] = df[i].str.decode("utf-8") return df df = convert_str(df) df
Данные байтового типа преобразуются в строковые типы. Но все же есть проблема. Посмотрите на последнее значение столбца выше. Это '?'. В разных местах может быть больше таких «?», которые мы не можем проверить по одному в большом наборе данных. Используя цикл, мы проверим каждый столбец. Если есть '?', он будет заменен пустой строкой.
for i in df.columns: if type(df[i][0]) == str: df[i] = df[i].str.replace('?', '') df
Посмотрите еще раз на последнее значение первого столбца, этот «?» исчез!
Но и это не решает проблемы. Потому что все эти данные байтового типа преобразуются в строковый тип. В идеале нам нужны числовые данные. Давайте теперь преобразуем их в числовые данные.
col = [] for i in df.columns: if type(df[i][0]) == str: col.append(i) df[col] = df[col].apply(pd.to_numeric, errors = 'coerce')
В последней строке показано несколько нулевых значений, и весь набор данных может содержать намного больше нулевых значений. Мы должны проверить количество нулевых значений в каждом столбце.
df.isnull().sum()
Выход:
x.aidtst3 805 employ1 34 income2 114 weight2 180 height3 198 ... x.michd 114 x.ltasth1 0 x.casthm1 0 x.state 0 havarth3 0 Length: 108, dtype: int64
У нас есть много нулевых значений, с которыми нужно иметь дело.
Работа с нулевыми значениями
Есть несколько часто используемых способов работы с нулевыми значениями. Один из способов — удалить все нулевые значения, используя этот простой фрагмент кода, который просто удалит все строки с любыми нулевыми значениями:
df.dropna()
Проблема в том, что таким образом у вас может остаться только несколько строк данных. Если набор данных действительно большой и даже после удаления нулевых значений у вас достаточно данных для анализа, все может быть в порядке. Но я не призываю вас удалять всю строку данных таким образом. Вы можете потерять важную информацию.
Другой способ — заполнить все нулевые значения нулями следующим образом:
df.fillna(0)
Это заполнит все нулевые значения нулями. Если это служит вашей цели, идите с этим.
Нулевые значения также могут быть заполнены значением непосредственно перед этим:
df.fillna(method = "bfill")
Точно так же вы можете заполнить нулевые значения значением сразу после этого:
df.fillna(method="ffill")
Это все доступные варианты. Наконец, я покажу способ, который мне нравится больше всего. Я обычно заполняю нулевые значения медианой. Также часто пустые значения заполняют средними или стандартными нормальными значениями. Итак, мы пройдемся по столбцам и заполним нулевые значения всех столбцов, используя их медиану.
for i in df.columns: df[i].fillna(df[i].median(), inplace=True)
Давайте еще раз проверим количество нулевых значений в каждом столбце:
df.isnull().sum()
Выход:
x.aidtst3 0 employ1 0 income2 0 weight2 0 height3 0 .. x.michd 0 x.ltasth1 0 x.casthm1 0 x.state 0 havarth3 0 Length: 108, dtype: int64
Нет больше нулевых значений!
Этот набор данных должен быть готов к работе.
Далее у меня будет набор данных, в котором каждый столбец нуждается в некоторой очистке. Этот набор данных подготовлен специально для этого урока. Я добавил сюда некоторые очень распространенные типы задач.
Вот ссылка на набор данных:
Вот как это выглядит:
ppl = pd.read_csv("people.csv") ppl
Вы можете видеть, что почти в каждой колонке есть над чем работать.
Удаление повторяющихся данных
Если вы заметили, некоторые люди заходили дважды. Данные «Алиса» и «Руми» дублируются. Это обычное дело. Это могло быть напечатано дважды или, может быть, два разных человека взяли свои данные. Давайте избавимся от дубликатов данных перед любой другой очисткой данных:
people = people.drop_duplicates(subset="Name") people
Дубликаты исчезли! Давайте работать со столбцами один за другим.
Получение простых четырехзначных данных о годе
Начиная со столбца «Год», некоторые значения не являются 4-значным годом. Он имеет некоторые дополнительные значения, которые следует удалить перед анализом.
Мы будем использовать регулярное выражение, чтобы получить только 4-значное значение года:
year_ex = people["Year"].str.extract(r'^(\d{4})', expand=False) year_ex
Выход:
0 1980 1 1978 2 1982 3 1992 4 1987 Name: Year, dtype: object
Мы получили простые 4-значные годы!
Оценка дохода в виде простых числовых значений
Теперь идет столбец «Доход», где у нас есть строковые значения, содержащие «K», «M». Для анализа или визуализации со значениями дохода более полезными будут числовые значения. Итак, эти «К» и «М» нужно заменить тремя нулями и шестью нулями:
people["Income"].replace({"K": "*1e3", "M": "*1e6"}, regex=True).map(pd.eval).astype(int)
Выход:
0 2000000 1 40000 2 120000 3 10000000 4 6000000 Name: Income, dtype: int32
В столбце «Образование» слова «Гарвард», «Стэнфорд» или «Оксфорд» содержат несколько дополнительных слов или символов. Если вы хотите использовать эти данные в анализе, два данных Гарварда будут рассматриваться как два разных данных. Итак, нам нужно избавиться от этих лишних символов и иметь только простые и точно такие же строки для одного и того же университета. Вот как я это сделал.
Во-первых, создайте три логические переменные для трех разных университетов.
edu = people["Education"] harvard = edu.str.contains("Harvard") stanford = edu.str.contains("Stanford") oxford = edu.str.contains("Oxford")
Теперь мы будем использовать «Гарвард», если строка содержит «Гарвард», использовать «Стэнфорд», где строка содержит «Стэнфорд», и т. д. с вложенными функциями np.where().
people["Education"] = np.where(harvard, "Harvard", np.where(stanford, "Stanford", np.where(oxford, "Oxford", people["Education"])))
Выход:
0 Harvard 1 Oxford 2 Oxford 3 Stanford 4 Harvard Name: Education, dtype: object
Смотреть! Теперь у нас очень простые имена из одного слова. Никакой другой путаницы!
Преобразование данных временного ряда в формат DateTime
Теперь колонка «Выпускной». Для анализа данных о времени или дате в pandas полезно иметь формат datetime. Я чувствую, что это также более ясно. Это обычная задача очистки данных, если есть данные временных рядов. Много времени они приходят не в дружеском формате.
Его очень просто преобразовать в формат datetime:
pd.to_datetime(people['Graduation'])
Выход:
0 2020-03-12 1 2020-04-14 2 2020-08-01 3 2020-05-18 4 2021-05-10 Name: Graduation, dtype: datetime64[ns]
Сделанный!
Очистить цифры или специальные символы и оставить только чистый текст
Наконец, эта «стоячая» колонка. Почти всегда текстовые данные требуют некоторой очистки данных. Постоянно появляются опечатки, ненужные цифры и символы. Почти никогда не бывает так, что текстовые данные получаются очень чистыми. Для начала мы сделаем все строки строчными. Таким образом, «низкий» и «низкий» не будут отличаться.
people["Standing"] = people["Standing"].str.lower()
Я избавлюсь от всех цифр и других символов, используя простое регулярное выражение:
people["Standing"].map(lambda x: re.sub('([^a-z]+)', '', x))
Выход:
0 medium 1 low 2 excellant 3 low 4 excellant Name: Standing, dtype: object
Это чисто и просто!
Заключение
Данные могут поступать самыми разными путями. Существует так много различных вариантов очистки данных. В этой статье я хотел перечислить некоторые общие задачи очистки данных для повседневной очистки данных. Такой список может сэкономить много времени. Надеюсь, в будущем я добавлю больше вариантов очистки данных.
Пожалуйста, не стесняйтесь подписаться на меня в Твиттере, на странице Facebook и загляните на мой новый канал YouTube.