Topic: Finding the Relationship Between Song Characteristics and Spotify Popularity
Music is a major part of everyday life, and different songs have characteristics that make them appealing to listeners. Some songs have faster tempos, higher energy, or greater danceability, while others have more acoustic or instrumental elements. I wanted to explore whether these musical characteristics have any relationship with how popular a song becomes. This project uses a dataset containing 2,000 songs and 18 variables, including artist, song title, genre, release year, popularity, danceability, energy, loudness, tempo, and other audio features.
The dataset includes six categorical variables and twelve quantitative variables. The categorical variables describe information such as the artist, song title, genre, explicit content, musical key, and mode. The quantitative variables measure characteristics such as song duration, popularity, danceability, energy, acousticness, valence, and tempo. The dataset covers songs released between 1998 and 2020. I plan to focus on danceability, energy, valence, tempo, loudness, and other audio characteristics to investigate their relationships. I chose this topic because I enjoy listening to music and wanted to understand whether the musical qualities I notice in songs are related to their popularity.
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.2.1 ✔ readr 2.2.0
✔ forcats 1.0.1 ✔ stringr 1.6.0
✔ ggplot2 4.0.3 ✔ tibble 3.3.1
✔ lubridate 1.9.5 ✔ tidyr 1.3.2
✔ purrr 1.2.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readr)library(ggplot2)library(plotly)
Attaching package: 'plotly'
The following object is masked from 'package:ggplot2':
last_plot
The following object is masked from 'package:stats':
filter
The following object is masked from 'package:graphics':
layout
library(RColorBrewer)# Load the Spotify datasetspotify <-read_csv("C:/Users/akeeb/Downloads/spotifysongs.csv")
Rows: 2000 Columns: 18
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (3): artist, song, genre
dbl (14): duration_ms, year, popularity, danceability, energy, key, loudness...
lgl (1): explicit
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
artist song duration_ms explicit
Length :2000 Length :2000 Min. :113000 Mode :logical
N.unique : 835 N.unique :1879 1st Qu.:203580 FALSE:1449
N.blank : 0 N.blank : 0 Median :223280 TRUE :551
Min.nchar: 2 Min.nchar: 1 Mean :228748
Max.nchar: 41 Max.nchar: 114 3rd Qu.:248133
Max. :484146
year popularity danceability energy
Min. :1998 Min. : 0.00 Min. :0.1290 Min. :0.0549
1st Qu.:2004 1st Qu.:56.00 1st Qu.:0.5810 1st Qu.:0.6220
Median :2010 Median :65.50 Median :0.6760 Median :0.7360
Mean :2009 Mean :59.87 Mean :0.6674 Mean :0.7204
3rd Qu.:2015 3rd Qu.:73.00 3rd Qu.:0.7640 3rd Qu.:0.8390
Max. :2020 Max. :89.00 Max. :0.9750 Max. :0.9990
key loudness mode speechiness
Min. : 0.000 Min. :-20.514 Min. :0.0000 Min. :0.02320
1st Qu.: 2.000 1st Qu.: -6.490 1st Qu.:0.0000 1st Qu.:0.03960
Median : 6.000 Median : -5.285 Median :1.0000 Median :0.05985
Mean : 5.378 Mean : -5.512 Mean :0.5535 Mean :0.10357
3rd Qu.: 8.000 3rd Qu.: -4.168 3rd Qu.:1.0000 3rd Qu.:0.12900
Max. :11.000 Max. : -0.276 Max. :1.0000 Max. :0.57600
acousticness instrumentalness liveness valence
Min. :0.0000192 Min. :0.0000000 Min. :0.0215 Min. :0.0381
1st Qu.:0.0140000 1st Qu.:0.0000000 1st Qu.:0.0881 1st Qu.:0.3867
Median :0.0557000 Median :0.0000000 Median :0.1240 Median :0.5575
Mean :0.1289549 Mean :0.0152260 Mean :0.1812 Mean :0.5517
3rd Qu.:0.1762500 3rd Qu.:0.0000683 3rd Qu.:0.2410 3rd Qu.:0.7300
Max. :0.9760000 Max. :0.9850000 Max. :0.8530 Max. :0.9730
tempo genre
Min. : 60.02 Length :2000
1st Qu.: 98.99 N.unique : 59
Median :120.02 N.blank : 0
Mean :120.12 Min.nchar: 3
3rd Qu.:134.27 Max.nchar: 37
Max. :210.85
# Check for missing valuescolSums(is.na(spotify))
artist song duration_ms explicit
0 0 0 0
year popularity danceability energy
0 0 0 0
key loudness mode speechiness
0 0 0 0
acousticness instrumentalness liveness valence
0 0 0 0
tempo genre
0 0
# Check for duplicate rowssum(duplicated(spotify))
[1] 59
# Check the different genrescount(spotify, genre, sort =TRUE)
# A tibble: 59 × 2
genre n
<chr> <int>
1 pop 428
2 hip hop, pop 277
3 hip hop, pop, R&B 244
4 pop, Dance/Electronic 221
5 pop, R&B 178
6 hip hop 124
7 hip hop, pop, Dance/Electronic 78
8 rock 58
9 rock, pop 43
10 Dance/Electronic 41
# ℹ 49 more rows
# Check the number of songs per yearcount(spotify, year, sort =TRUE)
# Clean and transform the datasetspotify_clean <- spotify %>%mutate(explicit =factor(explicit),genre =factor(genre),mode =factor(mode),key =factor(key),duration_minutes = duration_ms /60000 )
# Explore the most common music genresspotify_clean %>%count(genre, sort =TRUE) %>%head(10) %>%ggplot(aes(x =reorder(genre, n), y = n)) +geom_col(fill ="blue") +coord_flip() +labs(title ="Top 10 Most Common Music Genres",x ="Genre",y ="Number of Songs" ) +theme_minimal()
# Explore the distribution of danceabilityggplot(spotify_clean, aes(x = danceability)) +geom_histogram(bins =30,fill ="purple",color ="white" ) +labs(title ="Distribution of Song Danceability",x ="Danceability Score",y ="Number of Songs" ) +theme_minimal()
# Explore the distribution of energyggplot(spotify_clean, aes(x = energy)) +geom_histogram(bins =30,fill ="orange",color ="white" ) +labs(title ="Distribution of Song Energy",x ="Energy Score",y ="Number of Songs" ) +theme_minimal()
# Create a multiple linear regression modelmodel <-lm( danceability ~ valence + energy + tempo + speechiness + acousticness + liveness + loudness,data = spotify_clean)# View the regression resultssummary(model)
Call:
lm(formula = danceability ~ valence + energy + tempo + speechiness +
acousticness + liveness + loudness, data = spotify_clean)
Residuals:
Min 1Q Median 3Q Max
-0.49317 -0.07052 0.00470 0.08096 0.32338
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.8359025 0.0278086 30.059 < 2e-16 ***
valence 0.2979256 0.0129183 23.062 < 2e-16 ***
energy -0.2908496 0.0255238 -11.395 < 2e-16 ***
tempo -0.0007102 0.0001006 -7.061 2.28e-12 ***
speechiness 0.1624508 0.0280633 5.789 8.22e-09 ***
acousticness -0.1284572 0.0171954 -7.470 1.19e-13 ***
liveness -0.1105398 0.0192385 -5.746 1.06e-08 ***
loudness 0.0033060 0.0018175 1.819 0.0691 .
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.119 on 1992 degrees of freedom
Multiple R-squared: 0.2846, Adjusted R-squared: 0.282
F-statistic: 113.2 on 7 and 1992 DF, p-value: < 2.2e-16