Очистка данных с помощью pandas и NumPy
Оглавление
- Уменьшение столбцов в фрейме данных
- Изменение индекса фрейма данных
- Уборка полей в данных
- Комбинирование методов str с NumPy для очистки столбцов
- Очистка всего набора данных с помощью функции applymap
- Переименование столбцов и пропуск строк
- Очистка данных на Python: Обзор и ресурсы
Специалисты по изучению данных тратят много времени на очистку наборов данных и приведение их в форму, с которой можно работать. Фактически, многие специалисты по изучению данных утверждают, что начальные шаги по получению и очистке данных составляют 80 % работы.
Поэтому, если вы только начинаете работать в этой области или планируете начать работать в ней, важно уметь работать с беспорядочными данными, будь то пропущенные значения, непоследовательное форматирование, неправильные записи или нелепые выбросы.
В этом уроке мы будем использовать библиотеки Python pandas и NumPy для очистки данных.
Мы расскажем о следующем:
- Удаление ненужных столбцов в
DataFrame
- Изменение индекса
DataFrame
- Использование методов
.str()
для очистки столбцов - Использование функции
DataFrame.applymap()
для очистки всего набора данных по элементам - Переименование столбцов в более узнаваемый набор меток
- Удаление ненужных строк в CSV-файле
Бесплатный бонус: Нажмите здесь, чтобы получить доступ к бесплатному руководству по ресурсам NumPy, которое поможет вам найти лучшие учебники, видео и книги для улучшения навыков работы с NumPy.
Вот наборы данных, которые мы будем использовать:
- BL-Flickr-Images-Book.csv - CSV-файл, содержащий информацию о книгах из Британской библиотеки
- university_towns.txt - Текстовый файл, содержащий названия городов колледжей в каждом штате США
- olympics.csv - CSV-файл, содержащий информацию об участии всех стран в летних и зимних Олимпийских играх
Вы можете загрузить наборы данных из репозитория Real Python GitHub для того, чтобы следовать приведенным здесь примерам.
Примечание: Я рекомендую использовать Jupyter Notebooks, чтобы следить за ходом работы.
Этот учебник предполагает базовое понимание библиотек pandas и NumPy, включая рабочие лошадки Панды Series
и DataFrame
объекты , общие методы, которые могут быть применены к этим объектам, и знакомство с NaN
значениями NumPy.
Давайте импортируем необходимые модули и приступим к работе!
>>> import pandas as pd
>>> import numpy as np
Уменьшение столбцов в DataFrame
Часто вы обнаружите, что не все категории данных в наборе данных полезны для вас. Например, у вас может быть набор данных, содержащий информацию об ученике (имя, класс, стандарт, имена родителей и адрес), но вы хотите сосредоточиться на анализе оценок ученика.
В этом случае категории адреса или имен родителей для вас не важны. Сохранение этих ненужных категорий будет занимать лишнее место и, возможно, загромождать время выполнения.
pandas предоставляет удобный способ удаления ненужных столбцов или строк из DataFrame
с помощью функции drop()
. Рассмотрим простой пример, в котором мы удаляем несколько столбцов из DataFrame
.
Сначала создадим DataFrame
из CSV-файла 'BL-Flickr-Images-Book.csv'. В примерах ниже мы передаем относительный путь к pd.read_csv
, что означает, что все наборы данных находятся в папке с именем Datasets
в нашем текущем рабочем каталоге:
>>> df = pd.read_csv('Datasets/BL-Flickr-Images-Book.csv')
>>> df.head()
Identifier Edition Statement Place of Publication \
0 206 NaN London
1 216 NaN London; Virtue & Yorston
2 218 NaN London
3 472 NaN London
4 480 A new edition, revised, etc. London
Date of Publication Publisher \
0 1879 [1878] S. Tinsley & Co.
1 1868 Virtue & Co.
2 1869 Bradbury, Evans & Co.
3 1851 James Darling
4 1857 Wertheim & Macintosh
Title Author \
0 Walter Forbes. [A novel.] By A. A A. A.
1 All for Greed. [A novel. The dedication signed... A., A. A.
2 Love the Avenger. By the author of “All for Gr... A., A. A.
3 Welsh Sketches, chiefly ecclesiastical, to the... A., E. S.
4 [The World in which I live, and my place in it... A., E. S.
Contributors Corporate Author \
0 FORBES, Walter. NaN
1 BLAZE DE BURY, Marie Pauline Rose - Baroness NaN
2 BLAZE DE BURY, Marie Pauline Rose - Baroness NaN
3 Appleyard, Ernest Silvanus. NaN
4 BROOME, John Henry. NaN
Corporate Contributors Former owner Engraver Issuance type \
0 NaN NaN NaN monographic
1 NaN NaN NaN monographic
2 NaN NaN NaN monographic
3 NaN NaN NaN monographic
4 NaN NaN NaN monographic
Flickr URL \
0 http://www.flickr.com/photos/britishlibrary/ta...
1 http://www.flickr.com/photos/britishlibrary/ta...
2 http://www.flickr.com/photos/britishlibrary/ta...
3 http://www.flickr.com/photos/britishlibrary/ta...
4 http://www.flickr.com/photos/britishlibrary/ta...
Shelfmarks
0 British Library HMNTS 12641.b.30.
1 British Library HMNTS 12626.cc.2.
2 British Library HMNTS 12625.dd.1.
3 British Library HMNTS 10369.bbb.15.
4 British Library HMNTS 9007.d.28.
Когда мы рассматриваем первые пять записей, используя метод head()
, мы видим, что несколько столбцов содержат вспомогательную информацию, которая была бы полезна для библиотеки, но не очень описывает сами книги: Edition Statement
, Corporate Author
, Corporate Contributors
, Former owner
, Engraver
, Issuance type
и Shelfmarks
.
Мы можем отказаться от этих столбцов следующим образом:
>>> to_drop = ['Edition Statement',
... 'Corporate Author',
... 'Corporate Contributors',
... 'Former owner',
... 'Engraver',
... 'Contributors',
... 'Issuance type',
... 'Shelfmarks']
>>> df.drop(to_drop, inplace=True, axis=1)
Выше мы определили список, содержащий имена всех столбцов, которые мы хотим удалить. Далее мы вызываем функцию drop()
на нашем объекте, передавая параметр inplace
как True
, а параметр axis
как 1
. Это говорит pandas, что мы хотим, чтобы изменения были сделаны непосредственно в нашем объекте и что он должен искать значения, которые нужно опустить, в столбцах объекта.
Когда мы снова просмотрим DataFrame
, мы увидим, что ненужные столбцы были удалены:
>>> df.head()
Identifier Place of Publication Date of Publication \
0 206 London 1879 [1878]
1 216 London; Virtue & Yorston 1868
2 218 London 1869
3 472 London 1851
4 480 London 1857
Publisher Title \
0 S. Tinsley & Co. Walter Forbes. [A novel.] By A. A
1 Virtue & Co. All for Greed. [A novel. The dedication signed...
2 Bradbury, Evans & Co. Love the Avenger. By the author of “All for Gr...
3 James Darling Welsh Sketches, chiefly ecclesiastical, to the...
4 Wertheim & Macintosh [The World in which I live, and my place in it...
Author Flickr URL
0 A. A. http://www.flickr.com/photos/britishlibrary/ta...
1 A., A. A. http://www.flickr.com/photos/britishlibrary/ta...
2 A., A. A. http://www.flickr.com/photos/britishlibrary/ta...
3 A., E. S. http://www.flickr.com/photos/britishlibrary/ta...
4 A., E. S. http://www.flickr.com/photos/britishlibrary/ta...
Альтернативный вариант - удалить столбцы, передав их в параметр columns
напрямую, вместо того чтобы отдельно указывать метки, которые нужно удалить, и ось, на которой pandas должна искать метки:
>>> df.drop(columns=to_drop, inplace=True)
Этот синтаксис более интуитивен и читабелен. То, что мы пытаемся сделать здесь, непосредственно очевидно.
Если вы заранее знаете, какие столбцы вы хотите сохранить, другой вариант - передать их в аргумент usecols
в pd.read_csv
.
Изменение индекса DataFrame
А pandas Index
расширяет функциональность массивов NumPy для более универсальной нарезки и маркировки. Во многих случаях в качестве индекса полезно использовать уникальное идентификационное поле данных.
Например, в наборе данных, использованном в предыдущем разделе, можно ожидать, что при поиске записи библиотекарь может ввести уникальный идентификатор (значения в столбце Identifier
) для книги:
>>> df['Identifier'].is_unique
True
Заменим существующий индекс на этот столбец с помощью set_index
:
>>> df = df.set_index('Identifier')
>>> df.head()
Place of Publication Date of Publication \
206 London 1879 [1878]
216 London; Virtue & Yorston 1868
218 London 1869
472 London 1851
480 London 1857
Publisher \
206 S. Tinsley & Co.
216 Virtue & Co.
218 Bradbury, Evans & Co.
472 James Darling
480 Wertheim & Macintosh
Title Author \
206 Walter Forbes. [A novel.] By A. A A. A.
216 All for Greed. [A novel. The dedication signed... A., A. A.
218 Love the Avenger. By the author of “All for Gr... A., A. A.
472 Welsh Sketches, chiefly ecclesiastical, to the... A., E. S.
480 [The World in which I live, and my place in it... A., E. S.
Flickr URL
206 http://www.flickr.com/photos/britishlibrary/ta...
216 http://www.flickr.com/photos/britishlibrary/ta...
218 http://www.flickr.com/photos/britishlibrary/ta...
472 http://www.flickr.com/photos/britishlibrary/ta...
480 http://www.flickr.com/photos/britishlibrary/ta...
Технические подробности: В отличие от первичных ключей в SQL, pandas Index
не гарантирует их уникальность, хотя многие операции индексирования и объединения заметят ускорение во времени выполнения, если они уникальны.
Мы можем получить доступ к каждой записи простым способом с помощью loc[]
. Хотя название loc[]
может быть не слишком интуитивным, оно позволяет нам делать индексацию на основе меток, которая представляет собой маркировку строки или записи без учета ее положения:
>>> df.loc[206]
Place of Publication London
Date of Publication 1879 [1878]
Publisher S. Tinsley & Co.
Title Walter Forbes. [A novel.] By A. A
Author A. A.
Flickr URL http://www.flickr.com/photos/britishlibrary/ta...
Name: 206, dtype: object
Другими словами, 206 - это первая метка индекса. Чтобы получить к ней доступ по позиции, мы могли бы использовать df.iloc[0]
, который делает индексацию на основе позиции.
Технические подробности: .loc[]
технически является экземпляром класса и имеет некоторый специальный синтаксис, который не совсем соответствует большинству простых методов экземпляра в Python.
Ранее наш индекс представлял собой RangeIndex: целые числа, начиная с 0
, аналогично встроенному в Python range
. Передав имя столбца в set_index
, мы изменили индекс на значения в Identifier
.
Вы, наверное, заметили, что мы переназначили переменную на объект, возвращаемый методом с помощью df = df.set_index(...)
. Это происходит потому, что по умолчанию метод возвращает измененную копию нашего объекта и не вносит изменения непосредственно в объект. Мы можем избежать этого, задав параметр inplace
:
df.set_index('Identifier', inplace=True)
Уборка полей в данных
На данный момент мы удалили ненужные столбцы и изменили индекс нашего DataFrame
на более разумный. В этом разделе мы очистим конкретные столбцы и приведем их к единому формату, чтобы лучше понять набор данных и обеспечить согласованность. В частности, мы очистим Date of Publication
и Place of Publication
.
При осмотре оказывается, что все типы данных в настоящее время имеют вид object
dtype, что примерно аналогично str
в родном Python.
В нем содержатся любые поля, которые не могут быть аккуратно оформлены как числовые или категориальные данные. Это имеет смысл, поскольку мы работаем с данными, которые изначально представляют собой кучу беспорядочных строк:
>>> df.get_dtype_counts()
object 6
Одно из полей, где имеет смысл использовать числовое значение, - это дата публикации, чтобы в дальнейшем можно было производить расчеты:
>>> df.loc[1905:, 'Date of Publication'].head(10)
Identifier
1905 1888
1929 1839, 38-54
2836 [1897?]
2854 1865
2956 1860-63
2957 1873
3017 1866
3131 1899
4598 1814
4884 1820
Name: Date of Publication, dtype: object
У конкретной книги может быть только одна дата публикации. Поэтому нам нужно сделать следующее:
- Уберите лишние даты в квадратных скобках, где они присутствуют: 1879 [1878]
- Преобразуйте диапазоны дат к их "начальной дате", где бы они ни присутствовали: 1860-63; 1839, 38-54
- Полностью удалите даты, в которых мы не уверены, и замените их датами NumPy
NaN
: [1897?] - Преобразуйте строку
nan
в значениеNaN
от NumPy
Синтезируя эти шаблоны, мы можем воспользоваться одним регулярным выражением для извлечения года публикации:
regex = r'^(\d{4})'
Приведенное выше регулярное выражение предназначено для поиска любых четырех цифр в начале строки, чего вполне достаточно для нашего случая. Приведенное выше выражение представляет собой сырую строку (это означает, что обратная косая черта больше не является управляющим символом), что является стандартной практикой для регулярных выражений.
\d
представляет собой любую цифру, а {4}
повторяет это правило четыре раза. Символ ^
соответствует началу строки, а круглые скобки обозначают группу захвата, которая сигнализирует pandas, что мы хотим извлечь эту часть regex. (Мы хотим использовать ^
, чтобы избежать случаев, когда [
начинается со строки.)
Давайте посмотрим, что произойдет, если мы запустим этот регекс в нашем наборе данных:
>>> extr = df['Date of Publication'].str.extract(r'^(\d{4})', expand=False)
>>> extr.head()
Identifier
206 1879
216 1868
218 1869
472 1851
480 1857
Name: Date of Publication, dtype: object
Дальнейшее чтение: Не знакомы с regex? Вы можете просмотреть выражение выше на сайте regex101.com и узнать все о регулярных выражениях с помощью Regular Expressions: Regexes in Python.
Технически этот столбец по-прежнему имеет dtype object
, но мы можем легко получить его числовую версию с помощью pd.to_numeric
:
>>> df['Date of Publication'] = pd.to_numeric(extr)
>>> df['Date of Publication'].dtype
dtype('float64')
Это приводит к тому, что примерно каждое десятое значение отсутствует, что является небольшой платой за то, что теперь мы можем производить вычисления для оставшихся допустимых значений:
>>> df['Date of Publication'].isnull().sum() / len(df)
0.11717147339205986
Великолепно! Готово!
Комбинирование str
методов с NumPy для очистки столбцов
Выше вы, возможно, заметили использование df['Date of Publication'].str
. Этот атрибут - способ доступа к быстрым строковым операциям в pandas, которые во многом имитируют операции над родными строками Python или скомпилированными регулярными выражениями, такими как .split()
, .replace()
и .capitalize()
.
Для очистки поля Place of Publication
мы можем объединить методы pandas str
с функцией NumPy np.where
, которая, по сути, является векторной формой макроса Excel IF()
. Она имеет следующий синтаксис:
>>> np.where(condition, then, else)
Здесь condition
- это либо объект типа массива, либо маска Boolean. then
- это значение, которое будет использоваться, если condition
оценивается как True
, а else
- значение, которое будет использоваться в противном случае.
По сути, .where()
берет каждый элемент объекта, используемого для condition
, проверяет, оценивает ли этот элемент в True
в контексте условия, и возвращает ndarray
, содержащий then
или else
, в зависимости от того, что применяется.
Он может быть вложен в составной оператор if-then, что позволяет нам вычислять значения на основе нескольких условий:
>>> np.where(condition1, x1,
np.where(condition2, x2,
np.where(condition3, x3, ...)))
Мы будем использовать эти две функции для очистки Place of Publication
, поскольку в этом столбце есть строковые объекты. Вот содержимое столбца:
>>> df['Place of Publication'].head(10)
Identifier
206 London
216 London; Virtue & Yorston
218 London
472 London
480 London
481 London
519 London
667 pp. 40. G. Bryan & Co: Oxford, 1898
874 London]
1143 London
Name: Place of Publication, dtype: object
Мы видим, что для некоторых строк место публикации окружено другой ненужной информацией. Если бы мы рассмотрели больше значений, то увидели бы, что это относится только к некоторым строкам, в которых место публикации указано как "Лондон" или "Оксфорд".
Давайте рассмотрим две конкретные записи:
>>> df.loc[4157862]
Place of Publication Newcastle-upon-Tyne
Date of Publication 1867
Publisher T. Fordyce
Title Local Records; or, Historical Register of rema...
Author T. Fordyce
Flickr URL http://www.flickr.com/photos/britishlibrary/ta...
Name: 4157862, dtype: object
>>> df.loc[4159587]
Place of Publication Newcastle upon Tyne
Date of Publication 1834
Publisher Mackenzie & Dent
Title An historical, topographical and descriptive v...
Author E. (Eneas) Mackenzie
Flickr URL http://www.flickr.com/photos/britishlibrary/ta...
Name: 4159587, dtype: object
Эти две книги были изданы в одном и том же месте, но в одной есть дефисы в названии места, а в другой - нет.
Чтобы очистить этот столбец одним движением, мы можем использовать str.contains()
, чтобы получить булеву маску.
Очищаем колонку следующим образом:
>>> pub = df['Place of Publication']
>>> london = pub.str.contains('London')
>>> london[:5]
Identifier
206 True
216 True
218 True
472 True
480 True
Name: Place of Publication, dtype: bool
>>> oxford = pub.str.contains('Oxford')
Мы объединяем их с np.where
:
df['Place of Publication'] = np.where(london, 'London',
np.where(oxford, 'Oxford',
pub.str.replace('-', ' ')))
>>> df['Place of Publication'].head()
Identifier
206 London
216 London
218 London
472 London
480 London
Name: Place of Publication, dtype: object
Здесь функция np.where
вызывается во вложенной структуре, где condition
является Series
из булевых чисел, полученных с помощью str.contains()
. Метод contains()
работает аналогично встроенному ключевому слову in
, используемому для поиска вхождения сущности в итерабельную переменную (или подстроку в строке).
Замена, которая будет использоваться, - это строка, представляющая желаемое место публикации. Мы также заменяем дефисы пробелом с помощью str.replace()
и переназначаем на колонку в нашем DataFrame
.
Хотя в этом наборе данных есть и другие грязные данные, мы пока обсудим только эти два столбца.
Давайте посмотрим на первые пять записей, которые выглядят намного лучше, чем когда мы начинали:
>>> df.head()
Place of Publication Date of Publication Publisher \
206 London 1879 S. Tinsley & Co.
216 London 1868 Virtue & Co.
218 London 1869 Bradbury, Evans & Co.
472 London 1851 James Darling
480 London 1857 Wertheim & Macintosh
Title Author \
206 Walter Forbes. [A novel.] By A. A AA
216 All for Greed. [A novel. The dedication signed... A. A A.
218 Love the Avenger. By the author of “All for Gr... A. A A.
472 Welsh Sketches, chiefly ecclesiastical, to the... E. S A.
480 [The World in which I live, and my place in it... E. S A.
Flickr URL
206 http://www.flickr.com/photos/britishlibrary/ta...
216 http://www.flickr.com/photos/britishlibrary/ta...
218 http://www.flickr.com/photos/britishlibrary/ta...
472 http://www.flickr.com/photos/britishlibrary/ta...
480 http://www.flickr.com/photos/britishlibrary/ta...
Примечание: На данном этапе Place of Publication
был бы хорошим кандидатом для преобразования в Categorical
dtype, потому что мы можем закодировать довольно небольшой уникальный набор городов целыми числами. ( Использование памяти Categorical пропорционально количеству категорий плюс длина данных; объектный dtype - это константа, умноженная на длину данных.)
Очистка всего набора данных с помощью функции applymap
В некоторых ситуациях вы увидите, что "грязь" не локализована на одной колонке, а более разбросана.
В некоторых случаях полезно применить настраиваемую функцию к каждой ячейке или элементу DataFrame. Метод pandas .applymap()
аналогичен встроенной map()
функции и просто применяет функцию ко всем элементам в DataFrame
.
Давайте рассмотрим пример. Мы создадим DataFrame
из файла "university_towns.txt":
$ head Datasets/univerisity_towns.txt
Alabama[edit]
Auburn (Auburn University)[1]
Florence (University of North Alabama)
Jacksonville (Jacksonville State University)[2]
Livingston (University of West Alabama)[2]
Montevallo (University of Montevallo)[2]
Troy (Troy University)[2]
Tuscaloosa (University of Alabama, Stillman College, Shelton State)[3][4]
Tuskegee (Tuskegee University)[5]
Alaska[edit]
Мы видим, что периодически за названиями штатов следуют университетские города в этом штате: StateA TownA1 TownA2 StateB TownB1 TownB2...
. Если мы посмотрим на то, как записаны названия штатов в файле, то увидим, что все они содержат подстроку "[edit]".
Мы можем воспользоваться этим шаблоном, создав список (state, city)
кортежей и обернув этот список в DataFrame
:
>>> university_towns = []
>>> with open('Datasets/university_towns.txt') as file:
... for line in file:
... if '[edit]' in line:
... # Remember this `state` until the next is found
... state = line
... else:
... # Otherwise, we have a city; keep `state` as last-seen
... university_towns.append((state, line))
>>> university_towns[:5]
[('Alabama[edit]\n', 'Auburn (Auburn University)[1]\n'),
('Alabama[edit]\n', 'Florence (University of North Alabama)\n'),
('Alabama[edit]\n', 'Jacksonville (Jacksonville State University)[2]\n'),
('Alabama[edit]\n', 'Livingston (University of West Alabama)[2]\n'),
('Alabama[edit]\n', 'Montevallo (University of Montevallo)[2]\n')]
Мы можем обернуть этот список в DataFrame и задать столбцы как "State" и "RegionName". pandas возьмет каждый элемент списка и установит State
в левое значение, а RegionName
- в правое.
Результирующий DataFrame выглядит следующим образом:
>>> towns_df = pd.DataFrame(university_towns,
... columns=['State', 'RegionName'])
>>> towns_df.head()
State RegionName
0 Alabama[edit]\n Auburn (Auburn University)[1]\n
1 Alabama[edit]\n Florence (University of North Alabama)\n
2 Alabama[edit]\n Jacksonville (Jacksonville State University)[2]\n
3 Alabama[edit]\n Livingston (University of West Alabama)[2]\n
4 Alabama[edit]\n Montevallo (University of Montevallo)[2]\n
Хотя мы могли бы очистить эти строки в цикле for выше, pandas делает это проще. Нам нужны только название штата и название города, а все остальное мы можем удалить. Хотя мы могли бы снова использовать здесь методы pandas .str()
, мы также могли бы использовать applymap()
для сопоставления вызываемого файла Python с каждым элементом DataFrame.
Мы использовали термин элемент, но что именно мы под ним понимаем? Рассмотрим следующий "игрушечный" DataFrame:
0 1
0 Mock Dataset
1 Python pandas
2 Real Python
3 NumPy Clean
В данном примере каждая ячейка ('Mock', 'Dataset', 'Python', 'pandas' и т. д.) является элементом. Поэтому applymap()
будет применять функцию к каждому из них независимо. Давайте определим эту функцию:
>>> def get_citystate(item):
... if ' (' in item:
... return item[:item.find(' (')]
... elif '[' in item:
... return item[:item.find('[')]
... else:
... return item
pandas' .applymap()
принимает только один параметр, который является функцией (вызываемой), которая должна быть применена к каждому элементу:
>>> towns_df = towns_df.applymap(get_citystate)
Сначала мы определяем функцию Python, которая принимает в качестве параметра элемент из DataFrame
. Внутри функции выполняются проверки, чтобы определить, есть ли в элементе (
или [
или нет.
В зависимости от проверки функция возвращает соответствующие значения. Наконец, функция applymap()
вызывается на нашем объекте. Теперь DataFrame выглядит гораздо аккуратнее:
>>> towns_df.head()
State RegionName
0 Alabama Auburn
1 Alabama Florence
2 Alabama Jacksonville
3 Alabama Livingston
4 Alabama Montevallo
Метод applymap()
брал каждый элемент из DataFrame, передавал его в функцию, и исходное значение заменялось возвращаемым значением. Все просто!
Технические подробности: Несмотря на удобство и универсальность метода, .applymap
может иметь значительное время выполнения для больших наборов данных, поскольку он сопоставляет Python callable с каждым отдельным элементом. В некоторых случаях эффективнее выполнять векторизованные операции, использующие Cython или NumPY (которые, в свою очередь, выполняют вызовы на C) под капотом.
Переименование столбцов и пропуск строк
Часто наборы данных, с которыми вы будете работать, содержат либо непонятные названия столбцов, либо неважную информацию в первых и/или последних строках, например, определения терминов, используемых в наборе данных, или сноски.
В этом случае мы захотим переименовать столбцы и пропустить некоторые строки, чтобы можно было дойти до нужной информации с правильными и разумными метками.
Чтобы продемонстрировать, как мы можем это сделать, давайте сначала посмотрим на первые пять строк набора данных "olympics.csv":
$ head -n 5 Datasets/olympics.csv
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
,? Summer,01 !,02 !,03 !,Total,? Winter,01 !,02 !,03 !,Total,? Games,01 !,02 !,03 !,Combined total
Afghanistan (AFG),13,0,0,2,2,0,0,0,0,0,13,0,0,2,2
Algeria (ALG),12,5,2,8,15,3,0,0,0,0,15,5,2,8,15
Argentina (ARG),23,18,24,28,70,18,0,0,0,0,41,18,24,28,70
Теперь прочитаем его в pandas DataFrame:
>>> olympics_df = pd.read_csv('Datasets/olympics.csv')
>>> olympics_df.head()
0 1 2 3 4 5 6 7 8 \
0 NaN ? Summer 01 ! 02 ! 03 ! Total ? Winter 01 ! 02 !
1 Afghanistan (AFG) 13 0 0 2 2 0 0 0
2 Algeria (ALG) 12 5 2 8 15 3 0 0
3 Argentina (ARG) 23 18 24 28 70 18 0 0
4 Armenia (ARM) 5 1 2 9 12 6 0 0
9 10 11 12 13 14 15
0 03 ! Total ? Games 01 ! 02 ! 03 ! Combined total
1 0 0 13 0 0 2 2
2 0 0 15 5 2 8 15
3 0 0 41 18 24 28 70
4 0 0 11 1 2 9 12
Это действительно грязно! Столбцы представляют собой строковую форму целых чисел, индексированных по 0. Строка, которая должна была быть нашим заголовком (т. е. использоваться для задания имен столбцов), находится по адресу olympics_df.iloc[0]
. Это произошло потому, что наш CSV-файл начинается с 0, 1, 2, ..., 15.
Кроме того, если мы обратимся к источнику этого набора данных, то увидим, что NaN
выше должно быть что-то вроде "Страна", ? Summer
должно представлять "Летние игры", 01 !
должно быть "Золото", и так далее.
Поэтому нам нужно сделать две вещи:
- Пропустите одну строку и установите заголовок в качестве первой (с индексом 0) строки
- Переименуйте столбцы
Мы можем пропускать строки и устанавливать заголовок при чтении CSV-файла, передавая некоторые параметры в функцию read_csv()
.
Эта функция принимает много необязательных параметров, но в данном случае нам нужен только один (header
), чтобы удалить 0-й ряд:
>>> olympics_df = pd.read_csv('Datasets/olympics.csv', header=1)
>>> olympics_df.head()
Unnamed: 0 ? Summer 01 ! 02 ! 03 ! Total ? Winter \
0 Afghanistan (AFG) 13 0 0 2 2 0
1 Algeria (ALG) 12 5 2 8 15 3
2 Argentina (ARG) 23 18 24 28 70 18
3 Armenia (ARM) 5 1 2 9 12 6
4 Australasia (ANZ) [ANZ] 2 3 4 5 12 0
01 !.1 02 !.1 03 !.1 Total.1 ? Games 01 !.2 02 !.2 03 !.2 \
0 0 0 0 0 13 0 0 2
1 0 0 0 0 15 5 2 8
2 0 0 0 0 41 18 24 28
3 0 0 0 0 11 1 2 9
4 0 0 0 0 2 3 4 5
Combined total
0 2
1 15
2 70
3 12
4 12
Теперь у нас есть правильная строка, установленная в качестве заголовка, и все ненужные строки удалены. Обратите внимание на то, как pandas изменила название столбца, содержащего название стран, с NaN
на Unnamed: 0
.
Для переименования столбцов мы воспользуемся методом rename()
DataFrame, который позволяет перемаркировать ось на основе отображения (в данном случае dict
).
Начнем с определения словаря, который сопоставляет текущие имена столбцов (в качестве ключей) с более удобными (значениями словаря):
>>> new_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'}
Мы вызываем функцию rename()
на нашем объекте:
>>> olympics_df.rename(columns=new_names, inplace=True)
Установка inplace в значение True
указывает, что наши изменения будут внесены непосредственно в объект. Давайте проверим, насколько это соответствует действительности:
>>> olympics_df.head()
Country Summer Olympics Gold Silver Bronze Total \
0 Afghanistan (AFG) 13 0 0 2 2
1 Algeria (ALG) 12 5 2 8 15
2 Argentina (ARG) 23 18 24 28 70
3 Armenia (ARM) 5 1 2 9 12
4 Australasia (ANZ) [ANZ] 2 3 4 5 12
Winter Olympics Gold.1 Silver.1 Bronze.1 Total.1 # Games Gold.2 \
0 0 0 0 0 0 13 0
1 3 0 0 0 0 15 5
2 18 0 0 0 0 41 18
3 6 0 0 0 0 11 1
4 0 0 0 0 0 2 3
Silver.2 Bronze.2 Combined total
0 0 2 2
1 2 8 15
2 24 28 70
3 2 9 12
4 4 5 12
Очистка данных на языке Python: Обзор и ресурсы
В этом уроке вы узнали, как можно удалить ненужную информацию из набора данных с помощью функции drop()
, а также как задать индекс для набора данных, чтобы на элементы в нем можно было легко ссылаться.
Кроме того, вы узнали, как очищать object
поля с помощью аксессора .str()
и как очищать весь набор данных с помощью метода applymap()
. Наконец, мы изучили, как пропускать строки в CSV-файле и переименовывать столбцы с помощью метода rename()
.
Знать об очистке данных очень важно, потому что это большая часть науки о данных. Теперь вы имеете базовое представление о том, как можно использовать pandas и NumPy для очистки наборов данных!
По ссылкам ниже вы найдете дополнительные ресурсы, которые помогут вам на пути к науке о данных на Python:
- Pandas документация
- The NumPy documentation
- Python для анализа данных от Уэса Маккинни, создателя pandas
- pandas Cookbook Теда Петру, тренера и консультанта по науке о данных