Если ты совсем торопишься, то можешь скачать проект в формате .ipynb:
"3 Базовый Python: Музыка больших городов.ipynb"
Следующая тема: БРу. Заключение.
Вернуться в раздел: Базовый Python
Вернуться в оглавление: Я.Практикум
13. Проект: Музыка больших городов
13.1 Первое исследование
13.2 Описание проекта
13.3 Не делайте проект*
Шпаргалки
В процессе работы над проектом вам могут понадобиться шпаргалки, чтобы вспомнить синтаксис и назначение различных методов и конструкций. Для удобства собрали их в одном месте:
- Переменные и типы данных
- Строки
- Списки
- Цикл for
- Вложенные списки
- Условный оператор
- Функции
- Словари
- Pandas для анализа данных
- Предобработка данных
- Анализ данных и оформление результатов
13.4 Проект Базовый Python
КОНТРОЛЬНЫЙ ПРОЕКТ
Яндекс Музыка
Сравнение Москвы и Петербурга окружено мифами. Например:
- Москва — мегаполис, подчинённый жёсткому ритму рабочей недели;
- Петербург — культурная столица, со своими вкусами.
На данных Яндекс Музыки вы сравните поведение пользователей двух столиц.
Цель исследования — проверьте три гипотезы:
- Активность пользователей зависит от дня недели. Причём в Москве и Петербурге это проявляется по-разному.
- В понедельник утром в Москве преобладают одни жанры, а в Петербурге — другие. Так же и вечером пятницы преобладают разные жанры — в зависимости от города.
- Москва и Петербург предпочитают разные жанры музыки. В Москве чаще слушают поп-музыку, в Петербурге — русский рэп.
Ход исследования
Данные о поведении пользователей вы получите из файла yandex_music_project.csv
. О качестве данных ничего не известно. Поэтому перед проверкой гипотез понадобится обзор данных.
Вы проверите данные на ошибки и оцените их влияние на исследование. Затем, на этапе предобработки вы поищете возможность исправить самые критичные ошибки данных.
Таким образом, исследование пройдёт в три этапа:
- Обзор данных.
- Предобработка данных.
- Проверка гипотез.
1 Обзор данных
Составьте первое представление о данных Яндекс Музыки.
Задание 1
Основной инструмент для работы с данными — pandas
. Импортируйте эту библиотеку в сокращённой форме — pd
.
# импорт библиотеки pandas
import pandas as pd
Прочитайте файл yandex_music_project.csv
из папки /datasets
и сохраните его в переменной df
.
# чтение файла с данными и сохранение в df
#df = pd.read_csv('/datasets/yandex_music_project.csv')
df = pd.read_csv('datasets/music_project.csv')
Выведите на экран первые десять строк датафрейма df
.
# получение первых 10 строк таблицы df
df.head(10)
abject
Согласно документации к данным:
user
- индентификатор пользователя;
Track
- название трека;
artist
- имя исполнителя;
genre
- название жанра;
City
- город пользователя;
time
- время начала прослушивания;
Day
- день недели.
Количество значений в столбцахразличается. Значит, в данных есть пропущенные значения.
Одной командой получите общую информацию о таблице.
# получение общей информации о данных в таблице df
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 65079 entries, 0 to 65078
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Unnamed: 0 65079 non-null int64
1 userID 65079 non-null object
2 Track 63848 non-null object
3 artist 57876 non-null object
4 genre 63881 non-null object
5 City 65079 non-null object
6 time 65079 non-null object
7 Day 65079 non-null object
dtypes: int64(1), object(7)
memory usage: 4.0+ MB
Вопрос со свободной формой ответа
В названиях колонок видны три нарушения стиля:
- Строчные буквы сочетаются с прописными.
- Встречаются пробелы.
Найдите третье нарушение стиля. Напишите ваш ответ в ячейке комментарием. Не удаляйте символ #
. Не меняйте тип этой ячейки на Markdown
.
Это задание не будет проверено автоматически, оно предназначено для самопроверки. Просто нажмите кнопку “Проверить задание” после ввода ответа, чтобы перейти к следующему заданию.
# Напишите ваш ответ здесь комментарием. Не удаляйте символ
#. Не меняйте тип этой ячейки на Markdown.
В каждой строке таблицы — данные о прослушанном треке. Часть колонок описывает саму композицию: название, исполнителя и жанр. Остальные данные рассказывают о пользователе: из какого он города, когда он слушал музыку.
Предварительно можно утверждать, что данных достаточно для проверки гипотез. Но встречаются пропуски в данных, а в названиях колонок — расхождения с хорошим стилем.
Чтобы двигаться дальше, нужно устранить проблемы в данных.
# для совпадения заданий с практическим удалим столбец Unnamed: 0
df = df.drop(['Unnamed: 0'], axis=1)
2.1 Переименование столбцов
Задание 6
Выведите на экран названия столбцов датафрейма df
.
# перечень названий столбцов таблицы df
df.columns
Index([' userID', 'Track', 'artist', 'genre', ' City ', 'time', 'Day'], dtype='object')
Приведите названия в соответствие с хорошим стилем:
-
несколько слов в названии запишите в «змеином_регистре»,
-
все символы сделайте строчными,
устраните пробелы.
Для этого переименуйте колонки так:
userID
→ user_id
;
Track
→ track
;
City
→ city
;
Day
→ day
.
# переименование столбцов
df = df.rename(columns={' userID':'user_id','Track':'track',' City ':'city','Day':'day' })
df.columns
Index(['user_id', 'track', 'artist', 'genre', 'city', 'time', 'day'], dtype='object')
Проверьте результат. Для этого ещё раз выведите на экран названия столбцов датафрейма df
.
# проверка результатов - перечень названий столбцов
df.columns
Задание 9
Посчитайте, сколько в каждом из столбцов таблицы пропущенных значений.
# подсчёт пропусков
df.isna().sum()
track
и artist
пропуски не важны для работы. Достаточно заменить их явными обозначениями.Но пропуски в genre
могут помешать сравнивать музыкальные вкусы москвичей и петербуржцев. На практике было бы правильно установить причину пропусков и восстановить данные. Такой возможности нет в учебном проекте. Поэтому придется:
- заполнить эти пропуски явными обозначениями;
- оценить, насколько они повредят расчетам.
Замените пропущенные значения в столбцах track
, artist
и genre
на строку 'unknown'
. Для этого создайте список columns_to_replace
, переберите его элементы циклом for и для каждого столбца выполните замену пропущенных значений.
# замена пропущенных значений на 'unknown'
columns_to_replace = ['track','artist','genre']
for column in columns_to_replace:
df[column] = df[column].fillna('unknown')
Убедитесь, что в таблице не осталось пропусков. Для этого ещё раз посчитайте пропущенные значения.
# проверка на отсутствие пропусков
df.isna().sum()
2.3 Обработка дубликатов
Задание 12
Посчитайте явные дубликаты в таблице одной командой из двух методов.
# подсчёт явных дубликатов
df.duplicated().sum()
Вызовите специальный метод pandas
, чтобы удалить явные строки-дубликаты в данных. В этой же строке вызовите метод, который создаст новые индексы и удалит старые.
# удаление явных дубликатов, создание новых индексов и удаление старых
df = df.drop_duplicates().reset_index(drop=True)
Задание 14
Ещё раз посчитайте явные дубликаты в таблице — убедитесь, что полностью от них избавились.
# проверка на отсутствие явных дубликатов
df.duplicated().sum()
0
Выведите на экран список уникальных названий жанров, отсортированный в алфавитном порядке. Для этого:
-
извлеките нужный столбец датафрейма;
-
примените к нему метод сортировки из библиотеки pandas, используйте сортировку по возрастанию;
-
для отсортированного столбца вызовите метод из библиотеки pandas, который вернёт уникальные значения из столбца.
# просмотр уникальных отсортированных названий жанров
df['genre'].sort_values(ascending = True).unique()
array(['acid', 'acoustic', 'action', 'adult', 'africa', 'afrikaans',
Просмотрите список и найдите неявные дубликаты названия hiphop
. Это могут быть названия с ошибками или альтернативные названия того же жанра.
Вы увидите следующие неявные дубликаты:
-
hip,
-
hop,
-
hip-hop.
Вам нужно исправить колонку genre
в таблице df
: заменить каждое значение из списка дубликатов на верное. Вместо hip
, hop
и hip-hop
в таблице должно быть значение hiphop
.
# Устранение неявных дубликатов
df['genre'] = df['genre'].replace(['hip', 'hop', 'hip-hop'], 'hiphop')
Задание 17
Проверьте, что заменили неправильные названия:
-
hip
, -
hop
, -
hip-hop
.
Выведите отсортированный по возрастанию список уникальных значений столбца genre
.
# проверка на отсутствие неявных дубликатов
df['genre'].sort_values(ascending = True).unique()
Предобработка обнаружила три проблемы в данных:
- нарушения в стиле заголовков,
- пропущенные значения,
- дубликаты — явные и неявные.
Вы исправили заголовки, чтобы упростить работу с таблицей. Без дубликатов исследование станет более точным.
Пропущенные значения вы заменили на 'unknown'
. Ещё предстоит увидеть, не повредят ли исследованию пропуски в колонке genre
.
Теперь можно перейти к проверке гипотез.
- Разделим пользователей Москвы и Санкт-Петербурга.
- Сравним, сколько треков послушала каждая группа пользователей в понедельник, среду и пятницу.
Для тренировки сначала выполните каждый из расчётов по отдельности.
Оцените активность пользователей в каждом городе. Сгруппируйте данные по городу и посчитайте прослушивания в каждой группе.
# подсчёт прослушиваний в каждом городе
df.groupby('city')['user_id'].count()
Задание 19
Теперь сгруппируйте данные по дню недели и посчитайте прослушивания в понедельник, среду и пятницу. Учтите, что в данных есть информация о прослушиваниях только за эти дни.
# подсчёт прослушиваний в каждый из трёх дней
df.groupby('day')['user_id'].count()
day
В среднем пользователи из двух городов менее активны по средам. Но картина может измениться, если рассмотреть каждый город в отдельности.
Вы видели, как работает группировка по городу и по дням недели. Теперь напишите функцию, которая объединит два эти расчёта. Часть кода уже написана, допишите оставшиеся команды.
Создайте функцию number_tracks()
, которая посчитает прослушивания для заданного дня и города. Ей понадобятся два параметра:
- день недели,
- название города.
В функции сохраните в переменную строки исходной таблицы, у которых значение:
в колонке day
равно параметру day
, в колонке city
равно параметру city
.
Для этого примените последовательную фильтрацию с логической индексацией.
Затем посчитайте значения в столбце user_id
получившейся таблицы. Результат сохраните в новую переменную. Верните эту переменную из функции.
def number_tracks(day, city):
# выберите только строки df со значением переменной day в столбце day
track_list = df[df['day']==day]
# выберите только строки track_list со значением переменной city в столбце city
track_list = track_list[track_list['city']==city]
# вызовите метод подсчета строк для track_list и выберите столбец user_id
track_list_count = track_list['user_id'].count()
# верните значение track_list_count из функции
return track_list_count
Задание 21
Вызовите number_tracks() шесть раз, меняя значение параметров — так, чтобы получить данные для каждого города в каждый из трёх дней.
Переберите следующие пары:
'Monday', 'Moscow'
'Monday', 'Saint-Petersburg'
'Wednesday', 'Moscow'
'Wednesday', 'Saint-Petersburg'
'Friday', 'Moscow'
'Friday', 'Saint-Petersburg'
# количество прослушиваний в Москве по понедельникам
number_tracks(day='Monday', city='Moscow')
# количество прослушиваний в Санкт-Петербурге по понедельникам
number_tracks(day='Monday', city='Saint-Petersburg')
# количество прослушиваний в Москве по средам
number_tracks(day='Wednesday', city='Moscow')
# количество прослушиваний в Санкт-Петербурге по средам
number_tracks(day='Wednesday', city='Saint-Petersburg')
# количество прослушиваний в Москве по пятницам
number_tracks(day='Friday', city='Moscow')
# количество прослушиваний в Санкт-Петербурге по пятницам
number_tracks(day='Friday', city='Saint-Petersburg')
С помощью конструктора pd.DataFrame
создайте таблицу info
, где:
-
названия колонок —
['city', 'monday', 'wednesday', 'friday']
; -
данные — результаты, которые вы получили с помощью
number_tracks
.
Это задание не будет проверяться. Мы просто предлагаем вам вспомнить, как создавать датафрейм с помощью конструктора.
# создание таблицы с результатами
info = info = pd.DataFrame(data=[['Москва',15750,11056,15945], ['Санкт-Петербург',5614,7003,5895]], columns=['city', 'monday','wednesday','friday'])
# вывод таблицы на экран
info()
Данные показывают разницу поведения пользователей:
- В Москве пик прослушиваний приходится на понедельник и пятницу, а в среду заметен спад.
- В Петербурге, наоборот, больше слушают музыку по средам. Активность в понедельник и пятницу здесь почти в равной мере уступает среде.
Значит, данные говорят в пользу первой гипотезы.
3.2 Музыка в начале и в конце недели
Согласно второй гипотезе, утром в понедельник в Москве преобладают одни жанры, а в Петербурге — другие. Так же и вечером пятницы преобладают разные жанры — в зависимости от города.
Задание 23
Сохраните таблицы с данными в две переменные:
-
по Москве — в
moscow_general
; -
по Санкт-Петербургу — в
spb_general
.
# получение таблицы moscow_general из тех строк таблицы df, для которых значение в столбце 'city' равно 'Moscow'
moscow_general = df[df['city']=='Moscow']
# получение таблицы spb_general из тех строк таблицы df, для которых значение в столбце 'city' равно 'Saint-Petersburg'
spb_general = df[df['city']=='Saint-Petersburg']
Задание 24
Создайте функцию genre_weekday()
с четырьмя параметрами:
-
таблица (датафрейм) с данными,
-
день недели,
-
начальная временная метка в формате
'hh:mm'
, -
последняя временная метка в формате
'hh:mm'
.
Функция должна вернуть информацию о топ-10 жанров тех треков, которые прослушивали в указанный день, в промежутке между двумя отметками времени.
def genre_weekday(df, day, time1, time2):
# последовательная фильтрация
# оставляем в genre_df только те строки df, у которых день равен day
genre_df = df[df['day']==day]# ваш код здесь
# оставляем в genre_df только те строки genre_df, у которых время меньше time2
genre_df = genre_df[genre_df['time']<time2] # ваш код здесь
# оставляем в genre_df только те строки genre_df, у которых время больше time1
genre_df = genre_df[genre_df['time']>time1] # ваш код здесь
# сгруппируем отфильтрованный датафрейм по столбцу с названиями жанров, возьмём столбец genre и посчитаем кол-во строк для каждого жанра методом count()
genre_df_grouped = genre_df.groupby('genre')['genre'].count() # ваш код здесь
# отсортируем результат по убыванию (чтобы в начале Series оказались самые популярные жанры)
genre_df_sorted = genre_df_grouped.sort_values(ascending=False) # ваш код здесь
# вернём Series с 10 самыми популярными жанрами в указанный отрезок времени заданного дня
return genre_df_sorted[:10]
Сравните результаты функции genre_weekday()
для Москвы и Санкт-Петербурга в понедельник утром (с 7:00 до 11:00) и в пятницу вечером (с 17:00 до 23:00). Объекты, хранящие время, являются строками и сравниваются как строки — оставим это как есть.
Пример вызова: genre_weekday(moscow_general, 'Monday', '07:00', '11:00')
# вызов функции для утра понедельника в Москве (вместо df — таблица moscow_general)
genre_weekday(moscow_general, 'Monday', '07:00', '11:00')
# вызов функции для утра понедельника в Петербурге (вместо df — таблица spb_general)
genre_weekday(spb_general, 'Monday', '07:00', '11:00')
# вызов функции для вечера пятницы в Москве
genre_weekday(moscow_general, 'Friday', '17:00', '23:00')
# вызов функции для вечера пятницы в Петербурге
genre_weekday(spb_general, 'Friday', '17:00', '23:00')
Если сравнить топ-10 жанров в понедельник утром, можно сделать такие выводы:
-
В Москве и Петербурге слушают похожую музыку. Единственное различие — в московский рейтинг вошёл жанр “world”, а в петербургский — джаз и классика.
-
В Москве пропущенных значений оказалось так много, что значение
'unknown'
заняло десятое место среди самых популярных жанров. Значит, пропущенные значения занимают существенную долю в данных и угрожают достоверности исследования.
Вечер пятницы не меняет эту картину. Некоторые жанры поднимаются немного выше, другие спускаются, но в целом топ-10 остаётся тем же самым.
Таким образом, вторая гипотеза подтвердилась лишь частично:
- Пользователи слушают похожую музыку в начале недели и в конце.
- Разница между Москвой и Петербургом не слишком выражена. В Москве чаще слушают русскую популярную музыку, в Петербурге — джаз.
Однако пропуски в данных ставят под сомнение этот результат. В Москве их так много, что рейтинг топ-10 мог бы выглядеть иначе, если бы не утерянные данные о жанрах.
Гипотеза: Петербург — столица рэпа, музыку этого жанра там слушают чаще, чем в Москве. А Москва — город контрастов, в котором, тем не менее, преобладает поп-музыка.
Сгруппируйте таблицу moscow_general
по жанру и посчитайте прослушивания треков каждого жанра методом count()
. Чтобы избежать дублирующих вычислений, выберите столбец genre
. Затем отсортируйте результат в порядке убывания и сохраните его в таблице moscow_genres
.
# одной строкой: группировка таблицы moscow_general по столбцу 'genre', выбор столбца `genre`, подсчёт числа значений 'genre' методом count(), сохранение в moscow_genres
# сортировка получившегося Series в порядке убывания и сохранение обратно в moscow_genres
moscow_genres = moscow_general.groupby('genre')['genre'].count().sort_values(ascending = False)
Выведите на экран первые десять строк moscow_genres.
# просмотр первых 10 строк moscow_genres
moscow_genres.head(10)
Теперь повторите то же и для Петербурга.
Сгруппируйте таблицу spb_general
по жанру. Посчитайте прослушивания треков каждого жанра. Результат отсортируйте в порядке убывания и сохраните в таблице spb_genres
.
# одной строкой: группировка таблицы spb_general по столбцу 'genre', выбор столбца `genre`, подсчёт числа значений 'genre' методом count(), сохранение в spb_genres
# сортировка получившегося Series в порядке убывания и сохранение обратно в spb_genres
spb_genres = spb_general.groupby('genre')['genre'].count().sort_values(ascending = False)
Выведите на экран первые десять строк spb_genres.
# просмотр первых 10 строк spb_genres
spb_genres.head(10)
- Поп-музыка — самый популярный жанр в Москве, как и предполагала гипотеза. Более того, в топ-10 жанров встречается близкий жанр — русская популярная музыка.
- Вопреки ожиданиям, рэп одинаково популярен в Москве и Петербурге. `m
- День недели по-разному влияет на активность пользователей в Москве и Петербурге.
Первая гипотеза полностью подтвердилась.
- Музыкальные предпочтения не сильно меняются в течение недели — будь то Москва или Петербург. Небольшие различия заметны в начале недели, по понедельникам:
- в Москве слушают музыку жанра “world”,
- в Петербурге — джаз и классику.
Таким образом, вторая гипотеза подтвердилась лишь отчасти. Этот результат мог оказаться иным, если бы не пропуски в данных.
- Во вкусах пользователей Москвы и Петербурга больше общего, чем различий. Вопреки ожиданиям, предпочтения жанров в Петербурге напоминают московские.
Третья гипотеза не подтвердилась. Если различия в предпочтениях и существуют, на основной массе пользователей они незаметны.
На практике исследования содержат проверки статистических гипотез. Из части данных одного сервиса невозможно сделать какие-то выводы о всех пользователях сервиса без методов статистики. Проверки статистических гипотез покажут, насколько они достоверны, исходя из имеющихся данных. С методами проверок гипотез вы ещё познакомитесь в следующих темах.
Дорогой друг!
Я внимательно наблюдал за твоими действиями на сайте через вебвизор.
Если ты добрался до этой страницы, то скорее всего ты проходишь платное обучение на курсах.
Благодаря моим стараниям, целую главу ты можешь закрыть за сутки, а то и быстрее.
Думаю, что сэкономленное время, ты потратил с пользой, в то время как я сидел и пыхтел над заданиями.
Лучшая благодарность от тебя, это пожертвования на мой IT - проект.
Жертвуй столько, сколько не жалко, но и не скупись, чтобы было не стыдно.
Большое тебе спасибо, за твою благодарность!
Следующая тема: БРу. Заключение.
Вернуться в раздел: Базовый Python
Вернуться в оглавление: Я.Практикум