-
-
Save dsibi/bbf6f3d445768fda730a81ea326bd04b to your computer and use it in GitHub Desktop.
Данные исследуют в четыре стадии: | |
Получение данных и ознакомление с ними | |
Предподготовка данных | |
Анализ данных | |
Оформление результатов исследования | |
В этой теме мы начнём с первой стадии. В получении данных и ознакомлении с ними важную роль выполняет библиотека Pandas. | |
Чему вы научитесь | |
Познакомитесь с библиотекой Pandas и её базовыми методами: чтением файла, выводом данных на экран, получением сводной информации и запросом значений из определённых ячеек таблицы. | |
Сколько времени это займёт | |
2 часа = 6 уроков от 2 до 30 минут. | |
Постановка задачи | |
Решим задачу из реальной практики сервиса Яндекс.Музыка: исследовать предпочтения пользователей и ответить на вопрос, целесообразна ли интеграция двух сервисов — Музыки и Радио. |
Мы готовим к запуску новый релиз, где соединим вместе приложения Музыка и Радио. Нам важно понимать, как пользователи отреагируют на такое новшество. Для этого мы оцениваем на небольшом количестве пользователей, насколько им понравилась возможность быстрого доступа к Радио. | |
Каждая компания по-разному оценивает реакцию пользователя на нововведения. Продуктовые исследования в нашем сервисе показали, что среднее время прослушивания лучше всего отражает лояльность пользователей. | |
Метрика удовлетворенности пользователя сервисом называется happiness. В Яндекс.Музыке метрика happiness равна среднему времени прослушивания треков. Чем выше этот показатель, тем больше доволен пользователь. | |
image | |
Есть и другие метрики, которые помогают ответить на вопрос: «Как дела у нашего продукта?» | |
Если метрика happiness в эксперименте повысится, команда примет решение добавить Радио всем пользователям. | |
Ваша задача как аналитика Яндекс.Музыки определить текущее значение метрики happiness. Вы пройдёте все стадии – от знакомства с данными до решения бизнес-задачи. |
Вот так выглядят наши данные на этапе знакомства: | |
image | |
Этот хаос нужно превратить в аккуратную табличку, поддающуюся обработке. Для такой задачи подойдёт и Excel, но лучше использовать профильный инструмент — программную библиотеку Pandas. | |
Библиотеки – это наборы готовых методов для решения распространённых задач. Из того, что есть в Python, для операций с таблицами чаще всего применяют Pandas. Название — от сокращения panel data (англ. «панельные данные») — пришло из терминологии применяемого в экономике панельного анализа, который изучает изменение определённого признака у определённого объекта во времени (например, уровень бедности в Бразилии во второй половине 20 века). Библиотека Pandas оказалась таким универсальным инструментом, что годится для исследования любых данных, которые вообще можно собрать в таблицу. | |
Почему библиотека Pandas такая крутая и популярная? У неё богатейшие возможности: | |
Готовые методы для всяческих манипуляций с таблицами: добавления, удаления, преобразования, агрегирования данных; | |
Одновременная обработка данных из разных файлов; | |
Готовые методы для операций с пропущенными значениями, выявления и устранения проблемных данных; | |
Использование данных в самых разных форматах. | |
Кроме того, вы всегда можете обратиться к хорошо подготовленной документации и активному комьюнити. | |
Инструменты библиотеки становятся доступны, когда мы вызываем её командой import. | |
import pandas | |
Библиотека хранится в переменной, через которую можно вызвать её методы. В сообществе принято давать ей короткое имя pd. | |
import pandas as pd | |
Эта команда означает «импортируй библиотеку Pandas как pd». | |
У нас есть набор данных, который нужно превратить в таблицу. Это делается вызовом конструктора DataFrame(). | |
Конструктор принимает два аргумента – список данных и названия столбцов, которые должны быть в таблице. Например, если информация о столицах разных стран хранится в переменной atlas: | |
atlas = [ | |
['Франция','Париж'], | |
['Россия','Москва'], | |
['Китай','Пекин'], | |
['Мексика','Мехико'], | |
['Египет','Каир'] | |
] | |
и нужно построить таблицу из двух столбцов country и capital, | |
geography = ['country', 'capital'] | |
синтаксис вызова конструктора DataFrame() выглядит так: | |
world_map = pd.DataFrame(data = atlas , columns = geography) | |
image | |
Обратите внимание, что DataFrame() – это конструктор библиотеки Pandas, поэтому перед именем конструктора стоит обращение к переменной, в которой библиотека хранится – pd.DataFrame(). | |
atlas = [ | |
['Франция','Париж'], | |
['Россия','Москва'], | |
['Китай','Пекин'], | |
['Мексика','Мехико'], | |
['Египет','Каир'], | |
] | |
geography = ['country', 'capital'] | |
world_map = pd.DataFrame(data = atlas , columns = geography) # таблица сохраняется в переменной с произвольно выбранным именем world_map | |
print(world_map) # вывод на экран | |
COUNTRY CAPITAL | |
0 Франция Париж | |
1 Россия Москва | |
2 Китай Пекин | |
3 Мексика Мехико | |
4 Египет Каир | |
В результате простой список пар страна-столица превратился в таблицу с индексами и именованными столбцами. Давайте создадим таблицу с данными о ваших музыкальных предпочтениях. | |
TASK_1_4 | |
Получите доступ к библиотеке Pandas, используйте имя переменной pd. | |
SOLUTION | |
import pandas as pd | |
TASK_2_4 | |
Создайте список music с 5 парами «имя вашего любимого исполнителя - название его песни». Пример такого списка - atlas из теоретического введения к этому уроку. | |
import pandas as pd | |
SOLUTION | |
import pandas as pd | |
music=[['Меладзе','Она была'], | |
['Билан','Тоска'], | |
['Пресняков','Стюардесса'], | |
['Тальков','Чистые пруды'], | |
['Розенбаум','Брат мой'], | |
] | |
TASK_3_4 | |
Создайте список entries с названиями для двух столбцов — artist и track (здесь эти английские слова употребляются в значении «исполнитель» и «композиция»). | |
SOLUTION | |
import pandas as pd | |
music=[['Меладзе','Она была'], | |
['Билан','Тоска'], | |
['Пресняков','Стюардесса'], | |
['Тальков','Чистые пруды'], | |
['Розенбаум','Брат мой'], | |
] | |
entries=['artist','track'] | |
TASK_4_4 | |
Используя конструктор DataFrame(), создайте таблицу из списка ваших любимых исполнителей music и списка столбцов entries. Сохраните таблицу в переменной playlist и выведите эту сборную таблицу на экран. | |
SOLUTION | |
import pandas as pd | |
music=[['Меладзе','Она была'], | |
['Билан','Тоска'], | |
['Пресняков','Стюардесса'], | |
['Тальков','Чистые пруды'], | |
['Розенбаум','Брат мой'], | |
] | |
entries=['artist','track'] | |
playlist=pd.DataFrame(data=music, columns=entries) | |
print (playlist) |
У вас есть набор данных. Чтобы начать работу с ним, данные нужно прочитать. Давайте разберёмся, как это сделать. | |
Знакомому формату электронной таблицы Excel в Pandas соответствует структура данных DataFrame. Аналитики обычно называют такие объекты просто DataFrame. | |
В рабочей практике вы столкнётесь с тем, что данные хранят в файлах разных форматов. Из них самый распространённый – CSV (от англ. Comma-Separated Values, «значения, разделённые запятой»). Каждая строка такого файла представляет собой одну строку таблицы, где данные разделены запятыми. В первой строке собраны заголовки столбцов (если они есть). | |
Посмотрите, как одинаковые данные выглядят в Excel (снизу) и CSV (сверху): | |
image | |
Файлы CSV удобнее всего открывать вызовом метода read_csv() из библиотеки Pandas. | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') # аргумент - путь к файлу | |
Обратите внимание, что содержимое файла CSV сохраняется в переменной df. Это имя, которое будет встречаться постоянно — общепринятое сокращение от DataFrame. | |
Теперь все данные из файла можно напечатать на экране командой print(df), но это не всегда нужно делать — не исключено, что таблица огромна и неудобна для изучения. Для знакомства с данными запрашивают несколько строк из начала или конца таблицы, вызывая специальные методы head() и tail(). По умолчанию head() возвращает первые 5 строк набора данных, а метод tail() – последние 5 строк. Когда нужно не 5, количество строк передаётся этим методам как аргумент. Например, head(10) вернёт первые 10 строк. Давайте возьмёмся за поставленную менеджером задачу и откроем файл с данными сервиса Яндекс.Музыка. Получим первый десяток строк этой обширной таблицы: | |
print(df.head(10)) | |
USER_ID TOTAL PLAY ARTIST GENRE TRACK | |
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 | |
TASK_1_2 | |
Прочитайте файл music_log.csv и сохраните его в переменной df. Сохраните первые 5 строк с данными из music_log.csv в переменной music_head и выведите значение переменной на экран. | |
SOLUTION | |
# <импортируйте библиотеку pandas> | |
import pandas as pd | |
df=pd.read_csv('music_log.csv') | |
music_head = df[:5] | |
print(music_head.head()) | |
TASK_2_2 | |
Прочитайте файл music_log.csv и сохраните его в переменной df. Сохраните последние 10 строк с данными из music_log.csv в переменной music_tail и выведите значение переменной на экран. | |
SOLUTION | |
import pandas as pd | |
df=pd.read_csv('music_log.csv') | |
music_tail = df[-10:] | |
print(music_tail.tail(10)) |
Таблица, которую мы получили, хранится в структуре данных DataFrame. Давайте подробно разберём, из чего состоит этот объект и какие операции с ним можно выполнять. | |
image | |
DataFrame — это двумерная структура данных Pandas, где у каждого элемента есть две координаты: по строке и по столбцу. | |
image | |
Вы видите две оси, которые формируют объект DataFrame. Первая ось называется индексы, вторая ось — столбцы. По умолчанию индексация в DataFrame начинается с нуля. | |
Каждая строка — это одно наблюдение, запись об объекте исследования. А столбцы — признаки объектов. В нашем случае одна строка — это одно действие одного пользователя. Прослушивание такой-то композиции в исполнении такой-то группы в течение такого-то времени. | |
Для лучшего понимания данных полезно получить доступ к их описанию. Это либо документация со сведениями о содержании каждого столбца, либо – не самый лучший вариант – рассказ человека, который предоставил вам эту информацию. Сейчас документация выглядит так: | |
user_id — содержит информацию об уникальном идентификаторе пользователя; | |
total play — сколько секунд пользователь слушал трек; | |
Artist — имя исполнителя; | |
genre — жанр (рок, поп, электронная музыка, классическая и др.); | |
track — название трека. | |
Такое описание поможет нам ставить себе корректные задачи. | |
У DataFrame есть неотъемлемые свойства, значения которых можно запросить. Они называются атрибуты. Например, атрибут columns содержит информацию о названиях столбцов в наборе данных. | |
print(df.columns) | |
image | |
В данном случае атрибут columns вернул список названий столбцов и сообщил, что каждое из них имеет тип данных object. | |
Вообще типы данных могут быть разные. Для просмотра типа данных каждого столбца лучше всего использовать атрибут dtypes. | |
print(df.dtypes) | |
image | |
Типы данных, о которых сообщают нам атрибуты — это типы данных библиотеки Pandas. Каждому из них соответствует определённый тип данных языка Python. | |
Так, для int таким «двойником» в Pandas будет int64. Тип данных object используется, когда данные не подходят ни под одну категорию или соответствуют в Python типу «строка». Вот таблица соответствия типов данных Pandas и Python: | |
PANDAS DTYPE PYTHON TYPE ЗНАЧЕНИЕ | |
object str Строка | |
int64 int Целые числа | |
float64 float Вещественные числа | |
bool bool Логический тип данных | |
О размерах таблицы с данными сообщает её атрибут shape. В результате получается кортеж (неизменяемый список) из двух чисел: первое – количество строк, второе – количество столбцов. | |
print(df.shape) | |
image | |
В таблице 67963 строк (наблюдений) и 5 столбцов. | |
Кортеж – одномерная неизменяемая последовательность. Это структура данных, похожая на список, её тоже можно сохранять в переменной. Например, кортеж artist содержит имена исполнителей: | |
artist = ('Queen', 'Led Zeppelin', 'Scorpions') | |
Но если мы попытаемся изменить элемент кортежа, то Python вернёт ошибку: | |
artist[0] = 'Spice Girls' | |
--------------------------------------------------------------------------- | |
TypeError Traceback (most recent call last) | |
<ipython-input-25-4409a5f0dbe8> in <module>() | |
----> 1 artist[0] = 'Spice Girls' | |
TypeError: 'tuple' object does not support item assignment | |
Можно получить информацию как обо всём кортеже, так и об отдельных его элементах: | |
print(artist) | |
('Queen', 'Led Zeppelin', 'Scorpions') | |
print(artist[0]) | |
'Queen' | |
Кортеж нужен для хранения и чтения данных, которые лучше не изменять. Он похож на текстовый документ, защищённый от редактирования. | |
Всю информацию, которую предоставляют разные атрибуты DataFrame, можно получить вызовом одного-единственного метода info(). Изучив результаты, которые этот метод возвращает, аналитик выбирает тактику дальнейшей работы с таблицей. | |
df.info() | |
<class 'pandas.core.frame.DataFrame'> | |
RangeIndex: 67963 entries, 0 to 67962 | |
Data columns (total 5 columns): | |
user_id 67963 non-null object | |
total play 67963 non-null float64 | |
Artist 60157 non-null object | |
genre 65223 non-null object | |
track 65368 non-null object | |
dtypes: float64(1), object(4) | |
memory usage: 2.6+ MB | |
image | |
Например, здесь в разных столбцах разное количество элементов с определёнными значениями (non-null). Следовательно, в таблице есть пропущенные значения (null). Прежде чем анализировать такие данные, их нужно обработать. Это одна из самых интересных задач аналитика, и мы поговорим о ней подробнее в следующей теме. | |
TASK_1_4 | |
Прочитайте файл music_log.csv и сохраните его в переменной df. Создайте переменную shape_table и сохраните в ней размеры таблицы music_log.csv. Напечатайте на экране размер таблицы в таком виде: | |
Размер таблицы: ... | |
SOLUTION | |
import pandas as pd | |
df=pd.read_csv('music_log.csv') | |
shape_table=df.shape | |
print('Размер таблицы:', shape_table) | |
TASK_2_4 | |
Сколько наблюдений в наборе данных? В переменной shape_table хранится кортеж. Его первый элемент — количество наблюдений, который надо сохранить в переменной observations_table (не забывайте, что индексация элементов идёт с 0). Напечатайте на экране ответ в таком виде: | |
Количество наблюдений: ... | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
shape_table = df.shape | |
observations_table = shape_table [0] | |
print('Количество наблюдений:', observations_table) | |
TASK_3_4 | |
Найдите в информации, которую вернул метод info(), число наблюдений. Вручную присвойте это число как значение переменной observations_info_table. | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
df.info() | |
observations_info_table = 67963 | |
TASK_4_4 | |
Вы ещё не запутались? Давайте осмотримся и заодно вспомним условные конструкции. | |
Поскольку в ходе работы аналитик объявляет разные переменные и сохраняет в них добытую разными способами информацию, запутаться очень легко. Именно поэтому необходимо проверять себя и текущие результаты. Сравните полученные результаты в переменных observations_info_table и observations_table. Если значения переменных совпадают, то выведите количество наблюдений и сообщение: | |
"Решение верно, количество наблюдений равно", observations_table | |
Если значения переменных не совпадают, то выведите сообщение: | |
"Решение неверно, проверьте ещё раз!" | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
observations_info_table = df.shape[0] | |
observations_table = 67963 | |
if observations_info_table == observations_table: | |
print("Решение верно, количество наблюдений равно", observations_table) | |
else: | |
print("Решение неверно, проверьте ещё раз!", observations_table) |
Сейчас мы, наконец, попробуем получить данные, которые понадобятся непосредственно для выполнения задачи, поставленной менеджером Яндекс.Музыки. Для этого надо уметь запрашивать информацию из определённых ячеек таблицы. | |
К каждой ячейке с данными в DataFrame можно обратиться по её индексу и названию столбца. Мы можем получать различные срезы данных в зависимости от того, какой запрос к DataFrame мы сформулируем. Этот процесс называется индексация. Для DataFrame она проводится разными способами. | |
Атрибут loc[строка, столбец] даёт доступ к элементу по строке и столбцу. | |
image | |
ВИД РЕАЛИЗАЦИЯ | |
Одна ячейка .loc[7, 'genre'] | |
Один столбец .loc[:, 'genre'] | |
Несколько столбцов .loc[:, ['genre', 'Artist']] | |
Несколько столбцов подряд (срез) .loc[:, 'user_id': 'genre'] | |
Одна строка .loc[1] | |
Все строки, начиная с заданной .loc[1:] | |
Все строки до заданной .loc[:3] | |
Несколько строк подряд (срез) .loc[2:5] | |
image image | |
Также вы могли заметить, что запрос к атрибуту loc[] использует квадратные скобки, это напоминает списки в Python. Индексация здесь очень похожа на индексацию списков. | |
Вспомните, как вы играли в «Морской бой» на уроках. Для победы недостаточно просто стрелять куда попало — нужно обдумывать ситуацию на поле противника, чтобы бить в цель как можно точнее. | |
Посмотрите на поле для игры, оно подобно DataFrame: есть столбцы с буквенными обозначениями и ось индексов. Обратите внимание на разницу в индексах — в DataFrame они от 0 до 9. | |
image | |
import pandas as pd | |
data = [[0,0,0,0,0,0,0,0,0,0], | |
[0,'-','-','-',0,0,0,0,0,0], | |
[0,'-','X','-',0,0,'X','X','X','X'], | |
[0,'-','X','-',0,0,0,0,0,0], | |
[0,'-','-','-',0,0,0,0,0,0], | |
[0,0,'-',0,0,0,0,0,'X',0], | |
[0,'-','X','X',0,0,0,0,0,0], | |
[0,0,'-','-',0,0,0,0,0,0], | |
[0,0,0,0,'-','X',0,0,0,0], | |
[0,0,0,0,0,0,0,0,0,0]] | |
columns = ['А','Б','В','Г','Д','Е','Ж','З','И','К'] | |
battle = pd.DataFrame(data = data, columns = columns) | |
print(battle) | |
А Б В Г Д Е Ж З И К | |
0 0 0 0 0 0 0 0 0 0 0 | |
1 0 - - - 0 0 0 0 0 0 | |
2 0 - X - 0 0 X X X X | |
3 0 - X - 0 0 0 0 0 0 | |
4 0 - - - 0 0 0 0 0 0 | |
5 0 0 - 0 0 0 0 0 X 0 | |
6 0 - X X 0 0 0 0 0 0 | |
7 0 0 - - 0 0 0 0 0 0 | |
8 0 0 0 0 - X 0 0 0 0 | |
9 0 0 0 0 0 0 0 0 0 0 | |
Ячейки DataFrame со значением 0 обозначают пустую клетку. Информация в ячейке меняется на X, если игрок выстрелил и попал в корабль противника, а если не попал, то меняется на прочерк. | |
Давайте исследуем текущую ситуацию на поле. Просмотрим столбец 'В' и убедимся, что там уже началась атака на корабль: | |
print(battle.loc[:,'В']) | |
0 0 | |
1 - | |
2 X | |
3 X | |
4 - | |
5 - | |
6 X | |
7 - | |
8 0 | |
9 0 | |
Name: В, dtype: object | |
Действительно, атака уже началась в 6-ой строке, но корабль ещё не убит, а выстрелы в ячейки сверху и снизу кончились промахом. Просмотрим 6-ую строку и узнаем расположение корабля. | |
print(battle.loc[6]) | |
А 0 | |
Б - | |
В X | |
Г X | |
Д 0 | |
Е 0 | |
Ж 0 | |
З 0 | |
И 0 | |
К 0 | |
Name: 6, dtype: object | |
Корабль ориентирован по горизонтали, по нему произведено два удачных выстрела, которые пришлись на столбцы В и Г. Нужно оценить ситуацию вокруг корабля. Для этого просмотрим строки с 5 по 7. | |
print(battle.loc[5:7]) | |
А Б В Г Д Е Ж З И К | |
5 0 0 - 0 0 0 0 0 X 0 | |
6 0 - X X 0 0 0 0 0 0 | |
7 0 0 - - 0 0 0 0 0 0 | |
Очевидно, что следующий выстрел нужно сделать по координате 6Д. Ура, корабль убит! | |
Теперь мы хотим найти второй трёхпалубный корабль. Оценим, где вероятнее всего он может располагаться. Для этого просмотрим две половины игрового поля. Срез из столбцов от А до Д даст возможность вывести левую часть таблицы: | |
print(battle.loc[:,'А':'Д']) | |
А Б В Г Д | |
0 0 0 0 0 0 | |
1 0 - - - 0 | |
2 0 - X - 0 | |
3 0 - X - 0 | |
4 0 - - - 0 | |
5 0 0 - 0 0 | |
6 0 - X X 0 | |
7 0 0 - - 0 | |
8 0 0 0 0 - | |
9 0 0 0 0 0 | |
Такая же операция для столбцов от Е до К покажет правую часть таблицы: | |
print(battle.loc[:,'Е':'К']) | |
Е Ж З И К | |
0 0 0 0 0 0 | |
1 0 0 0 0 0 | |
2 0 X X X X | |
3 0 0 0 0 0 | |
4 0 0 0 0 0 | |
5 0 0 0 X 0 | |
6 0 0 0 0 0 | |
7 0 0 0 0 0 | |
8 X 0 0 0 0 | |
9 0 0 0 0 0 | |
Важное замечание: когда мы используем срезы в списках, то конец среза не включается в результат. А вот атрибут .loc[] тем и выделяется, что включает и начало, и конец среза. | |
Например, есть список исполнителей: | |
artist = ['Marina Rei', 'Stive Morgan','Rixton','Henry Hall & His Gleneagles Hotel Band', 'Andrew Paul Woodworth', 'Pillar Point','Steve Campbell','David Civera','Lumipa Beats', 'Henning Wehland'] | |
Элементы с 2 по 4 получают запросом: | |
print(artist[2:5]) | |
['Rixton', 'Henry Hall & His Gleneagles Hotel Band', 'Andrew Paul Woodworth'] | |
Последним в запросе указан индекс 5 — именно для того, чтобы в срез попал элемент с индексом 4. Запрос на получение со 2 по 4 строки в таблице будет выглядеть вот так: | |
print(df.loc[2:4]) | |
USER_ID TOTAL PLAY ARTIST GENRE TRACK | |
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 | |
Итак, вы видели, как запрашивать один столбец, одну строку, диапазон столбцов и диапазон строк. Это самые ходовые запросы, которые вам предстоит делать как аналитику данных. | |
На практике чаще применяют сокращённую форму записи для индексации. Но возможности у неё ограничены. Имейте в виду, что она не всегда возвращает те же результаты, что атрибут .loc[] в его полном варианте. | |
ВИД РЕАЛИЗАЦИЯ СОКРАЩЁННАЯ ЗАПИСЬ | |
Одна ячейка .loc[7, 'genre'] - | |
Один столбец .loc[:, 'genre'] df['genre'] | |
Несколько столбцов .loc[:, ['genre', 'Artist']] df [['genre', 'Artist']] | |
Несколько столбцов подряд (срез) .loc[:, 'user_id': 'genre'] - | |
Одна строка .loc[1] - | |
Все строки, начиная с заданной .loc[1:] df[1:] | |
Все строки до заданной .loc[:3] включая 3 df[:3] не включая 3 | |
Несколько строк подряд (срез) .loc[2:5]включая 5 df[2:5] не включая 5 | |
Неприятельский трёхпалубный корабль, скорее всего, на правой половине поля. Это можно предположить, оглядев по очереди оба среза. Аналитики называют такой метод изучения «посмотреть на данные глазами». | |
На глаз хорошо выбирать направление дальнейших поисков, но так не получишь точных цифр, которые можно включить в отчёт. Надо уметь подсчитать количество определённых значений, например, точных попаданий. В Pandas для этого есть метод count(). | |
Его вызывают и приказывают сосчитать, например, количество ячеек столбца В, где были попадания. Удачный выстрел — это значение "X" в ячейке. Для столбца В таблицы battle такие ячейки отвечают логическому условию battle.loc[:,'В'] == 'X'. Поскольку в указании, какие именно значения считать, нужен логический оператор, такой доступ к значению ячейки называют логическая индексация. | |
image | |
print(battle.loc[battle.loc[:,'В'] == 'X']['В'].count()) # используем метод .count() для подсчёта записей, удовлетворяющих условию в столбце В | |
3 | |
ВИД РЕАЛИЗАЦИЯ СОКРАЩЁННАЯ ЗАПИСЬ | |
Все строки, удовлетворяющие условию battle.loc[battle.loc[:,'В'] == 'X'] battle[battle['В'] == 'X'] | |
Столбец, удовлетворяющий условию battle.loc[battle.loc[:,'В'] == 'X']['В'] battle[battle['В'] == 'X']['В'] | |
Применение метода battle.loc[battle.loc[:,'В'] == 'X']['В'].count() battle[battle['В'] == 'X']['В'].count() | |
Конечно, писать вызов метода count() для подсчёта попаданий в «морском бою» то же, что стрелять из пушки по воробьям. Но в анализе таблиц на много тысяч строк счётный метод — мощное орудие. Попробуйте в задаче с данными Яндекс.Музыки определить, какой жанр оказался популярнее у пользователей: поп или рок? | |
TASK_1_4 | |
Получите таблицу, состоящую из столбцов genre и Artist. Сохраните её в переменной genre_fight. | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
genre_fight=df.loc[:,['genre', 'Artist']] | |
TASK_2_4 | |
Посчитайте число прослушанных треков в жанре поп. Для этого лучше всего использовать логическое условие genre_fight['genre'] == 'pop'. Сохраните результат в переменной genre_pop. Напечатайте ответ на экране в таком виде: | |
Число прослушанных треков в жанре поп равно ... | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
genre_fight=df.loc[:,['genre', 'Artist']] | |
genre_pop=genre_fight.loc[genre_fight.loc[:,'genre'] == 'pop']['genre'].count() | |
print('Число прослушанных треков в жанре поп равно', genre_pop) | |
TASK_3_4 | |
Теперь посчитайте число прослушанных треков в жанре рок. Допишите в код подсчёт, похожий на предыдущий, только с логическим условием df['genre'] == 'rock'. Сохраните результат в переменной genre_rock. Напечатайте ответ на экране в таком виде: | |
Число прослушанных треков в жанре поп равно ... Число прослушанных треков в жанре рок равно ... | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
genre_fight=df.loc[:,['genre', 'Artist']] | |
genre_pop=genre_fight.loc[genre_fight.loc[:,'genre'] == 'pop']['genre'].count() | |
genre_rock=genre_fight.loc[genre_fight.loc[:,'genre'] == 'rock']['genre'].count() | |
print('Число прослушанных треков в жанре поп равно', genre_pop) | |
print('Число прослушанных треков в жанре рок равно', genre_rock) | |
TASK_4_4 | |
Напишите условную конструкцию, которая сравнивает полученные значения и выводит информацию о победителе в этом бою! Если победил жанр рок, то выведите сообщение "Рок победил!", а если победил жанр поп - сообщение "Попса forever!" | |
Не удаляйте вывод числа прослушанных треков в жанрах поп и рок. | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
genre_fight=df.loc[:,['genre', 'Artist']] | |
genre_pop=genre_fight.loc[genre_fight.loc[:,'genre'] == 'pop']['genre'].count() | |
genre_rock=genre_fight.loc[genre_fight.loc[:,'genre'] == 'rock']['genre'].count() | |
print('Число прослушанных треков в жанре поп равно', genre_pop) | |
print('Число прослушанных треков в жанре рок равно', genre_rock) | |
if genre_pop>genre_rock: | |
print('Попса forever!') | |
else: | |
print('Рок победил!') |
В таблице, которую мы рассматривали весь прошлый урок, каждый столбец сам по себе — вовсе не структура данных DataFrame. Удивлены? Давайте проверим. | |
Таблица всё так же имеет тип DataFrame. | |
image | |
Но если мы возьмём отдельный столбец таблицы, то он представляет собой совсем иную структуру данных — Series. | |
image | |
Давайте разберёмся, что интересного в новом объекте Series. | |
Series — одномерная таблица, и её элементы можно получить по индексу. Каждый индекс — это номер отдельного наблюдения, и поэтому несколько различных Series вместе составляют DataFrame. В Series хранятся данные одного типа. | |
image | |
У каждой Series есть имя (Name), информация о количестве данных в столбце (Length) и тип данных, которые хранятся в ней (dtype). | |
print(df['Artist']) | |
0 Marina Rei | |
1 Stive Morgan | |
2 Stive Morgan | |
3 NaN | |
4 Rixton | |
5 Henry Hall & His Gleneagles Hotel Band | |
6 NaN | |
7 NaN | |
8 Andrew Paul Woodworth | |
9 Pillar Point | |
10 Steve Campbell | |
11 David Civera | |
12 Lumipa Beats | |
13 Henning Wehland | |
14 NaN | |
15 Пётр Каледин | |
16 ChinKong | |
... | |
67951 Julien Mier | |
67952 Bierstrassen Cowboys | |
67953 Flip Grater | |
67954 Alt & J | |
67955 TKN | |
67956 89ers | |
67957 Steel Pulse | |
67958 Nadine Coyle | |
67959 Digital Hero | |
67960 Red God | |
67961 Less Chapell | |
67962 NaN | |
Name: Artist, Length: 67963, dtype: object | |
Не все данные в этом столбце Artist относятся к типу object — есть и пропущенные значения NaN. О них мы подробно расскажем в теме, посвящённой «информационному мусору». Вместе с ними длина столбца равна общему числу наблюдений 67963. | |
Индексация в Series аналогична индексации элементов столбца в DataFrame. Давайте рассмотрим на примере. Сохраним столбец total play в переменной total_play. | |
total_play = df.loc[:, 'total play'] | |
Для получения пятого по индексу элемента укажем 5 в квадратных скобках. | |
print(total_play[5]) | |
3.007 | |
Если надо получить диапазон ячеек, запросите атрибут loc с границами среза в квадратных скобках. | |
print(total_play.loc[5:10]) | |
5 3.007 | |
6 0.100 | |
7 211.880 | |
8 3.161 | |
9 274.390 | |
10 8.836 | |
Name: total play, dtype: float64 | |
Вот таблица различных вариантов индексации в Series с сокращённой записью. | |
ВИД РЕАЛИЗАЦИЯ СОКРАЩЁННАЯ ЗАПИСЬ | |
Один элемент total_play.loc[7] total_play[7] | |
Несколько элементов total_play.loc[[5, 7, 10]] total_play[[5, 7, 10]] | |
Несколько элементов подряд (срез) total_play.loc[5:10] включая 10 total_play[5:10] не включая 10 | |
Все элементы, начиная с заданного total_play.loc[1:] total_play[1:] | |
Все элементы до заданного total_play.loc[:3] включая 3 total_play[:3] не включая 3 | |
Для Series также возможна логическая индексация. Рассмотрим такой пример — пользователю может не понравиться песня, которая начала играть, и он нажмёт кнопку «Следующий трек». Тогда в таблице сохраняется очень малое время прослушивания — от нуля до нескольких секунд. Вы можете проверить, сколько пользователей в течение нескольких секунд — не более 10 — приняли решение пропустить песню, которая только началась. | |
Для решения задачи воспользуемся логическим условием total_play <= 10: | |
image | |
print(total_play.loc[total_play <= 10]) # выведем все элементы, которые удовлетворяют условию | |
3 8.966000 | |
5 3.007000 | |
6 0.100000 | |
8 3.161000 | |
10 8.836000 | |
11 0.000000 | |
13 2.000000 | |
14 0.000000 | |
15 0.000000 | |
18 0.100000 | |
19 7.000000 | |
24 8.109446 | |
26 0.000000 | |
31 3.253000 | |
33 0.100000 | |
34 0.000000 | |
36 9.200000 | |
39 3.663453 | |
40 0.100000 | |
41 0.100000 | |
42 1.464589 | |
45 0.100000 | |
47 0.100000 | |
49 6.000000 | |
51 0.872000 | |
55 8.443000 | |
63 1.687000 | |
64 3.773000 | |
66 7.670000 | |
68 0.000000 | |
... | |
67878 0.919000 | |
67880 1.090000 | |
67885 2.000000 | |
67886 4.040000 | |
67887 7.680563 | |
67888 8.578000 | |
67894 2.065000 | |
67896 0.000000 | |
67897 0.100000 | |
67899 3.676000 | |
67900 0.000000 | |
67905 0.000000 | |
67906 3.682000 | |
67907 4.179592 | |
67908 0.000000 | |
67912 0.000000 | |
67916 6.947000 | |
67917 4.000000 | |
67918 0.000000 | |
67923 1.753000 | |
67927 1.954282 | |
67929 0.100000 | |
67932 1.439000 | |
67946 2.101000 | |
67949 0.100000 | |
67950 3.496000 | |
67951 1.318000 | |
67953 2.502000 | |
67956 2.000000 | |
67962 0.100000 | |
Name: total play, Length: 29160, dtype: float64 | |
print(total_play.loc[total_play <= 10].count()) # посчитаем общее количество элементов, которые удовлетворяют условиям | |
29160 | |
Порог в 10 секунд мы выбрали произвольно. Но было бы интересно установить, существует ли на самом деле какое-нибудь пороговое время — длительность воспроизведения композиции, после которого пользователь чаще всего не пропускает трек, а слушает его до конца. Анализ данных позволяет ставить такие задачи и находить на них ответы. | |
Давайте вернёмся к войне между роком и попсой! Вам предлагается установить, сколько композиций этих жанров слушали не более 5 секунд и определить: зависит ли быстрота принятия решения о пропуске трека от жанра или на это влияют другие причины? | |
TASK_1_7 | |
Получите таблицу только с жанром rock и сохраните её в переменной rock. | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
df['genre'] == 'rock' | |
rock = df[df['genre'] == 'rock'] | |
TASK_2_7 | |
Выделим время прослушивания роковых композиций в особую структуру данных. Сохраните столбец 'total play' таблицы rock в переменной rock_time. | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
df['genre'] == 'rock' | |
rock = df[df['genre'] == 'rock'] | |
# Выделим время прослушивания роковых композиций в особую структуру данных | |
# Сохраним столбец 'total play' таблицы rock в переменной rock_time. | |
rock_time = rock['total play'] | |
TASK_3_7 | |
Обратитесь к новой Series c именем rock_time и посчитайте количество треков жанра рок, пропущенных в течение 5 секунд. Логическим условием укажите rock_time <= 5. Результат сохраните в переменной rock_haters и напечатайте на экране с пояснением: | |
Количество пропущенных треков жанра рок равно ... | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
df['genre'] == 'rock' | |
rock = df[df['genre'] == 'rock'] | |
# Выделим время прослушивания роковых композиций в особую структуру данных | |
# Сохраним столбец 'total play' таблицы rock в переменной rock_time. | |
rock_time = rock['total play'] | |
rock_haters=rock_time.loc[rock_time <= 5].count() | |
print('Количество пропущенных треков жанра рок равно',rock_haters) | |
TASK_4_7 | |
Выберите из исходной таблицы только строки с жанром 'pop' и сохраните эту новую таблицу в переменной pop. | |
Вывод результата предыдущей задачи закомментируйте. | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
#df['genre'] == 'rock' | |
#rock = df[df['genre'] == 'rock'] | |
# Выделим время прослушивания роковых композиций в особую структуру данных | |
# Сохраним столбец 'total play' таблицы rock в переменной rock_time. | |
#rock_time = rock['total play'] | |
#rock_haters=rock_time.loc[rock_time <= 5].count() | |
#print('Количество пропущенных треков жанра рок равно',rock_haters) | |
df['genre'] == 'pop' | |
pop = df[df['genre'] == 'pop'] | |
TASK_5_7 | |
Теперь по аналогии с роком создайте Series, где хранятся только данные о времени воспроизведения композиций в жанре поп. Назовите его pop_time и сохраните в нём данные столбца 'total play' из таблицы pop . | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
#df['genre'] == 'rock' | |
#rock = df[df['genre'] == 'rock'] | |
# Выделим время прослушивания роковых композиций в особую структуру данных | |
# Сохраним столбец 'total play' таблицы rock в переменной rock_time. | |
#rock_time = rock['total play'] | |
#rock_haters=rock_time.loc[rock_time <= 5].count() | |
#print('Количество пропущенных треков жанра рок равно',rock_haters) | |
df['genre'] == 'pop' | |
pop = df[df['genre'] == 'pop'] | |
pop_time = pop['total play'] | |
TASK_6_7 | |
Снова по аналогии с роком обратитесь к Series, на сей раз pop_time, чтобы посчитать количество пропущенных в течение 5 секунд треков жанра поп. Используйте условие pop_time <= 5. Результат сохраните в переменной pop_haters и напечатайте на экране в таком виде: | |
Количество пропущенных треков жанра поп равно ... | |
Вывод данных о роке закомментируйте. | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
#df['genre'] == 'rock' | |
#rock = df[df['genre'] == 'rock'] | |
# Выделим время прослушивания роковых композиций в особую структуру данных | |
# Сохраним столбец 'total play' таблицы rock в переменной rock_time. | |
#rock_time = rock['total play'] | |
#rock_haters=rock_time.loc[rock_time <= 5].count() | |
#print('Количество пропущенных треков жанра рок равно',rock_haters) | |
df['genre'] == 'pop' | |
pop = df.loc[df['genre'] == 'pop'] | |
pop_time = pop['total play'] | |
pop_haters=pop_time.loc[pop_time <= 5].count() | |
print('Количество пропущенных треков жанра поп равно',pop_haters) | |
TASK_7_7 | |
Для обоих жанров посчитайте долю быстро пропущенных пользователями композиций в процентах. Разделите количество треков, которые пользователи пропустили — соответственно rock_haters и pop_haters — на общее количество треков жанра рок и жанра поп. | |
Общее количество треков жанра равно количеству наблюдений в таблицах rock и pop, т.е. значению атрибута shape[0] этих таблиц. | |
Результаты сохраните в переменных rock_skip и pop_skip. Выведите значения новых переменных в процентах с точностью до одного знака после запятой в такой форме: | |
Доля пропущенных композиций жанра рок равна: ... | |
Доля пропущенных композиций жанра поп равна: ... | |
Вывод результата предыдущей задачи закомментируйте. | |
SOLUTION | |
import pandas as pd | |
df = pd.read_csv('music_log.csv') | |
# Получим таблицу только с жанром rock и сохраним её в переменной rock | |
df['genre'] == 'rock' | |
rock = df[df['genre'] == 'rock'] | |
# Выделим время прослушивания роковых композиций в особую структуру данных | |
# Сохраним столбец 'total play' таблицы rock в переменной rock_time. | |
rock_time = rock['total play'] | |
# Обратитмся к новой Series c именем rock_time и посчитайте количество треков жанра рок, пропущенных в течение 5 секунд. | |
rock_haters = rock_time[rock_time <= 5].count() | |
# print('Количество пропущенных треков жанра рок равно', rock_haters) | |
# Выберем из исходной таблицы только строки с жанром 'pop' и сохраним эту новую таблицу в переменной pop. | |
df['genre'] == 'pop' | |
pop = df[df['genre'] == 'pop'] | |
# Теперь по аналогии с роком создайте Series, где хранятся только данные о времени воспроизведения композиций в жанре поп. | |
pop_time = pop['total play'] | |
#Снова по аналогии с роком обратимся к Series, на сей раз pop_time, чтобы посчитать количество пропущенных в течение 5 секунд треков жанра поп. | |
pop_haters = pop_time[pop_time <= 5].count() | |
# print('Количество пропущенных треков жанра поп равно', pop_haters) | |
# Для обоих жанров посчитайте долю быстро пропущенных пользователями композиций в процентах. | |
df['genre'] == 'rock' | |
rock = df[df['genre'] == 'rock'] | |
rock_time = rock['total play'] | |
rock_haters = rock_time[rock_time <= 5].count() | |
df['genre'] == 'pop' | |
pop = df[df['genre'] == 'pop'] | |
pop_time = pop['total play'] | |
pop_haters = pop_time[pop_time <= 5].count() | |
rock_skip=rock_haters/rock.shape[0] | |
print('Доля пропущенных композиций жанра рок равна: {:.1%}'.format(rock_skip)) | |
pop_skip = pop_haters/pop.shape[0] | |
print('Доля пропущенных композиций жанра поп равна: {:.1%}'.format(pop_skip)) |
Результат работы | |
Соединив знание основ языка Python — арифметические и логические операторы, переменные, вызов функций print() и format() — с некоторыми возможностями библиотеки Pandas, вы уже получили важный для реального бизнеса результат. Он касается задачи, поставленной менеджером Яндекс.Музыки: увеличить время прослушивания. | |
Вывод из вашего исследования контринтуитивный, как это нередко бывает в анализе данных. Казалось бы, раз «все» качают попсу, время прослушивания можно увеличить, просто ставя в плейлист побольше поп-музыки. А вот и нет! Хотя попсу действительно слушают чаще, пропускают её так же быстро, как и рок. | |
Яндекс пошёл другим путём: объединил сервисы Музыка и Радио. Итог этого эксперимента вам и предстоит оценить. | |
Что дальше | |
Перед тем, как взяться за предоставленную менеджером громадную таблицу, надо вычистить из неё пропуски, дубликаты и другие нежелательные артефакты. Готовится важное решение. Нельзя, чтобы «информационный мусор» толкнул вас на неверный путь. Предобработка данных так важна, что её приёмам посвящена вся следующая тема. | |
Забери с собой | |
Чтобы ничего не забыть, скачайте шпаргалку |
Привет! Только обратил внимание на твой вопрос, спасибо тебе за него, хоть разобрался где тут настраивать оповещалки о подобных вопросах )
Давай по порядку. В твоем коде:
music_tail = df.tail(10)
print(music_tail.tail(10))
В 1ой строке ты создаешь переменную music_tail
в которую сохраняешь 10 последних строк из датафрейма df
, а в след. строке ты выводишь на экран 10 последних строк из датафрейма music_tail
. Т.е. ты явно зная, что переменная music_tail
содержит всего 10 строк снова говоришь вывести именно 10 последних строк.
В коде из другого примера:
music_head = df.head()
print(music_head.head())
Обрати внимание, что в скобках функции head ничего нет, это потому, что по умолчанию туда ставится цифра 5, т.е. данным кодом ты сохраняешь первые 5 строк в переменную music_head
и выводишь первые 5 строк этой переменой, что тоже явно чрезмерно.
Теперь варик с моим кодом:
music_tail = df[-10:]
print(music_tail.tail(10))
Я записываю сохраняю 10 последних строк с помощью индексного обращения к ним, а далее вывожу на экран снова как и ты 10 последних строк, явно зная, что там итак их всего 10.
Т.е. наиболее корректное решение здесь:
music_tail = df.tail(10)
print(music_tail)
НО, тренажер не бывает не такой умный и может не принимать правильные лаконичные ответы, поэтому в таких случаях лучше всего писать в поддержку.
Также советую искать ответы напрямую в документации, там тоже все хорошо и с примерами расписано.
Релевантные сслыки из документации:
Надеюсь, что тебе все это еще актуально и полезно )
Удачи в кодинге!
Уважаемый dsibi, как вам написать в личку?
Добавил свой контакт в Twitter, аналогичный никнейм в Telegram
Вопрос по уроку 4. Получение данных (введение)
Добрый день, можете дать исчерпывающий ответ по вашему заданию https://gist.github.com/dsibi/bbf6f3d445768fda730a81ea326bd04b#file-4
TASK_2_2
Прочитайте файл music_log.csv и сохраните его в переменной df. Сохраните последние 10 строк с данными из music_log.csv в переменной music_tail и выведите значение переменной на экран.
Я написал код:
Но тренажер упорно не пропускал меня, сообщая , что я неправильно вывожу информацию на экран. (точное сообщение об ошибке уже не могу сказать, забыл заскринить)
При том, что в первой подзадаче был код аналогичный этому, с поправкой на другие переменные и другой метод head() / вот его код:
Такое тренажер пропускал.
В итоге, я пол дня бился над этим таском, пока случайно не нагуглил твой репозиторий. :
Он отличается от моего лишь одной строкой:
music_tail = df[-10:]
После вставки этого кода, тренажер засчитал задание. Вопрос, на каком основании? Более того, конструкция df[-10:] ранее не проходилась в курсе.
Очень надеюсь, что не проигнорируешь вопрос