Рубрики
Без рубрики

Анализируя мою историю прослушивания Spotify 🎵 – Часть 2

Этот пост был изначально написан на моем личном сайте, где графики и участки являются интерактивными … Теги с Python, Jupyter, Datascity.

Spotify Analysis (2 части серии)

Этот пост был изначально написан на моем личном веб-сайте, где графики и участки интерактивны, и я показываю код с участием. Если вы пропустите это, проверьте оригинал .

В части 1 этой серии мы посмотрели на первую часть этого проекта. Это включено:

  1. Данные, с которыми мы работаем и как выглядит.
  2. Сумма прослушивания, сделанного в год и в месяц.
  3. Сумма прослушивания, выполненного в час дня, также в течение многих лет.
  4. Количество жанров, которые у нас есть за песню/артист.

Мы продолжим, откуда мы уехали, дайвинг глубже в жанры.

Мы загрузим оригинал JSON из Spotify, а также жанры, которые мы создали в части 1. Затем мы объединяем их в расческу, комбинированное DataFrame. В Genres.csv мы снова видим 20 столбцов с жанрами для каждой песни, где жанры собираются от художника, поскольку песни не помечены как имеющие жанр. Для получения более подробной информации, пожалуйста, посмотрите на часть 1.

2013-10-09 20: 24: 30 + 00: 00 9 2 Нан 2013-10-09 20 2013 10 Нан False False 15010 0 Click-row NL. Нан неизвестный Нан Дикий на ночь (подвиг. Скрилькс и птичкана … Long.live.a $ ap (versuxe версия) $ AP Rokey

Жанры извлечены из Spotify и комбинированного датафарама. Мы переименовываем столбцы жанров только с числом 0-20 до «Genre_x» с X от 0 до 20, поэтому их легче распознать.

гребень состоит из дф. + genres_df. , с столбцами жанров в конце.

# genres retrieved through Spotify API
genres_df = pd.read_csv('genres.csv', low_memory=False)
genres_df = genres_df.rename(columns={str(x): f'genre_{x}' for x in range(21)})
comb = pd.concat([df, genres_df], axis=1)
comb.head(2)
2013-10-09 20: 24: 30 + 00: 00 False False Нан Нан Нан Нан Нан Нан Нан Нан Нан Нан 15010 0 Click-row NL. неизвестный Дикий на ночь (подвиг. Скрилькс и птичкана … Long.live.a $ ap (versuxe версия) $ AP Rokey
2013-10-09 20: 19: 20 + 00: 00 False False Нан Нан Нан Нан Нан Нан Нан Нан Нан Нан 68139 1 Click-row NL. неизвестный Взорвать NTHº. Авансовый

В части 1 мы видели, сколько жанров каждая песня имеет и как их цифры распределены. Следующий вопрос тогда, естественно, есть: какие они жанры? Так что посмотрим!

Для следующих анализов помните, что если я сыграю 10 песен Canye, жанры Kanye будут присутствовать в 10 раз.

Чтобы проанализировать жанры, я сначала создаю DataFrame, который содержит все жанры и их количество. Это будет удобно в ближайшем будущем.

top_genres = (
    genres_df.apply(pd.Series.value_counts)
    .apply(np.sum, axis=1)
    .sort_values(ascending=False)
    .reset_index()
    .rename(columns={'index': 'genre', 0: 'count'})
)

Тогда мы можем заговор. Давайте начнем с общих слушателей на жанр.

Здесь нет больших сюрпризов. Мои основные музыкальные вкусы – это хип-хоп и электронная музыка, с основными жанрами Techno и барабаном и басом. Однако для последних двух я в основном использую YouTube, которые хозяева устанавливают, что Spotify не имеет. Таким образом, мой Spotify в основном доминирует хип-хоп и связанные с ними жанры, как рэп , хип-хоп и поп-рэп (Что бы то ни было? Может быть?). Я ожидаю, что многие песни хип-хоп также помечены как поп , что бы объяснить высокую поп Присутствие, пока я обычно не такой поп-поклонник. Давайте погрузиться немного глубже в это!

Лучшие жанры на песню

В качестве следующего шага давайте проверим, какие жанры совпадают с которыми другие жанры. Это проверит нашу гипотезу, что поп используется в качестве тега для хип-хоп Но также в целом также даст нам лучшее ощущение того, что жанры связаны с какими другими жанрами.

Для этого мы цикла по ряду и для каждого нынешнего жанра, мы поставили 1 в этой колонне, а также литья до np.int8. . Это означает, что вместо нормально 32 бит мы используем 8 бит и таким образом безопасную память. Так как мы хотим только представлять двоичное состояние (настоящее или отсутствие), мы также могли бы использовать логию. Однако, поскольку мы делаем арифметику с этим позже, INT8 будет делать. Мы заполняем пустые ячейки 0. Мы делаем это только для 20 лучших жанров. Это приводит к DataFrame с столбцом для каждого из 20 лучших жанров.

rows = []
for i, row in comb.loc[:, [f'genre_{x}' for x in range(21)]].iterrows():
    new_row = {}
    for value in row.values:
        if value in top_genres_20:
            new_row[value] = 1
    rows.append(new_row)
genre_presence = pd.DataFrame(rows)
genre_presence = genre_presence.fillna(0).astype(np.int8)
genre_presence.head(2)
0 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0

Теперь, когда у нас есть эти данные, мы можем сделать корреляционный анализ, когда каждый жанр совпадает с каким другим жанром. Теперь, потому что жанр является номинальным типом данных, мы не можем использовать Стандартная корреляция , что является Коэффициент корреляции Пирсона Отказ Вместо этого мы должны использовать метрику, которая работает с номинальными значениями. Я выбираю Кендалл Тау Для этого из-за его простоты. Обычно Тау Кендалл предназначен для порядковый ?| значения (переменные, которые имеют упорядочение). Однако, потому что мы работаем с двоичной ситуацией (жанр либо присутствует, либо нет), представленный 0 и 1, я думаю, что это все равно будет работать. Еще одна вещь, которую следует отметить, что Тау Кендалл – это Симметричный , а это значит Тау (а, б) такой же, как Тау (б, а) Отказ

Давайте распилеем все комбинации топ-20 жанров и вычислить их коэффициент тау.

from scipy.stats import kendalltau
from itertools import product
rows = []
for genre_a, genre_b in product(genre_presence.columns.values, repeat=2):
    tau, p = kendalltau(genre_presence[genre_a].values, genre_presence[genre_b].values)
    rows.append({'genre_a': genre_a, 'genre_b': genre_b, 'tau': tau})
tau_values = pd.DataFrame(rows)
tau_values[:2]
1.000000 хип-хоп хип-хоп 0
-0.040954 хип-хоп поп 1

Если мы построим это хорошо, мы получаем следующий обзор «корреляций».

Мы сразу видим некоторые интересные кластеры. Мы можем видеть сильный тау между большинством электронных музыкальных жанров, таких как EDM , Электро , басовая ловушка , большая комната , бростеп и Электронная ловушка Отказ Тогда, глядя на хип-хоп мы можем видеть очень сильные коэффициенты с рэп и поп-рэп Ни один из которых не являются большими сюрпризами. Моя первоначальная гипотеза, которую поп Было бы коррелировано с хип-хопом было разграблено, хотя. Поп кажется, более сильно связан с EDM и некоторые другие электронные жанры, а имеют отрицательный тау с жанрами, связанными с хип-хоп, как хип-хоп (-0.29), поп-рэп (-0.28) и рэп (-0,32).

В этом обзоре, я думаю, что есть две интересные идеи:

  • Сильный коэффициент между Сознательный хип-хоп и Западное побережье рэп . Я на самом деле не ожидал этого, но, вероятно, приписывается художникам, таких как Кендрик Ламар, который занимается социальными и политическими вопросами в их лирике. Кроме того, города, такие как Комптон, сыграли большую роль в западном побережье хип-хоп, и часто были сильно связаны с их социальной и экономической ситуацией (также для Кендрика Ламара).
  • Сильный коэффициент между G-Funk и Детройт хип-хоп . G-Funk – это поджанр хип-хоп, который возник на Западном побережье, а Детройт хип-хоп, как говорит название, поступает из Детройта. Сильный коэффициент между G-Funk и Западное побережье рэп возможно, был более ожидан. Интересно увидеть, но я не буду глубже в этих результатах.

Это очень интересный анализ, на мой взгляд, но и один из более сложных. Я подошел к проблеме следующим образом, учитывая данные, которые у меня были.

  1. Подсчитайте частоту каждого жанра на определенный интервал, ежемесячно в этом случае.
  2. Разделите эти номера на общих пьесах для этих интервалов, поэтому мы получаем процент от общего количества этого месяца. Этот номер означает, сколько песни имели этот жанр. Это означает, что эти проценты не будут суммы на одну (или вы знаете, могут, но они не должны).
  3. Сортировать эти проценты и извлечь ежемесячную топ-5.

Шаг 1 : подсчитайте частоту на интервал. Мы не делаем это только для топ-н-жанров, но для все Жанры. Это, естественно, приводит к большому количеству колонн и очень широкое датафарам.

# Step 1. Count all genre occurences per month.
counters_per_month = []
unique_years = comb.year.sort_values().unique()
unique_months = comb.month.sort_values().unique()
for year, month in tqdm(product(unique_years, unique_months), total=len(unique_years)*len(unique_months)):
    if len(comb.loc[(comb.year == year) & (comb.month == month)]) > 0:
        counter = {'year': year, 'month': month}
        for i, row in comb.loc[(comb.year == year) & (comb.month == month)].iterrows():
            for genre in row[[f'genre_{x}' for x in range(21)]]:  # the genre columns are named '0' to '20'.
                counter[genre] = counter.get(genre, 0) + 1
        counters_per_month.append(counter)

Поставить counts_per_month В DataFrame и рассчитайте общие песни, сыгранные в месяц.

counts_per_genre_per_month = pd.DataFrame(counters_per_month)    
monthly_sum = df.groupby(['year', 'month']).size().reset_index().rename(columns={0: 'count'})

Шаг 2 : Затем мы нормализуем все жанры по количеству песен, сыгранных в этот период времени.

# 2.Normalize all genre counts by the number of songs played in that time period. 

# Select all columns except the time columns
columns = counts_per_genre_per_month.columns.tolist()
columns.remove('year')
columns.remove('month')

for i, row in monthly_sum.iterrows():
    counts_per_genre_per_month.loc[(counts_per_genre_per_month.year == row.year) & (counts_per_genre_per_month.month == row.month), columns] = counts_per_genre_per_month.loc[(counts_per_genre_per_month.year == row.year) & (counts_per_genre_per_month.month == row.month), columns] / row['count']

Чтобы получить более чистые визуальные, мы удаляем какие-либо данные до августа 2016 года.

counts_per_genre_per_month_filtered = counts_per_genre_per_month.loc[
    (counts_per_genre_per_month.year > 2016) 
    | (
          (counts_per_genre_per_month.year == 2016) 
          & (counts_per_genre_per_month.month > 8)
    )]

Теперь у нас есть DataFrame с 863 столбцами, что соответствует 861 разным жанрам. Это DataFrame имеет все жанры и какой процент от общего количества игр они присутствовали как жанр. Имейте в виду, что художник/песня обычно имеет более одного жанра, поэтому сумма этих фракций не является 1. Это dataframe выглядит так:

Наши данные теперь выглядят следующим образом:

16.689922 0.387597 0.519380 2016 9 0.007752 Нан 0.449612 0.488372 16 Нан 0.069767 Нан Нан Нан Нан Нан Нан 0.038760 Нан Нан
16.469657 0.343008 0.337731 2016 10 0.026385 Нан 0.313984 0.279683 17 Нан 0.036939 Нан Нан Нан Нан Нан Нан 0.055409 Нан Нан

Шаг 3 : Сортировать с учетом этих значений и извлечь топ 5. К сожалению, данные не в форме, которые мы можем сделать это (по крайней мере для моих знаний), поэтому нам нужно немного преобразовать его немного, переместившись от широкого до длинного формата данных и отфиксировать некоторые значения.

Расплавление данных DataFrame приводит к одному ряду на процент на за жанре за времяюнту. Это облегчает заговор с Altair. Кроме того, мы создаем столбец DateTime с нашего года + месяцы столбцов, что также лучше для использования Altair.

counts_per_genre_per_month_melted = pd.melt(
    counts_per_genre_per_month_filtered, 
    id_vars=['year', 'month'], 
    value_vars=columns, 
    var_name='genre', 
    value_name='percentage'
)
counts_per_genre_per_month_melted['datetime'] = pd.to_datetime(
    counts_per_genre_per_month_melted.month.astype(str) 
    + '-' + counts_per_genre_per_month_melted.year.astype(str), 
    format='%m-%Y')

Падение колонн, где либо жанр, либо проценты NAN. Это уменьшает количество рядов еще больше, так что принятие N-по более позднее будет быстрее.

counts_per_genre_per_month_melted = counts_per_genre_per_month_melted.dropna(
    subset=['percentage', 'genre']
)

Что дает нам:

2016 Восточный берег хип-хоп 9 2016-09-01 0 0.038760
2016 Восточный берег хип-хоп 10 2016-10-01 1 0.055409

Это выглядит великолепно! Но есть одна проблема, и это то, что у нас, вероятно, имеем слишком много строк для Altair. У нас почти 7k ряды, а максимам Altair – 5k. Не так уж плохо, но нам все еще нужно удалить кучу рядов. Но это нормально, так как мы заинтересованы только в топ-5 в каждом месяце. Используя Pands ‘ .группа по и .nlargest. Мы можем извлечь это довольно легко. Мы извлекаем те индексы оставшихся рядов и индекса в расплавленную таблицу данных, чтобы иметь только строки в топ-5 для каждого месяца.

top_genres_per_month_with_perc = counts_per_genre_per_month_melted.loc[
    (counts_per_genre_per_month_melted.groupby(['year', 'month'])
    .percentage.nlargest(5)
    .reset_index().level_2.values), :]

top_genres_per_month_with_perc.set_index(
    ['year', 'month']
).head(5)
2016 9 рэп 2016-09-01 0.519380
9 поп-рэп 0.488372 2016-09-01
9 хип-хоп 0.449612 2016-09-01
9 поп 0.387597 2016-09-01
9 Инди поп-рэп 0.131783 2016-09-01

И у нас осталось только 145 строк, поэтому мы можем использовать его с Altair 😎.

На графике ниже происходит много. На оси X у нас есть время, в то время как на оси Y у нас есть нормализованные проценты из 5 лучших жанров. Это означает, что для каждого месяца топ-5 процентов жанров для представления 1. Это может быть трудно понять, поэтому я поставил не нормализованный рядом с этим сюжетом, чтобы сделать разницу ясно. Некоторые цвета используются дважды, но в Altair нет цветовой схемы, которая поддерживает более 20 цветов, поэтому это придется делать сейчас 😉. Вы можете навешивать на панели, чтобы получить детали этих баров и щелкнуть на элементах Легенда, чтобы выделить жанр.

Верхние жанры с процентами 📊

Следующий сюжет очень приятный, когда он интерактивный, поэтому, пожалуйста, проверьте, что один на мой собственный Сайт

Есть определенно некоторые интересные вещи в тезисных участках. Мы видим некоторые последовательные участники, которые мы также видели в самых слушающих жанрах в целом, так что это не большой сюрприз. Например, они включают рэп , EDM и хип-хоп Отказ

  • Сезонные эффекты : Что вполне интересно, состоит в том, чтобы увидеть, когда очень распространенные жанры не доминируют на графике, вроде в декабре 2016 года. Как в ноябре, по декабре 2016 года, мы видим, что я был в очень сильном рождественском настроении, с Рождество Покрытие 16% песен в ноябре и 51% (!) В декабре. Верхние жанры в декабре Взрослый стандарт (Что бы это ни было), Легко слушать , Рождество и Lounge Отказ Они определенно находятся в одном сегменте, поэтому неудивительно, что эти другие жанры появляются рядом с Рождеством в тяжелом рождественском месяце. Мы не видим этого сезонного эффекта в 2017 и 2018 годах, но те годы, когда моя рождественская музыка призывает было чуть меньше, поэтому эта капля объяснима. Вместо рождества, в декабре 2018 года Эмо рэп в моих лучших 5 жанрах 🤔. Это может быть интересно посмотреть в другом посте в блоге.
  • Электронные периоды : Что-то еще, что выделяется, это то, что есть Электронная музыка Периоды, как и июль, июль и август 2017 года и январе 2018 года. Тем не менее, оба EDM и Electro House Присутствуют в основном каждый месяц как высокие бомбардиры, поэтому я определенно вентилятор в целом. Но эти пиковые месяцы все еще выделяются.
  • Рост рэпа : Последнее, что интересно, вероятно, факт что рэп и хип-хоп почти исключительно были лучшими 2 с февраля 2018 года по январь 2019 года. Это указывает на уход от более электронных жанров и более для хип-хопа. Возможная причина этого может быть переход к более настроенным пьесам для электронной музыки, которые, как правило, не в Spotify, а на платформах, таких как YouTube. В противном случае это может быть просто фактическое сдвижение предпочтений. Тем не менее, я все еще слушаю много таких музыки, поэтому я подозреваю, что первый. Глядя на данные с 2019 и 2020 года может дать некоторое понимание в этом.

Лучшие жанры без процентов 🏆

Итак, мы видели, как жанры относятся друг к другу с точки зрения процентов в месяц. Мы также можем посмотреть, какие лучшие жанры в течение месяца, но это определенно может быть улучшено. Я действительно просто хочу список с лучшими 5 жанрами в месяц, в идеале легко читаемого и довольно близко к примеру, которое мы имели от Last.fm.

Как напоминание, что выглядело так:

Мы можем получить список лучших жанров в месяц путем группировки, а затем применяя список на серии.

top_genres_per_month = (top_genres_per_month_with_perc
    .groupby(['year',  'month'])
    .genre.apply(list)
    .reset_index()
)

top_genres_per_month[:2]
2016 [ Рэп, поп-рэп, хип-хоп, поп, инди поп-рэп] 9 0
2016 [EDM, POP, RAP, Electro House, хип-хоп] 10 1

Затем мы создаем Numpy Array из этих значений и примените их столбец по столбцу к новым столбцам DataFrame.

До тех пор, пока мы наконец не приеду в следующие данные датафарама. Теперь, еще в значительной степени то, что я хотел, поэтому я доволен результатом.

эдм рэп поп эдм стандарты для взрослых genre_1. Электро поп-рэп рэп рэп поп рэп эдм рэп рэп рэп рэп рэп рэп рэп рэп
поп поп-рэп эдм поп легко слушать genre_2. фильтр дома рэп поп-рэп поп-рэп эдм хип-хоп рэп поп-рэп хип-хоп эдм хип-хоп хип-хоп хип-хоп хип-хоп хип-хоп
стандарты для взрослых хип-хоп рок рэп рождество genre_3. танцевальный панк эдм хип-хоп хип-хоп Электро эдм Электро хип-хоп поп-рэп хип-хоп поп-рэп поп-рэп эдм поп-рэп поп-рэп
рождество поп танцевать поп Электро бездельничать genre_4. электронный хип-хоп сознательный хип-хоп поп бростеп поп хип-хоп эдм эдм поп-рэп эдм эдм поп-рэп поп поп
легко слушать Инди поп-рэп тропический дом хип-хоп Голландский хип-хоп genre_5. Альтернативный танец поп Западное побережье рэп сознательный хип-хоп электронная ловушка поп-рэп поп поп Электро Электро поп Электро поп эдм эмо рэп

Теперь, в моем оригинальном посте я покрасил этот стол, чтобы быть легче интерпретироваться, но Dev.to не позволяет:(. Поэтому, пожалуйста, смотрите оригинальный пост для полной версии.

Но это выглядело так!

Лучше получить 🚒 Потому что эта таблица 🔥.

Это действительно близко к заговору Last.fm, кроме линий между пунктами, которые требуют 10 лет опыта D3.JS. Мы видим какой-то подобный шаблон для тех, кто на более раннем сюжете, но также может видеть некоторые новые идеи. Здесь мы можем сосредоточиться на аномалии, которые присутствуют, как Инди поп-рэп , Голландский хип-хоп , Фильтр дома и сознательный хип-хоп . Они выделяются больше, используя это представление, чем раньше, которые больше сосредоточены на тенденциях.

Понимание

  • Еще электронные пики : Мы видим, что февраль 2017 года на самом деле также был пиком в электронной музыке, но из-за подобных цветов на предыдущем сюжете это было немного скрыто.
  • Чистый хип-хоп периоды : Кроме того, мы также можем увидеть, что есть некоторые чистые периоды хип-хопа, например, в апреле и мае 2017 года, где EDM и Electro Name вообще нет, и мы видим более конкретные жанры хип-хоп, как Западное побережье рэп и сознательный хип-хоп .

В части 2 мы приблизились к тому, какие жанры я слушаю, и как это развилось со временем. Было очень интересное понимание, как эффекты праздников, а также изменение музыкальных предпочтений к рэту. Мы также воссоздали сюжет от Last.fm, как минимум, как можно меньше. Я вполне доволен результатом, но определенно у меня новое уважение к стихийным аналитикам, которые должны сделать это в течение большего количества людей. Хотя обобщение также приносит некоторые преимущества, конечно. Делать эти анализы также улучшают мои навыки с пандами, потому что я ранее не работал так много со временными данными, поэтому это отличное упражнение. Также, чтобы посмотреть в детали .группы И как он работает на агрегатах Thumeries, а какие операции возможны были отличными. Например, я узнал, что вы можете сделать группу Groupby в индексе DateTime или столбце, как так:

df.groupby(df['datetime-column'].dt.year)

И даже многоиндексировать это за месяц/год, используя:

df.groupby([
    df['datetime-column'].dt.year, 
    df['datetime-column'].dt.month
])

Что очень круто и уборщик, чем то, что я использовал! Но я сочидясь.

Округление; Спасибо за чтение и торчащие со мной! Мне очень любопытно, какие результаты Part 3 принесет.

Темы для части 3:

  1. Анализ музыкальных особенностей, таких как энергия, тансирование и акустичность. Это числовые значения и, таким образом, позволяют различные визуализации, затем все дискретные значения этого блогопоста.
  2. Какие песни я слушаю это эмо рэп. Это, вероятно, довольно маленькая точка исследования, но все же мне очень любопытно.

Если вам понравился этот блогпост, не стесняйтесь обращаться со мной на LinkedIn или Twitter Отказ 😊

Spotify Analysis (2 части серии)

Оригинал: “https://dev.to/baukebrenninkmeijer/analyzing-my-spotify-listening-history-part-2-2c5i”