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

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

В анализе представлены артисты Taylor Swift, Drake, Bad Bunny, The Weeknd, Billie Eilish, Post Malone, Ariana Grande, Travis Scott, SZA и Doja Cat.
Выбрана неоновая палитра отсылает к цифровым музыкальным сервисам и передаёт энергию, фиолетово‑синие оттенки создают музыкальную атмосферу 2000-х, а тёмный фон, выбран для усиления свечения акцентов и делает графики читаемыми.
Цвета
График 1
Горизонтальная диаграмма отображает десятку самых популярных артистов и их среднюю популярность. Здесь сравниваются уровни успеха исполнителей между собой и видны лидеры.
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 = [ '
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 ()
График 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 = ['
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))
График 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 = ['
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 ('
Линейный график показывает, как менялась средняя популярность треков с 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)
График 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 ()
График 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 = ['
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 ()
График иллюстрирует, как по годам распределяются артисты по уровню собственной популярности и популярности их треков, показывая, что высокая популярность артиста обычно сопровождается высокими значениями средней популярности его песен.