Last active
March 12, 2025 07:51
-
-
Save dsibi/3c78e6cbddf8fdde2681740124268c71 to your computer and use it in GitHub Desktop.
yandex_python_data_analyst_7_Анализ данных и оформление результатов
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Когда данные очищены от мусора, можно приступить к самому интересному — расчётам и презентации результатов заказчику. | |
| Чему вы научитесь | |
| Принципам группировки и сортировки данных, расчёту статистики и формированию наглядного отчёта об исследовании. | |
| Сколько времени это займёт | |
| 1,5 часа = 5 уроков от 1 до 25 минут. | |
| Постановка задачи | |
| Завершаем анализ данных Яндекс.Музыки, выполняем поставленную менеджером задачу и сдаём отчёт. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Идею объединения сервисов Музыка и Радио тестировали на небольшой группе пользователей. Результаты сведены в csv-файл, который вам предстоит изучить. Итог анализа таких данных — это метрики: величины, значения которых отражают пользовательские впечатления. Одна из важнейших — happiness. Здесь это среднее время, которое пользователь слушает музыку в течение выбранного периода времени (в нашей задаче — за сутки). Чем дольше пользователь слушает музыку, тем он довольнее. Ваша задача: найти значение happiness и посмотреть, как оно поменялось с прошлого эксперимента. | |
| В анализе данных важно наглядное представление результатов, чтобы их мог оценить заказчик. Это бизнес, где на кону громадный трафик, серверные мощности, личное время многотысячной аудитории. И одновременно это исследование. Как в настоящей экспериментальной науке, ответ непредсказуем. Его точность зависит от умелого владения статистическими методами и от качества исходных данных. Перед тем, как начинать считать, проверьте, грамотно ли ваши данные подготовлены. | |
| Ознакомление с данными: в предыдущих сериях. | |
| Перед тем, как браться за статистику, нужно: | |
| 1. Прочесть исходный файл и превратить его в структуру данных | |
| К заданию прилагается файл в формате csv, где все значения разделены запятыми. Это наши исходные данные. Чтобы применить к ним все возможности языка Python и библиотеки Pandas, надо импортировать эту библиотеку и сохранить её в переменной. По сокращённому названию панельных данных (panel data), с которых начиналась Pandas, эту переменную принято называть pd: | |
| import pandas as pd | |
| Для чтения csv-файла в библиотеке Pandas есть готовая функция — метод read_csv(). Как и все методы, он вызывается записью через точку после имени своего объекта. В скобках указывается аргумент (параметр) метода. У read_csv() это имя файла с данными. Прочтение превращает файл в структуру данных DataFrame. Имя переменной, в которой эта структура данных сохраняется, чаще всего df либо отражает тематику данных: | |
| df = pd.read_csv('music_log.csv') | |
| 2. Посмотреть на данные | |
| Вывести на экран таблицу и оценить данные: | |
| print(df) | |
| Как правило, таблица очень велика. Практичнее запросить определённое количество первых строк, методом head(): | |
| print(df.head(15)) # выведет первые 15 строк таблицы | |
| 3. Оценить качество предподготовки | |
| Нужно убедиться в том, что данные прошли предподготовку. По крайней мере, не должно быть пропусков и повторов. Пропущенные и неопределённые значения выявляет метод isna(), а суммарное количество таких значений — метод sum(). Обратите внимание: мы записали вызов обоих методов в одну строку, разделив их точкой. Python сначала вызовет метод isna(), а затем результаты его работы передаст методу sum(). | |
| print(df.isna().sum()) | |
| Повторяющиеся строки — дубликаты — выявляются методом duplicated() и подсчитываются тем же sum(): | |
| print(df.duplicated().sum()) | |
| Если возвращаются нули, всё хорошо — данные пригодны для исследования. | |
| TASK_1_4 | |
| Прочитайте данные из файла music_log_upd.csv и выведите первые 10 строк. | |
| music_log_upd.csv — обновлённый файл с данными, которые прошли предобработку в предыдущей теме. | |
| SOLUTION | |
| import pandas as pd | |
| df = pd.read_csv('music_log_upd.csv') | |
| print(df.head(10)) | |
| TASK_2_4 | |
| Получите список названий столбцов, запросив атрибут columns. Результат выведите на экран. | |
| SOLUTION | |
| import pandas as pd | |
| df = pd.read_csv('music_log_upd.csv') | |
| print(df.columns) | |
| TASK_3_4 | |
| Посчитайте количество пустых значений в наборе данных, сохраните результат в переменной na_number. Выведите её значение на экран. | |
| SOLUTION | |
| import pandas as pd | |
| df = pd.read_csv('music_log_upd.csv') | |
| na_number=(df.isna().sum()) | |
| print(na_number) | |
| TASK_4_4 | |
| Посчитайте количество дубликатов в наборе данных, сохраните результат в переменной duplicated_number. Выведите её значение на экран. | |
| SOLUTION | |
| import pandas as pd | |
| df = pd.read_csv('music_log_upd.csv') | |
| duplicated_number=df.duplicated().sum() | |
| print(duplicated_number) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Слово «анализ» означает разбор, рассмотрение с разных сторон. Анализ данных начинают с разделения их на группы по какому-нибудь признаку. Эта операция называется группировка данных. Она помогает изучить материал более подробно, чтобы затем перейти к поиску взаимосвязей между отдельными группами. | |
| Группировка оправданна, если данные чётко делятся по значимому признаку, а полученные группы близки к теме задачи. Например, когда есть данные обо всех покупках в супермаркете, можно смело заниматься группировкой. Так можно установить время наплыва покупателей и решить проблему пиковых нагрузок. Или посчитать средний чек — обычно для магазинов это ключевая метрика. | |
| Стадии группировки хорошо укладываются в словесную формулу split-apply-combine: | |
| разделить, split — разбиение на группы по определённому критерию; | |
| применить, apply — применение какого-либо метода к каждой группе в отдельности, например, подсчёт численности группы методом count() или суммирование вызовом sum(); | |
| объединить, combine — сведение результатов в новую структуру данных, в зависимости от условий разделения и выполнения метода это бывает DataFrame и Series. | |
| В библиотеке Pandas есть отличные инструменты группировки. Рассмотрим обращение с ними на примере анализа данных о планетах за пределами Солнечной системы, или экзопланетах. Орбитальные обсерватории засекли уже тысячи таких небесных тел. Их выявляют на снимках космических телескопов наши коллеги, аналитики данных. Поищем среди экзопланет похожие на Землю. Возможно, это наши будущие колонии, или там уже обитают разумные существа, с которыми однажды предстоит установить контакт. | |
| DataFrame с данными по нескольким тысячам экзопланет сохранён в переменной exoplanet. Посмотрим на первые 30 строк таблицы: | |
| print(exoplanet.head(30)) | |
| NAME MASS RADIUS DISCOVERED | |
| 0 1RXS 1609 b 14 19.04 2008 | |
| 1 2M 0122-24 b 20 11.2 2013 | |
| 2 2M 0219-39 b 13.9 16.128 2015 | |
| 3 2M 0746+20 b 12.21 10.864 2010 | |
| 4 2M 2140+16 b 20 10.304 2010 | |
| 5 2M 2206-20 b 30 14.56 2010 | |
| 6 51 Eri b 9.1 12.432 2015 | |
| 7 51 Peg b 0.47 21.28 1995 | |
| 8 55 Cnc e 0.02703 1.94544 2004 | |
| 9 BD+20 594 b 0.0513 2.2288 2016 | |
| 10 BD-10 3166 b 0.46 11.536 2000 | |
| 11 CT Cha b 17 24.64 2008 | |
| 12 CVSO 30 b 6.2 21.392 2012 | |
| 13 CoRoT-1 b 1.03 16.688 2007 | |
| 14 CoRoT-10 b 2.75 10.864 2010 | |
| 15 CoRoT-11 b 2.33 16.016 2010 | |
| 16 CoRoT-12 b 0.917 16.128 2010 | |
| 17 CoRoT-13 b 1.308 9.912 2010 | |
| 18 CoRoT-14 b 7.6 12.208 2010 | |
| 19 CoRoT-15 b 63.4 12.544 2010 | |
| 20 CoRoT-16 b 0.535 13.104 2010 | |
| 21 CoRoT-17 b 2.43 11.424 2010 | |
| 22 CoRoT-18 b 3.47 14.672 2011 | |
| 23 CoRoT-19 b 1.11 14.448 2011 | |
| 24 CoRoT-2 b 3.31 16.408 2007 | |
| 25 CoRoT-20 b 4.24 9.408 2011 | |
| 26 CoRoT-21 b 2.26 14.56 2011 | |
| 27 CoRoT-22 b 0.06 4.87648 2011 | |
| 28 CoRoT-23 b 2.8 12.096 2011 | |
| 29 CoRoT-24 b 0.018 3.696 2011 | |
| Документация | |
| Столбцы: | |
| name: название экзопланеты; | |
| mass: масса в массах планеты Юпитер; | |
| radius: радиус, пересчитанный в радиусах Земли; | |
| discovered: год открытия экзопланеты. | |
| Источник: каталог экзопланет на портале exoplanet.eu | |
| На картинке изображен принцип split-apply-combine для таблицы с экзопланетами. Посмотрим, как вообще идут дела с поиском экзопланет. Сначала данные делят по группам, где каждая группа — это год. Потом метод count() подсчитывает численность каждой группы. В итоге получаем новую структуру данных с группами, где каждая содержит год и число открытых за этот год экзопланет. | |
| image | |
| В Рandas для группировки данных есть метод groupby(). Он принимает как аргумент название столбца, по которому нужно группировать. В случае с делением экзопланет по годам открытия: | |
| print(exoplanet.groupby('discovered')) | |
| <pandas.core.groupby.DataFrameGroupBy object at 0x7fc1e1ca3400> | |
| Применение метода groupby() к объекту типа DataFrame приводит к созданию объекта особого типа — DataFrameGroupBy. Это сгруппированные данные. Если применить к ним какой-нибудь метод Pandas, они станут новой структурой данных типа DataFrame или Series. | |
| Подсчитаем сгруппированные по годам экзопланеты методом count(): | |
| print(exoplanet.groupby('discovered').count()) | |
| DISCOVERED NAME MASS RADIUS | |
| 1995 1 1 1 | |
| 1996 1 1 1 | |
| 1999 2 2 2 | |
| 2000 5 5 5 | |
| 2001 1 1 1 | |
| 2002 4 4 4 | |
| 2004 10 10 10 | |
| 2005 9 9 9 | |
| 2006 11 11 11 | |
| 2007 23 23 23 | |
| 2008 23 23 23 | |
| 2009 12 12 12 | |
| 2010 59 59 59 | |
| 2011 87 87 87 | |
| 2012 93 93 93 | |
| 2013 98 98 98 | |
| 2014 73 73 73 | |
| 2015 56 56 56 | |
| 2016 84 84 84 | |
| 2017 54 54 54 | |
| 2018 101 101 101 | |
| 2019 2 2 2 | |
| Результат выполнения кода exoplanet.groupby('discovered').count() — это уже новая структура данных, типа DataFrame. И с первого взгляда на этот DataFrame заметна тенденция: количество открытых экзопланет почти ежегодно растёт. | |
| Если нужно сравнить наблюдения по одному показателю, метод применяют к DataFrameGroupBy с указанием на один столбец. Нас в первую очередь интересует радиус экзопланет: мы ищем другую Землю. Давайте получим таблицу с единственным столбцом 'radius': | |
| exo_number = exoplanet.groupby('discovered')['radius'].count() | |
| print(exo_number) | |
| DISCOVERED | |
| 1995 1 | |
| 1996 1 | |
| 1999 2 | |
| 2000 5 | |
| 2001 1 | |
| 2002 4 | |
| 2004 10 | |
| 2005 9 | |
| 2006 11 | |
| 2007 23 | |
| 2008 23 | |
| 2009 12 | |
| 2010 59 | |
| 2011 87 | |
| 2012 93 | |
| 2013 98 | |
| 2014 73 | |
| 2015 56 | |
| 2016 84 | |
| 2017 54 | |
| 2018 101 | |
| 2019 2 | |
| Name: radius, dtype: int64 | |
| Получили Series, где по годам открытия расписано количество экзопланет, для которых удалось установить радиус. | |
| Посмотрим, как меняется средний радиус открытых экзопланет год от года. Для этого надо сложить радиусы планет, открытых за определённый год, и поделить на их количество (которое мы уже нашли). | |
| Сумма радиусов считается методом sum(): | |
| exo_radius_sum = exoplanet.groupby('discovered')['radius'].sum() | |
| print(exo_radius_sum) | |
| DISCOVERED | |
| 1995 21.280000 | |
| 1996 11.872000 | |
| 1999 26.992000 | |
| 2000 57.198400 | |
| 2001 10.315200 | |
| 2002 47.152000 | |
| 2004 110.988640 | |
| 2005 111.059200 | |
| 2006 246.568000 | |
| 2007 325.908800 | |
| 2008 350.884800 | |
| 2009 130.959289 | |
| 2010 723.900182 | |
| 2011 917.345484 | |
| 2012 707.924857 | |
| 2013 705.458700 | |
| 2014 554.762932 | |
| 2015 563.962784 | |
| 2016 971.348000 | |
| 2017 504.473312 | |
| 2018 994.195820 | |
| 2019 14.324800 | |
| Name: radius, dtype: float64 | |
| Очень кстати, что объекты Series можно делить друг на друга. Это позволит нам разделить перечень сумм радиусов на перечень количеств экзопланет без перебора в цикле: | |
| exo_radius_mean = exo_radius_sum/exo_number | |
| print(exo_radius_mean) | |
| DISCOVERED | |
| 1995 21.280000 | |
| 1996 11.872000 | |
| 1999 13.496000 | |
| 2000 11.439680 | |
| 2001 10.315200 | |
| 2002 11.788000 | |
| 2004 11.098864 | |
| 2005 12.339911 | |
| 2006 22.415273 | |
| 2007 14.169948 | |
| 2008 15.255861 | |
| 2009 10.913274 | |
| 2010 12.269495 | |
| 2011 10.544201 | |
| 2012 7.612095 | |
| 2013 7.198558 | |
| 2014 7.599492 | |
| 2015 10.070764 | |
| 2016 11.563667 | |
| 2017 9.342098 | |
| 2018 9.843523 | |
| 2019 7.162400 | |
| Name: radius, dtype: float64 | |
| Точность наших приборов растёт, и новые экзопланеты по размерам всё ближе к Земле. За 24 года средний радиус обнаруженных планет снизился втрое. | |
| Тем же методом groupby(), которым мы ищем новую Землю, можно поискать и необыкновенного человека в данных Яндекс.Музыки. Тем более, что без этого не выполнить поставленной менеджером задачи. | |
| Прежде, чем рассчитывать метрику happiness, нужно изучить пользователей, чьё «счастье» мы собираемся оценить. Какие они, эти люди, которые слушают действительно много музыки? Есть ли у них особые предпочтения, или они потребляют всё подряд? | |
| TASK_1_3 | |
| Меломаны у нас есть. Сейчас узнаем идентификатор user_id одного из них. Для этого сгруппируем данные по каждому пользователю, чтобы собрать жанры прослушанных им композиций. | |
| Сгруппируйте DataFrame по столбцу user_id, сохраните полученный результат в переменной genre_grouping. | |
| Посчитайте количество жанров, которые выбрали пользователи, методом count(), указав, что выбираем один столбец genre_name. Сохраните результат в переменной genre_counting и выведите первые 30 строк этой таблицы. | |
| SOLUTION | |
| import pandas as pd | |
| df = pd.read_csv('music_log_upd.csv') | |
| genre_grouping=df.groupby("user_id") | |
| genre_counting=genre_grouping['genre_name'].count() | |
| print(genre_counting.head(30)) | |
| TASK_2_3 | |
| Быть может, те, кто за день слушает больше 50 песен, имеют более широкие предпочтения. Чтобы найти такого, изготовим универсальный инструмент. | |
| Напишите функцию user_genres, которая принимает некую группировку как свой аргумент group. Функция должна перебирать группы, входящие в эту группировку. | |
| В каждой группе два элемента — имя группы с индексом 0 и список значений с индексом 1. | |
| Обнаружив такую группу, в которой список (элемент с индексом 1) содержит более 50 значений, функция возвращает имя группы (значение элемента с индексом 0). | |
| SOLUTION | |
| TASK_3_3 | |
| SOLUTION |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
В блоке 3 Группировка данных у вас верный код с точки зрения выполнения, но тренажер яндекс.практимума будет выдавать ошибку:
"Текущий тип переменной - DataFrameGroupBy а ожидался - SeriesGroupBy..."
и не пропустит задание. Код нужно немного подправить: