-
-
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:] ранее не проходилась в курсе.
Очень надеюсь, что не проигнорируешь вопрос