Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save dsibi/5d3b51d2a52844bd5c5f2869c600d86d to your computer and use it in GitHub Desktop.

Select an option

Save dsibi/5d3b51d2a52844bd5c5f2869c600d86d to your computer and use it in GitHub Desktop.
yandex_python_data_analyst_6_Предобработка данных
Предобработке данных аналитик посвящает массу времени: иначе удовлетворительное решение поставленной задачи неосуществимо.
Чему вы научитесь
Познакомитесь с распространёнными видами «мусора в данных» и специальными методами Pandas для борьбы с ним.
Сколько времени это займёт
2 часа = 5 уроков от 2 до 30 минут.
Постановка задачи
Продолжаем работать с данными Яндекс.Музыки — оцениваем качество данных и повышаем его до готовности к анализу.
Когда вы приступаете к работе над аналитической задачей, должны быть готовы не только вы (чтобы придумать, как найти зависимости или красиво визуализировать данные), но и сами данные.
Процесс подготовки данных для дальнейшего анализа называется предобработка. Заключается она в поиске проблем, которые могут быть в данных, и в решении этих проблем.
В информатике работает принцип GIGO (от англ. garbage in — garbage out, буквально «мусор на входе — мусор на выходе»). Это значит, что при ошибках во входных данных даже правильный алгоритм работы выдаёт неверные результаты.
Посмотрите на этот срез данных для нашей задачи:
image
Что сразу же бросается в глаза? В столбце genre для одного жанра есть два названия: джаз и jazz. Если принять всё как есть, подсчёт прослушанных джазовых композиций даст два ответа — для жанра джаз и для жанра jazz, которые представляют одну и ту же сущность. Это помешает сделать правильные выводы об интересе пользователей к джазу, и может повлечь неверные решения.
image
Нам предстоит очистить данные от мусора, чтобы эта аббревиатура расшифровывалась более приятно: good in — good out, т.е. «хорошо на входе — хорошо на выходе».
Подвох может крыться не только в содержании столбца, но даже в его названии. Вы уже умеете получать данные из столбцов таблицы, чтобы, к примеру, посчитать количество уникальных пользователей. Попробуем получить доступ к столбцу «user_id», как учили:
print(df['user_id'])
-----------------------------------------------------
KeyError Traceback (most recent call last)
/usr/local/lib/python3.6/dist-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
2524 try:
-> 2525 return self._engine.get_loc(key)
2526 except KeyError:
pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()
pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()
pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()
pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()
KeyError: 'user_id'
На первый взгляд кажется, что мы ввели имя столбца правильно, но при тщательном изучении можно заметить пробелы в начале. Обычно их порождает человеческий фактор — при именовании нажимают клавишу пробела или копируют лишние символы из текста технического задания. Ещё вариант: при выгрузке данных из какой-нибудь информационной системы заголовки сформировались вот так плохо. Каждое название бывает несчастливо по-своему — в одном столбце всё нормально, в другом 5 пробелов в начале и 1 в конце, в третьем 3 пробела в начале.
Трудно определить количество пробелов на глаз, чтобы задать точное правило их ликвидации, но ещё более сумасбродное решение — всякий раз копировать значимые символы названия столбца вручную. Это серьёзная проблема; избавляться от лишних пробелов учатся в первую очередь.
Вообще сложности с данными бывают двух видов:
данные содержат мусор;
данные корректны, но представлены в такой форме, что малопригодны для анализа.
Например, таблица с данными о расстоянии от Земли до небесных тел (в миллионах километров) может быть представлена в двух вариантах:
image
Как вы думаете, какой вариант лучше подходит для анализа?
В верхней таблице не разобрать, какая строка за что отвечает, тогда как в нижней всё более или менее ясно. Лучше, если в каждом столбце хранятся значения одной переменной, а каждая строка содержит одно наблюдение, к которому привязаны значения разных переменных.
Строки нижней таблицы можно представить как наблюдения за одним небесным телом: в переменных сохраним его название, минимальное и максимальное расстояния до Земли.
Верхняя таблица не совсем безнадёжна — такой вариант может быть представлен в качестве визуального отчета. Международные организации часто публикуют данные своих ежегодников в подобной форме.
С точки зрения организации данных менеджер Яндекс Музыки дал нам хорошую таблицу, но мусор в ней всё-таки есть. Понадобится выполнить ряд операций.
Посмотрите на первые 10 строк. Какие необычные явления вы можете заметить?
image
Проблемы с названиями столбцов уже известны. Теперь обратите внимание на повторяющиеся значения в столбце Artist: какие-то NaN, и это точно не музыкальная группа. А ещё нужно разобраться со строками 1 и 2. Там полный повтор.
image
Имейте в виду, что аналитик не только «чистит» данные, но и сообщает команде о проблемах, чтобы их причины были установлены. Каждый сорт мусора появляется в таблице не просто так.
В этой теме мы раскроем базовые механизмы борьбы с часто встречаемыми проблемами. Из них самые очевидные, но в то же время самые опасные:
• некорректное именование столбцов
• дублирование значений
• отсутствующие значения (NaN)
Механизмы борьбы с ними — основа, с которой вы можете начать своё развитие как специалист по предобработке данных.
Для начала вызовем метод info(), чтобы просмотреть сводку по всему набору данных.
TASK
Просмотрите информацию о наборе данных: воспользуйтесь методом info().
SOLUTION
import pandas as pd
df = pd.read_csv('music_log.csv')
df.info()
Метод info() возвращает названия столбцов таблицы и сведения о типах данных в ней. Итак, наши проблемы:
В начале названия одного столбца явно есть несколько лишних пробелов. От них нужно избавиться.
Название из двух слов содержит пробел, который необходимо заменить на символ нижнего подчёркивания.
Названия должны быть на одном языке и набраны в одном регистре, чтобы не заучивать уникальный формат для каждого столбца.
Каждый столбец содержит определённый признак — крайне желательно, чтобы название столбца отражало в краткой форме, какая информация в нём содержится.
Вернёмся к таблице с расстояниями (в миллионах километров) от Земли до небесных тел.
measurements = [['Солнце',146,152], # Измерения хранятся в списке списков
['Луна',0.36, 0.41], # measurements (англ. measurement, «измерение»).
['Меркурий',82, 217],
['Венера',38, 261],
['Марс',56,401],
['Юпитер',588, 968],
['Сатурн',1195, 1660],
['Уран',2750, 3150],
['Нептун', 4300, 4700],
['Комета Галлея', 6, 5400]]
# Названия столбцов хранятся в переменной header.
header = ['Небесные тела ','MIN', 'MAX']
# Сохраним структуру данных в переменной celestial (англ. celestial, «небесный»).
celestial = pd.DataFrame(data = measurements, columns = header)
Чтобы как следует разглядеть названия столбцов без отвлекающей информации, удобно запросить значения атрибута columns:
print(celestial.columns)
Index(['Небесные тела ', 'MIN', 'MAX'], dtype='object')
Заметно, что столбцы называются на разных языках. Название Небесные тела содержит опасный пробел в конце, и слова этого названия разделены пробелом.
Символы за пределами базовой латиницы — недруги аналитика; от них надо избавляться в первую очередь. Правильно будет переименовать «Небесные тела» в celestial_bodies. MIN и MAX уже написаны латиницей, но эти сокращения мало говорят о том, что за цифры записаны в соответствующих столбцах. Хорошее решение — назвать их min_distance и max_distance. Так сразу понятно, что это минимальные и максимальные расстояния.
Чтобы изменить названия столбцов, воспользуйтесь методом set_axis(). Он принимает три аргумента:
cписок с новыми названиями столбцов;
axis — ось, которой новые названия присваиваются: 'index', если они даются строкам, и 'columns', если это список новых названий столбцов;
inplace. Принимает значения True либо False. В первом случае метод set_axis() перестраивает структуру данных так, что она замещает прежнюю в переменной с тем же именем.
image
Заменим названия столбцов таблицы с небесными телами.
celestial.set_axis(['celestial_bodies','min_distance','max_distance'], axis = 'columns', inplace = True)
Проверим результат:
print(celestial.columns)
Index(['celestial_bodies', 'min_distance', 'max_distance'], dtype='object')
print(celestial)
CELESTIAL_BODIES MIN_DISTANCE MAX_DISTANCE
0 Солнце 146.00 152.00
1 Луна 0.36 0.41
2 Меркурий 82.00 217.00
3 Венера 38.00 261.00
4 Марс 56.00 401.00
5 Юпитер 588.00 968.00
6 Сатурн 1195.00 1660.00
7 Уран 2750.00 3150.00
8 Нептун 4300.00 4700.00
9 Комета Галлея 6.00 5400.00
Готово! Теперь эта таблица годится для анализа. Давайте проверим названия столбцов в данных Яндекс.Музыки.
TASK_1_4
Выведите список столбцов.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log.csv')
print(df.columns)
TASK_2_4
Подготовьте список new_names с новыми именами для столбцов.
user_id → user_id
total play → total_play_seconds
Artist → artist_name
genre → genre_name
track → track_name
SOLUTION
import pandas as pd
df = pd.read_csv('music_log.csv')
new_names=['user_id', 'total_play_seconds', 'artist_name', 'genre_name', 'track_name']
df.set_axis(new_names,axis='columns',inplace=True)
TASK_3_4
Переименуем столбцы таблицы, которая хранится в переменной df.
Вызовите метод set_axis() и передайте ему список new_names, а значением аргумента inplace установите True.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log.csv')
new_names=['user_id', 'total_play_seconds', 'artist_name', 'genre_name', 'track_name']
df.set_axis(new_names,axis='columns',inplace=True)
TASK_4_4
Проверьте, что получилось, запросив для нашей структуры данных df атрибут columns.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log.csv')
new_names=['user_id', 'total_play_seconds', 'artist_name', 'genre_name', 'track_name']
df.set_axis(new_names,axis='columns',inplace=True)
print(df.columns)
Теперь, когда названия столбцов нашей таблицы — её берега — обустроены, мы можем пуститься в плавание по самим данным. Вы помните, что столбец, который теперь называется artist_name, содержит пустые значения (мы употребляем также синоним «пропущенные значения»):
print(df.head(10))
USER_ID TOTAL_PLAY_SECONDS ARTIST_NAME GENRE_NAME TRACK_NAME
0 BF6EA5AF 92.851388 Marina Rei pop Musica
1 FB1E568E 282.981000 Stive Morgan ambient Love Planet
2 FB1E568E 282.981000 Stive Morgan ambient Love Planet
3 EF15C7BA 8.966000 NaN dance Loving Every Minute
4 82F52E69 193.776327 Rixton pop Me And My Broken Heart
5 4166D680 3.007000 Henry Hall & His Gleneagles Hotel Band jazz Home
6 F4F5677 0.100000 NaN classicmetal NaN
7 386FE1ED 211.880000 NaN electronic Riviera
8 A5E0D927 3.161000 Andrew Paul Woodworth pop The Name of This Next Song Is Called
9 E9E8A0CA 274.390000 Pillar Point indie Dove
Замены пропущенных значений в DataFrame бывают трёх видов:
1) Ожидаемые: None или NaN. None — это эквивалент null в других языках программирования: особое значение, указывающее, что в этой ячейке таблицы никакого значения нет. None относится к NoneType. NaN говорит о том, что в ячейке находится «не число». Основное отличие NaN в том, что его можно использовать в математических операциях, так как по типу это число с плавающей запятой.
2) Странные: плейсхолдеры (тексты-заполнители) какого-нибудь общепринятого стандарта, иногда неизвестного вам, но которого придерживаются составители. Чаще всего это n/a, na, NA, и N.N. либо NN.
3) Неожиданные: например, разработчики решили, что пустые значения в таблице будут заполняться знаками вопроса или нулями. В лучшем случае этот факт укажут в документации, в худшем – придётся просматривать данные самостоятельно. Если какой-нибудь спецсимвол или число встречаются часто, и этому нет внятного объяснения, то высока вероятность, что так передаются пропущенные значения.
Но будьте осторожны: иногда нули — это действительно нули, как в наборе данных из нашей задачи, где ноль показывает, что трек был пропущен (его слушали 0 секунд).
Рассмотрим методы борьбы с пропущенными значениями на примере данных ВОЗ о заболеваемости холерой в 2017 году:
import pandas as pd
cholera = pd.read_csv('cholera.csv')
print(cholera)
REGION COUNTRY TOTAL_CASES IMPORTED_CASES DEATHS CASE_FATALITY_RATE NOTES
0 Азия Афганистан 33 0 1 3.0 NaN
1 Азия Индия 385 NaN 3 0.7 NaN
2 Азия Иран 634 625 4 0.6 NaN
3 Азия Йемен 1032481 0 2261 0.2 NaN
4 Азия Китай 14 NaN 0 0 NaN
5 Азия Катар 5 5 0 0 NaN
6 Азия Малайзия 2 0 0 0 NaN
7 Азия Непал 7 NaN 0 0 NaN
8 Азия ОАЭ 12 12 0 0 NaN
9 Азия Саудовская Аравия 5 5 0 0 NaN
10 Азия Сингапур 3 3 0 0 NaN
11 Азия Таиланд 8 0 0 0 NaN
12 Азия Филиппины 134 NaN 2 1.5 NaN
13 Азия Южная Корея 5 5 0 0 NaN
14 Азия Япония 7 5 0 0 NaN
15 Америка Гаити 13681 0 159 1.2 NaN
16 Америка Доминиканская Республика 122 0 4 3.3 NaN
17 Америка Канада 4 3 0 0 NaN
18 Америка США 11 9 0 0 NaN
19 Африка Все страны 179835 NaN 3220 1.8 нет информации о завозных случаях
20 Европа Все страны NaN NaN NaN NaN нет сообщений за 2017
21 Океания Австралия 3 3 0 0 NaN
22 Мир Все страны 1227391 675 5654 0.5 NaN
Документация
Для разных регионов (столбец 'region') и стран ('country') указано общее число случаев заболевания холерой ('total_cases'), в том числе завозные случаи ('imported_cases') и смертельные ('deaths'). Всё это целые числа, поскольку данные приведены с точностью до человека. Столбец 'case_fatality_rate' содержит процент летальных исходов (тип float), столбец 'notes' — строки с примечаниями.
Источник: Всемирная организация здравоохранения, Weekly Epidemiological Record), 21 September 2018, vol. 93, 38, pp. 489–500.
Посчитать в каждом столбце отсутствующие значения можно методом .isnull(). Если значение элемента не существует, .isnull() возвращает True, а иначе — False. Суммируют эти True вызовом метода sum(), который в этом случае возвращает общее число элементов без определённых значений.
print(cholera.isnull().sum())
region 0
country 0
total_cases 1
imported_cases 6
deaths 1
case_fatality_rate 1
notes 21
dtype: int64
Обратите внимание на цепочку вызовов с точечной нотацией. Мы и прежде вызывали один метод, передавая ему другой метод как аргумент. Например, print(type(df)) . А сейчас результат работы метода isnull() передан методу sum(), и записано это через точку.
Также подойдёт метод isna(), подсчитывающий пустые значения. В таблице по холере пропущенные значения качественные, так что этот метод отыщет их все.
print(cholera.isna().sum())
region 0
country 0
total_cases 1
imported_cases 6
deaths 1
case_fatality_rate 1
notes 21
dtype: int64
Метод борьбы с пропущенными значениями, который вы выберете, должен учитывать интересы решения конечной задачи бизнеса. В целом есть два пути: заполнить пропущенные значения на основе имеющихся данных или удалить все строки с пропущенными значениями.
image
Бизнес разрывается между двумя решениями: как потратить не слишком много времени и как не упустить данные, которые обычно чего-то стоят.
Если сейчас пойти по пути удаления всех строк, где есть пропущенные значения, можно потерять важные данные, например, в Африке было зафиксировано 179835 случаев заболевания, но ни одного завозного случая (это сейчас отмечено NaN). Удалив эту строку, мы потеряем важные данные для статистики.
Другая ситуация с Европой. Вся строка состоит из пропущенных значений. Примечание сообщает, что в 2017 году европейцы холерой не болели. Значит, строку можно удалить и это никак не повлияет на результат.
Чтобы не лишиться строк с важными данными, заполним значения NaN в столбце 'imported_cases' нулями.
Для этого лучше всего использовать метод fillna(), где в качестве аргумента выступает заменитель отсутствующих значений.
image
cholera['imported_cases'] = cholera['imported_cases'].fillna(0)
Теперь все пропущенные значения в столбце заполнены нулями.
print(cholera)
REGION COUNTRY TOTAL_CASES IMPORTED_CASES DEATHS CASE_FATALITY_RATE NOTES
0 Азия Афганистан 33 0 1 3.0 NaN
1 Азия Индия 385 0 3 0.7 NaN
2 Азия Иран 634 625 4 0.6 NaN
3 Азия Йемен 1032481 0 2261 0.2 NaN
4 Азия Китай 14 0 0 0 NaN
5 Азия Катар 5 5 0 0 NaN
6 Азия Малайзия 2.0 0 0 0 NaN
7 Азия Непал 7 0 0 0 NaN
8 Азия ОАЭ 12 12 0 0 NaN
9 Азия Саудовская Аравия 5 5 0 0 NaN
10 Азия Сингапур 3 3 0 0 NaN
11 Азия Таиланд 8 0 0 0 NaN
12 Азия Филиппины 134 0 2 1 NaN
13 Азия Южная Корея 5 5 0 0 NaN
14 Азия Япония 7 5 0 0 NaN
15 Америка Гаити 13681 0 159 1.2 NaN
16 Америка Доминиканская Республика 122 0 4 3.3 NaN
17 Америка Канада 4 3 0 0 NaN
18 Америка США 11 9 0 0 NaN
19 Африка Все страны 179835 0 3220 1.8 нет информации о завозных случаях
20 Европа Все страны NaN 0 NaN NaN нет сообщений за 2017
21 Океания Австралия 3 3 0 0 NaN
22 Мир Все страны 1227391 675 5654 0.5 NaN
От строк с нулевыми значениями избавляются методом dropna(). Он удаляет любую строку, где есть хоть одно отсутствующее значение.
У этого метода есть аргументы:
subset = [ ]. Его значением указывают названия столбцов, где нужно искать пропуски.
Уже знакомый нам inplace.
image
cholera.dropna(subset = ['total_cases', 'deaths', 'case_fatality_rate' ], inplace = True)
print(cholera)
REGION COUNTRY TOTAL_CASES IMPORTED_CASES DEATHS CASE_FATALITY_RATE NOTES
0 Азия Афганистан 33 0 1 3.0 NaN
1 Азия Индия 385 0 3 0.7 NaN
2 Азия Иран 634 625 4 0.6 NaN
3 Азия Йемен 1032481 0 2261 0.2 NaN
4 Азия Китай 14 0 0 0 NaN
5 Азия Катар 5 5 0 0 NaN
6 Азия Малайзия 2 0 0 0 NaN
7 Азия Непал 7 0 0 0 NaN
8 Азия ОАЭ 12 12 0 0 NaN
9 Азия Саудовская Аравия 5 5 0 0 NaN
10 Азия Сингапур 3 3 0 0 NaN
11 Азия Таиланд 8 0 0 0 NaN
12 Азия Филиппины 134 0 2 1.5 NaN
13 Азия Южная Корея 5 5 0 0 NaN
14 Азия Япония 7 5 0 0 NaN
15 Америка Гаити 13681 0 159 1.2 NaN
16 Америка Доминиканская Республика 122 0 4 3.3 NaN
17 Америка Канада 4 3 0 0 NaN
18 Америка США 11 9 0 0 NaN
19 Африка Все страны 179835 0 3220 1.8 нет информации о завозных случаях
21 Океания Австралия 3 3 0 0 NaN
22 Мир Все страны 1227391 675 5654 0.5 NaN
Теперь удалим правый столбец с пропущенными значениями. Снова вызываем метод dropna(). Как и set_axis(), он имеет ещё и аргумент axis. Если этому аргументу присвоить значение 'columns', он удалит любой столбец, где есть хоть один пропуск.
image
cholera.dropna(axis = 'columns', inplace = True)
print(cholera)
REGION COUNTRY TOTAL_CASES IMPORTED_CASES DEATHS CASE_FATALITY_RATE
0 Азия Афганистан 33 0 1 3
1 Азия Индия 385 0 3 0.7
2 Азия Иран 634 625 4 0.6
3 Азия Йемен 1032481 0 2261 0.2
4 Азия Китай 14 0 0 0
5 Азия Катар 5 5 0 0
6 Азия Малайзия 2 0 0 0
7 Азия Непал 7 0 0 0
8 Азия ОАЭ 12 12 0 0
9 Азия Саудовская Аравия 5 5 0 0
10 Азия Сингапур 3 3 0 0
11 Азия Таиланд 8 0 0 0
12 Азия Филиппины 134 0 2 1.5
13 Азия Южная Корея 5 5 0 0
14 Азия Япония 7 5 0 0
15 Америка Гаити 13681 0 159 1.2
16 Америка Доминиканская Республика 122 0 4 3.3
17 Америка Канада 4 3 0 0
18 Америка США 11 9 0 0
19 Африка Все страны 179835 0 3220 1.8
21 Океания Австралия 3 3 0 0
22 Мир Все страны 1227391 675 5654 0.5
Теперь таблица готова к дальнейшему изучению: ненужные пропуски удалены, но при этом сохранены важные данные.
Пропуски в данных появляются разными путями. Например, пользователь не указал о себе какие-нибудь сведения или автоматизированная система сбора информации дала сбой. Иногда пропуски оставляют умышленно, рассчитывая на автозаполнение спецсимволами.
Давайте разберёмся с пропущенными значениями в данных для задачи Яндекс.Музыки.
TASK_1_6
Просмотрите информацию о наборе данных: воспользуйтесь методом info().
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_col.csv')
df.info()
TASK_2_6
Посчитайте количество пропущенных значений и выведите его на экран.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_col.csv')
print(df.isnull().sum())
TASK_3_6
Для решения поставленной менеджером задачи важно сохранить содержание столбца 'genre_name'. Если по какой-то причине имя исполнителя и название трека оказались упущены, а жанр композиции известен, эту строку надо сберечь.
Заполните отсутствующие значения столбца 'track_name' строкой 'unknown'.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_col.csv')
df['track_name']=df['track_name'].fillna('unknown')
TASK_4_6
Заполните отсутствующие значения столбца 'artist_name' словом unknown.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_col.csv')
df['track_name']=df['track_name'].fillna('unknown')
df['artist_name']=df['artist_name'].fillna('unknown')
TASK_5_6
Удалите пропущенные значения из столбца 'genre_name'.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_col.csv')
df['track_name']=df['track_name'].fillna('unknown')
df['artist_name']=df['artist_name'].fillna('unknown')
df.dropna(subset=['genre_name'],inplace=True)
TASK_6_6
Проверьте полученный результат. Просмотрите информацию о наборе данных: воспользуйтесь методом info().
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_col.csv')
df['track_name']=df['track_name'].fillna('unknown')
df['artist_name']=df['artist_name'].fillna('unknown')
df.dropna(subset=['genre_name'],inplace=True)
df.info()
Когда мы говорим о дубликатах (дублированных записях), то представляем себе две одинаковых строки с идентичной информацией. Действительно, это дубликаты, и мы ещё разберём, как от них избавляться.
Но бывают и менее явные дубликаты: скажем, две якобы разные категории публикаций. К примеру, «Политика» и «Политическая ситуация». При агрегировании данных эти две категории будут отражены как разные подмножества набора данных, хотя на самом деле все публикации о политике нужно объединить в одну категорию. При всех возможностях Pandas важно изучать данные глазами и обдумывать их. Иначе вас ожидает масса неприятных сюрпризов.
Помимо вредного влияния дубликатов на результат, их наличие затягивает процесс анализа. Большое количество повторов неоправданно раздувает размер таблицы, а значит, увеличивает время обработки данных.
Грубые дубликаты — повторы — обнаруживают методом duplicated(). Он возвращает Series со значением True при наличии дубликатов, и False, когда их нет.
print(df.duplicated())
Чтобы посчитать количество дубликатов в наборе данных, нужно вызвать метод sum():
print(df.duplicated().sum())
1156
Для удаления дубликатов есть метод drop_duplicates():
df.drop_duplicates(inplace = True)
При вызове метода drop_duplicates() вместе с повторяющимися строками удаляются их индексы.
Последовательность индексов нарушается: после 0 следует 2 и т.д.
Поэтому вызов drop_duplicates() соединяют в цепочку с вызовом метода reset_index(). Тогда создаётся новый DataFrame, где старые индексы превращаются в обычный столбец под названием index, а индексы всех строк снова следуют в естественном порядке.
Если же мы не хотим создавать новый столбец index, то при вызове reset_index() передаётся аргумент drop со значением True. Все индексы переписываются в порядке возрастания, без пропусков.
image
Вот такой код сохраняет в переменной df таблицу, очищенную от дубликатов, с новой индексацией.
df = df.drop_duplicates().reset_index(drop=True)
Дубликаты в названиях категорий отследить сложнее, но всё-таки можно. Для просмотра всех уникальных значений в столбце используется метод unique().
Например, в 2018 году рейтинг первой ракетки мира по версии ATP обновлялся 17 раз. Но спортсменов с самым большим в мире профессионального тенниса рейтингом гораздо меньше. Если применить ко второму столбцу метод unique(), возвращаются только три имени.
import pandas as pd
rating = ['date','name','points']
players = [
['2018.01.01', 'Рафаэль Надаль',10645],
['2018.01.08', 'Рафаэль Надаль', 10600],
['2018.01.29', 'Рафаэль Надаль', 9760],
['2018.02.19', 'Роджер Федерер', 10105],
['2018.03.05', 'Роджер Федерер', 10060],
['2018.03.19', 'Роджер Федерер', 9660],
['2018.04.02', 'Рафаэль Надаль', 8770],
['2018.06.18', 'Roger Federer', 8920],
['2018.06.25', 'Рафаэль Надаль', 8770],
['2018.07.16', 'Рафаэль Надаль', 9310],
['2018.08.13', 'Рафаэль Надаль', 10220],
['2018.08.20', 'Рафаэль Надаль', 10040],
['2018.09.10', 'Рафаэль Надаль', 8760],
['2018.10.08', 'Рафаэль Надаль', 8260],
['2018.10.15', 'Рафаэль Надаль', 7660],
['2018.11.05', 'Новак Джокович', 8045],
['2018.11.19', 'Новак Джокович', 9045]
]
tennis = pd.DataFrame(data = players, columns= rating)
print(tennis)
DATE NAME POINTS
0 2018.01.01 Рафаэль Надаль 10645
1 2018.01.08 Рафаэль Надаль 10600
2 2018.01.29 Рафаэль Надаль 9760
3 2018.02.19 Роджер Федерер 10105
4 2018.03.05 Роджер Федерер 10060
5 2018.03.19 Роджер Федерер 9660
6 2018.04.02 Рафаэль Надаль 8770
7 2018.06.18 Roger Federer 8920
8 2018.06.25 Рафаэль Надаль 8770
9 2018.07.16 Рафаэль Надаль 9310
10 2018.08.13 Рафаэль Надаль 10220
11 2018.08.20 Рафаэль Надаль 10040
12 2018.09.10 Рафаэль Надаль 8760
13 2018.10.08 Рафаэль Надаль 8260
14 2018.10.15 Рафаэль Надаль 7660
15 2018.11.05 Новак Джокович 8045
16 2018.11.19 Новак Джокович 9045
print(tennis['name'].unique())
array(['Рафаэль Надаль', 'Роджер Федерер', 'Roger Federer',
'Новак Джокович'], dtype=object)
Кроме имени Роджера Федерера на русском языке, вернулось его же имя латиницей. Это тоже дубликат. Нужно заменить этот артефакт правильным именем на русском языке.
Такие задачи решает метод replace(), где первый аргумент — текущее значение, а второй — новое, нужное.
tennis['name'] = tennis['name'].replace('Roger Federer', 'Роджер Федерер')
print(tennis)
DATE NAME POINTS
0 2018.01.01 Рафаэль Надаль 10645
1 2018.01.08 Рафаэль Надаль 10600
2 2018.01.29 Рафаэль Надаль 9760
3 2018.02.19 Роджер Федерер 10105
4 2018.03.05 Роджер Федерер 10060
5 2018.03.19 Роджер Федерер 9660
6 2018.04.02 Рафаэль Надаль 8770
7 2018.06.18 Роджер Федерер 8920
8 2018.06.25 Рафаэль Надаль 8770
9 2018.07.16 Рафаэль Надаль 9310
10 2018.08.13 Рафаэль Надаль 10220
11 2018.08.20 Рафаэль Надаль 10040
12 2018.09.10 Рафаэль Надаль 8760
13 2018.10.08 Рафаэль Надаль 8260
14 2018.10.15 Рафаэль Надаль 7660
15 2018.11.05 Новак Джокович 8045
16 2018.11.19 Новак Джокович 9045
Теперь всё хорошо. Настало время разобраться с дубликатами в таблице данных Яндекс.Музыки.
TASK_1_8
Сохраните текущий размер таблицы в переменной shape_table.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_nan.csv')
shape_table=(df.shape)
TASK_2_8
Посчитайте и выведите на экран суммарное количество дубликатов в таблице.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_nan.csv')
shape_table=(df.shape)
print(df.duplicated().sum())
TASK_3_8
Удалите дубликаты. Используйте метод reset_index() для сохранения порядка индексов.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_nan.csv')
df = df.drop_duplicates().reset_index(drop=True)
TASK_4_8
Сохраните в переменную shape_table_update размер таблицы после удаления дубликатов.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_nan.csv')
df = df.drop_duplicates().reset_index(drop=True)
shape_table_update=(df.shape)
TASK_5_8
Сравните переменные shape_table и shape_table_update. Если они равны, выведите сообщение 'Размер таблицы не изменился, текущий размер: ' и значение переменной shape_table_update.
В ином случае сообщение должно быть таким: 'Таблица уменьшилась, текущий размер: ' и значение переменной shape_table_update.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_nan.csv')
shape_table=(df.shape)
df = df.drop_duplicates().reset_index(drop=True)
shape_table_update=(df.shape)
if shape_table ==shape_table_update:
print('Размер таблицы не изменился, текущий размер:',shape_table_update)
else:
print('Таблица уменьшилась, текущий размер:',shape_table_update)
TASK_6_8
Получите уникальные значения столбца 'genre_name', используйте метод unique(). Просмотрите результат и найдите название жанра, которое выпадает из общего ряда.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_nan.csv')
print(df['genre_name'].unique())
TASK_7_8
В столбце 'genre_name' замените значение 'электроника' на 'electronic' .
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_nan.csv')
df['genre_name']=df['genre_name'].replace('электроника', 'electronic')
TASK_8_8
Оцените изменения: пересчитайте количество значений 'электроника' в столбце 'genre_name'. Если удалось всё заменить, результат должен быть равен 0. Сохраните этот результат в переменной genre_final_count, выведите на экран.
SOLUTION
import pandas as pd
df = pd.read_csv('music_log_upd_nan.csv')
df['genre_name']=df['genre_name'].replace('электроника', 'electronic')
genre_final_count=df.loc[df.loc[:,'genre_name']=='электроника']['genre_name'].count()
print(genre_final_count)
Результат работы
Мы избавились от самого вредоносного «мусора»: пропусков и повторов. При этом достаточно бережно, чтобы не пострадала самая нужная информация — время воспроизведения композиций. Наконец можно приступить к анализу данных.
Что дальше
Предстоит самая насыщенная математикой и одновременно самая творческая часть нашей работы, потому что здесь начинается настоящее исследование. Мы знаем, что именно надо посчитать, но конечный результат пока неизвестен. Какие выводы нам предстоит из него сделать? Трудно даже предположить, и в этом — интрига.
Забери с собой
Чтобы ничего не забыть, скачайте шпаргалку
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment