Original size 661x928

Динамика популярности артистов и эволюция их хитов

PROTECT STATUS: not protected
The project is taking part in the competition

Введение в проект

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

big
Original size 1922x928

Датасет о музыкальных треках и артистах за 2009–2025 годы позволит выявить наиболее востребованные жанры, самых популярных исполнителей, а также ключевые аудио‑характеристики, влияющие на успех композиции. Для визуализации данных планируется использовать различные типы диаграмм: столбчатые, круговые, точечные и линейные графики для отображения динамики популярности артистов и особенностей их хитов.

big
Original size 3294x523

В анализе представлены артисты Taylor Swift, Drake, Bad Bunny, The Weeknd, Billie Eilish, Post Malone, Ariana Grande, Travis Scott, SZA и Doja Cat.

Выбрана неоновая палитра отсылает к цифровым музыкальным сервисам и передаёт энергию, фиолетово‑синие оттенки создают музыкальную атмосферу 2000-х, а тёмный фон, выбран для усиления свечения акцентов и делает графики читаемыми.

Цвета

Original size 2011x633

График 1

Горизонтальная диаграмма отображает десятку самых популярных артистов и их среднюю популярность. Здесь сравниваются уровни успеха исполнителей между собой и видны лидеры. ​

Original size 1389x890

import pandas as pd import matplotlib.pyplot as plt import numpy as np

if 'df' not in locals (): print («Создаем тестовый DataFrame…») np.random.seed (42) n_tracks = 1000 df = pd.DataFrame ({ 'artist_name': np.random.choice ([ 'Taylor Swift', 'Drake', 'Bad Bunny', 'The Weeknd', 'Billie Eilish', 'Post Malone', 'Ariana Grande', 'Travis Scott', 'SZA', 'Doja Cat' ], n_tracks), 'artist_popularity': np.random.normal (75, 15, n_tracks).clip (0, 100), 'track_popularity': np.random.normal (65, 20, n_tracks).clip (0, 100) })

MODERN_PALETTE = [ '

7209B7', '

000080', '

00FF00', '

1B1B1B', '

7209B7', '

000080', '

00FF00', '

1B1B1B', '

7209B7', '

000080' ]

top_artists = ( df.groupby ('artist_name')['artist_popularity'] .mean () .sort_values (ascending=False) .head (10) .reset_index () )

print («Топ-10 артистов:») print (top_artists)

fig, ax = plt.subplots (figsize=(14, 9)) title_font = {'family': 'serif', 'color': '#FFFFFF', 'size': 22, 'weight': 'bold'} label_font = {'fontstyle': 'italic', 'color': '#CCCCCC', 'size': 14}

bars = plt.barh ( range (len (top_artists)), top_artists['artist_popularity'], color=MODERN_PALETTE, alpha=0.9, edgecolor='#00FF00', linewidth=3 )

ax.set_facecolor ('#1B1B1B') fig.patch.set_facecolor ('#1B1B1B')

ax.spines['top'].set_visible (False) ax.spines['right'].set_visible (False) ax.spines['left'].set_color ('#FFFFFF') ax.spines['bottom'].set_color ('#FFFFFF') ax.spines['left'].set_linewidth (3) ax.spines['bottom'].set_linewidth (3)

plt.yticks (range (len (top_artists)), top_artists['artist_name'], fontdict=label_font) plt.xlabel ('Средняя популярность артиста', fontdict=title_font) plt.title ('Топ-10 самых популярных артистов', fontdict=title_font, pad=40)

for i, bar in enumerate (bars): width = bar.get_width () plt.text ( width + 1, bar.get_y () + bar.get_height () / 2, f'{width:.0f}', ha='left', va='center', fontdict={'color': '#FFFFFF', 'size': 13, 'weight': 'bold'}, bbox=dict (boxstyle="round, pad=0.4», facecolor=MODERN_PALETTE[i], alpha=0.6) )

ax.grid (axis='x', alpha=0.3, color='#00FF00')

plt.tight_layout () plt.savefig ('top_artists_modern_colors.jpg', dpi=300, bbox_inches='tight', facecolor='#1B1B1B') plt.show ()

График 2

Диаграмма с выделенными хитами показывает, как распределяются значения популярности треков: основная масса треков где соотносится порог хитов и как среднее значение.

import pandas as pd import matplotlib.pyplot as plt import numpy as np

if 'df' not in locals (): print («Создаем тестовый DataFrame…») np.random.seed (42) n_tracks = 1000 df = pd.DataFrame ({ 'artist_name': np.random.choice ([ 'Taylor Swift', 'Drake', 'Bad Bunny', 'The Weeknd', 'Billie Eilish', 'Post Malone', 'Ariana Grande', 'Travis Scott', 'SZA', 'Doja Cat' ], n_tracks), 'artist_popularity': np.random.normal (75, 15, n_tracks).clip (0, 100), 'track_popularity': np.random.normal (65, 20, n_tracks).clip (0, 100) })

pop_data = df['track_popularity'].dropna ()

fig, ax = plt.subplots (figsize=(14, 7)) title_font = {'family': 'serif', 'color': '#FFFFFF', 'size': 22, 'weight': 'bold'} label_font = {'fontstyle': 'italic', 'color': '#CCCCCC', 'size': 14}

plt.hist ( pop_data, bins=35, color='#000080', alpha=0.75, edgecolor='#1E90FF', linewidth=3, label='Все треки' )

hits_mask = pop_data > 80 plt.hist ( pop_data[hits_mask], bins=35, color='#7209B7', alpha=0.9, edgecolor='#8A2BE2', linewidth=3, label='Хиты (pop > 80)' )

plt.axvline (pop_data.mean (), color='#00FF00', linestyle='--', linewidth=4, label='Средняя популярность')

ax.set_facecolor ('#1B1B1B') fig.patch.set_facecolor ('#1B1B1B') ax.spines['top'].set_visible (False) ax.spines['right'].set_visible (False) ax.spines['left'].set_color ('#FFFFFF') ax.spines['bottom'].set_color ('#FFFFFF') ax.spines['left'].set_linewidth (3) ax.spines['bottom'].set_linewidth (3)

plt.xlabel ('Популярность трека', fontdict=title_font) plt.ylabel ('Количество треков', fontdict=title_font) plt.title ( 'Распределение популярности музыкальных треков\n' 'Синий — все треки, фиолетовый — хиты, зеленый — среднее', fontdict=title_font, pad=40 )

plt.legend ( prop=label_font, frameon=True, fancybox=True, shadow=True, edgecolor='#00FF00' )

ax.grid (axis='y', alpha=0.3, color='#00FF00')

plt.tight_layout () plt.savefig ('popularity_modern_colors.jpg', dpi=300, bbox_inches='tight', facecolor='#1B1B1B') plt.show ()

Original size 1174x737

График 3

Диаграмма показывает, как связаны между собой популярность артиста, популярность трека, danceability, energy и valence. По значениям корреляций видно, какие параметры почти не связаны, а какие немного растут или падают вместе.

required_cols = ['artist_popularity', 'track_popularity', 'danceability', 'energy', 'valence'] missing_cols = [col for col in required_cols if col not in df.columns]

if missing_cols: for col in missing_cols: df[col] = np.random.normal (0.65, 0.15, len (df)).clip (0, 1)

metrics = ['artist_popularity', 'track_popularity', 'danceability', 'energy', 'valence'] corr = df[metrics].corr ()

mask = np.triu (np.ones_like (corr, dtype=bool))

plt.figure (figsize=(10, 8)) colors = ['

1B1B1B', '

000080', '

7209B7', '

00FF00'] custom_cmap = LinearSegmentedColormap.from_list ('modern', colors, N=256)

ax = sns.heatmap ( corr, mask=mask, annot=True, fmt='.2f', cmap=custom_cmap, center=0, square=True, linewidths=2, linecolor='#FFFFFF', cbar_kws={'label': 'Корреляция', 'shrink': 0.8} )

plt.title ( 'Корреляции музыкальных метрик', fontdict={'family': 'serif', 'color': '#FFFFFF', 'size': 20, 'weight': 'bold'}, pad=20 )

plt.gca ().set_facecolor ('#1B1B1B') plt.gcf ().patch.set_facecolor ('#1B1B1B')

ax.tick_params (colors='#FFFFFF') plt.setp (ax.get_xticklabels (), color='#FFFFFF', rotation=45, ha='right') plt.setp (ax.get_yticklabels (), color='#FFFFFF')

cbar = ax.collections[0].colorbar cbar.ax.yaxis.label.set_color ('#FFFFFF') for t in cbar.ax.get_yticklabels (): t.set_color ('#FFFFFF')

for text in ax.texts: text.set_color ('#FFFFFF')

plt.tight_layout () plt.savefig ('correlation_triangle_modern.jpg', dpi=300, bbox_inches='tight', facecolor='#1B1B1B') plt.show ()

print (corr.round (2))

Original size 813x789

График 4

Кольцевая диаграмма показывает доли основных жанров (Hip‑Hop, Pop, Rock, Latin, Electronic, R& B) в датасете. По ней видно, какие жанры доминируют, а какие представлены примерно равными. ​

import pandas as pd import matplotlib.pyplot as plt import numpy as np

if 'df' not in locals () or 'genre' not in df.columns: print («Создаем тестовые данные…») np.random.seed (42) n_tracks = 1000 df = pd.DataFrame ({ 'artist_name': np.random.choice ([ 'Taylor Swift', 'Drake', 'Bad Bunny', 'The Weeknd', 'Billie Eilish' ], n_tracks), 'genre': np.random.choice (['Pop', 'Hip-Hop', 'Latin', 'R& B', 'Rock'], n_tracks), 'artist_popularity': np.random.normal (75, 15, n_tracks).clip (0, 100), 'track_popularity': np.random.normal (65, 20, n_tracks).clip (0, 100) })

genre_counts = df['genre'].value_counts ()

print (f"Найдено жанров: {len (genre_counts)}») print (genre_counts)

MODERN_PALETTE = ['

7209B7', '

000080', '

00FF00', '

1B1B1B', '#7209B7']

plt.figure (figsize=(12, 10))

num_genres = len (genre_counts) explode = [0.05 if i == 0 else 0 for i in range (num_genres)]

wedges, texts, autotexts = plt.pie ( genre_counts.values, colors=MODERN_PALETTE[: num_genres], autopct='%1.1f%%', startangle=90, explode=explode, shadow=True, textprops={'color': '#FFFFFF', 'fontsize': 12, 'weight': 'bold'} )

for autotext in autotexts: autotext.set_color ('#FFFFFF') autotext.set_fontweight ('bold') autotext.set_fontsize (12)

plt.rcParams['text.color'] = '#FFFFFF' plt.rcParams['axes.labelcolor'] = '#FFFFFF' plt.rcParams['xtick.color'] = '#FFFFFF' plt.rcParams['ytick.color'] = '#FFFFFF' plt.rcParams['legend.labelcolor'] = '#FFFFFF'

plt.gca ().set_facecolor ('#1B1B1B') plt.gcf ().patch.set_facecolor ('#1B1B1B')

plt.title ( 'Распределение треков по жанрам', color='#FFFFFF', fontsize=24, fontweight='bold', pad=30, family='serif' )

centre_circle = plt.Circle ((0, 0), 0.70, fc='#1B1B1B') plt.gca ().add_artist (centre_circle)

legend = plt.legend ( wedges, [f'{label} ({value:.1f}%)' for label, value in zip (genre_counts.index, genre_counts.values/genre_counts.sum ()*100)], title='Жанры: ', loc='center left', bbox_to_anchor=(1, 0, 0.5, 1), fontsize=13, title_fontsize=15, frameon=True, fancybox=True, shadow=True, facecolor='#1B1B1B', edgecolor='#00FF00' )

legend.get_title ().set_color ('#FFFFFF') legend.get_title ().set_fontweight ('bold')

for text in legend.get_texts (): text.set_color ('#FFFFFF')

plt.tight_layout () plt.savefig ('

Original size 1161x989

Линейный график показывает, как менялась средняя популярность треков с 2015 по 2025 годы. Можно увидеть годы с пиками и провалами, а также общий тренд — есть ли рост.

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

График 5

import pandas as pd import matplotlib.pyplot as plt import numpy as np

if 'df' not in locals () or 'year' not in df.columns: np.random.seed (42) years = np.arange (2015, 2026) df = pd.DataFrame ({ 'year': np.repeat (years, 100), 'artist_popularity': np.random.normal (70, 15, 1100).clip (0, 100), 'track_popularity': np.random.normal (65, 20, 1100).clip (0, 100), 'danceability': np.random.normal (0.65, 0.15, 1100).clip (0, 1) })

pop_by_year = df.groupby ('year')[['artist_popularity', 'track_popularity']].mean ()

plt.figure (figsize=(14, 8))

plt.rcParams['text.color'] = '#FFFFFF' plt.rcParams['axes.labelcolor'] = '#FFFFFF' plt.rcParams['xtick.color'] = '#FFFFFF' plt.rcParams['ytick.color'] = '#FFFFFF'

plt.plot ( pop_by_year.index, pop_by_year['artist_popularity'], marker='o', linewidth=4, markersize=10, color='#7209B7', label='Популярность артистов', markerfacecolor='#7209B7', markeredgecolor='#00FF00', markeredgewidth=3 )

plt.plot ( pop_by_year.index, pop_by_year['track_popularity'], marker='s', linewidth=4, markersize=10, color='#000080', label='Популярность треков', markerfacecolor='#000080', markeredgecolor='#00FF00', markeredgewidth=3 )

plt.gca ().set_facecolor ('#1B1B1B') plt.gcf ().patch.set_facecolor ('#1B1B1B')

plt.title ( 'Динамика популярности (2015-2025)', color='#FFFFFF', fontsize=24, fontweight='bold', pad=30, family='serif' )

plt.xlabel ('Год', color='#FFFFFF', fontsize=16, fontweight='bold') plt.ylabel ('Средняя популярность', color='#FFFFFF', fontsize=16, fontweight='bold')

plt.legend ( fontsize=13, frameon=True, fancybox=True, shadow=True, facecolor='#1B1B1B', edgecolor='#00FF00' )

plt.grid (True, alpha=0.3, color='#00FF00') plt.xticks (pop_by_year.index)

legend = plt.gca ().get_legend () legend.get_title ().set_color ('#FFFFFF') for text in legend.get_texts (): text.set_color ('#FFFFFF')

plt.tight_layout () plt.savefig ('popularity_line_chart.jpg', dpi=300, bbox_inches='tight', facecolor='#1B1B1B') plt.show ()

plt.rcParams['text.color'] = 'black' plt.rcParams['axes.labelcolor'] = 'black'

print (pop_by_year)

Original size 1389x789

График 6

Столбиковый график показывает, как менялась средняя популярность треков и артистов с 2015 по 2025 годы. Можно увидеть годы с пиками и провалами, а также общий тренд — есть ли рост.

import matplotlib.pyplot as plt

df_plot = ( df.groupby ('year')['track_popularity'] .mean () .reset_index () .sort_values ('year') )

x = df_plot['year'] y = df_plot['track_popularity']

plt.figure (figsize=(16, 8)) ax = plt.gca ()

ax.set_facecolor ('#000000') plt.gcf ().patch.set_facecolor ('#000000')

plt.bar ( x, y, width=1.0, color='#7209B7', edgecolor='#7209B7', alpha=0.45 )

plt.plot ( x, y, color='#00FF00', linewidth=2.5 )

plt.grid (True, which='both', axis='both', color='#00FF00', alpha=0.3)

for spine in ['top', 'right']: ax.spines[spine].set_visible (False) for spine in ['left', 'bottom']: ax.spines[spine].set_color ('#FFFFFF') ax.spines[spine].set_linewidth (1.5)

plt.xlabel ('Год', color='#FFFFFF', fontsize=14) plt.ylabel ('Средняя популярность треков', color='#FFFFFF', fontsize=14) plt.title ( 'Средняя популярность треков по годам', color='#FFFFFF', fontsize=22, fontweight='bold', pad=25 )

plt.xticks (color='#FFFFFF', fontsize=10) plt.yticks (color='#FFFFFF', fontsize=10)

plt.tight_layout () plt.savefig ('tracks_popularity_by_year_green_purple_style.jpg', dpi=300, facecolor='#000000', bbox_inches='tight') plt.show ()

Original size 1588x790

График 7

import matplotlib.pyplot as plt import seaborn as sns from matplotlib.colors import LinearSegmentedColormap

pivot = ( df.groupby (['artist_popularity', 'year'])['track_popularity'] .mean () .reset_index () .pivot (index='artist_popularity', columns='year', values='track_popularity'))

line_data = ( df.groupby ('year')['track_popularity'] .mean () .reset_index () .sort_values ('year'))

fig, ax = plt.subplots (figsize=(16, 9))

colors = ['

1B1B1B', '

000080', '

7209B7', '

00FF00'] cmap = LinearSegmentedColormap.from_list ('modern', colors, N=256)

ax.set_facecolor ('#1B1B1B') fig.patch.set_facecolor ('#000000')

sns.heatmap ( pivot, cmap=cmap, linewidths=0.3, linecolor='#1B1B1B', cbar_kws={'label': 'Средняя популярность треков'}, square=False, ax=ax)

ax2 = ax.twinx () ax2.plot ( line_data['year'], line_data['track_popularity'], color='#00FF00', linewidth=2.5 )

ax.set_xlabel ('Год', color='#FFFFFF', fontsize=14) ax.set_ylabel ('Популярность артиста', color='#FFFFFF', fontsize=14) ax2.set_ylabel ('Средняя популярность треков', color='#00FF00', fontsize=14)

ax.set_title ( 'Популярность треков по годам и популярности артистов', color='#FFFFFF', fontsize=20, weight='bold', pad=20 )

ax.tick_params (axis='x', colors='#FFFFFF', rotation=45) ax.tick_params (axis='y', colors='#FFFFFF') ax2.tick_params (axis='y', colors='#00FF00')

cbar = ax.collections[0].colorbar cbar.ax.yaxis.label.set_color ('#FFFFFF') for t in cbar.ax.get_yticklabels (): t.set_color ('#FFFFFF')

years = pivot.columns.values artist_pops = pivot.index.values

for i, ap in enumerate (artist_pops): for j, yr in enumerate (years): if not pd.isna (pivot.iloc[i, j]): ax.scatter ( j + 0.5, i + 0.5, s=10, c='#FFFFFF', edgecolors='#00FF00', linewidths=0.3 )

plt.tight_layout () plt.savefig ('heatmap_with_green_line_and_points.jpg', dpi=300, bbox_inches='tight', facecolor='#000000') plt.show ()

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

Original size 1475x889
We use cookies to improve the operation of the website and to enhance its usability. More detailed information on the use of cookies can be fo...
Show more