"Анализ данных"

Общие задачи очистки данных в повседневной работе специалиста по данным/аналитика в 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.

Подробнее Чтение