Exploring Trends and Patterns in Spotify Music Track Data
Statistical Data Analysis Project

Akshat Kumar Singh - s4044797
Gautham Krishna - s4063790
Preyasya P - s4073266

Last updated: 02 June, 2024

Introduction

Problem Statement

  1. Identify the most common key used in popular Spotify tracks.
  2. Analyze and visualize the relationship between the key of a song and other musical attributes such as bpm, danceability, and energy. 3.Investigate whether there is a significant difference in the mean, bpm(beats per minutes) between songs in the key notes through Hypothesis testing.
  3. Determine if there is an association between a song’s key and mode through Chi-Square testing.
  4. Provide insights into the musical attributes that characterize popular songs based on the analysis.

RPubs link information

Data

The dataset used for this analysis is sourced from Kaggle.com (Kaggle 2024). It includes 24 attributes for each of the 953 tracks.

music <- read_csv("C:/Users/preya/Documents/MAnalytics/AA/A2/Popular_Spotify_Songs.csv")

track name: The name of the track.
artist(s) name: The name(s) of the artist(s) who performed the track.
artist count: The number of artists involved in the track.
streams: The number of streams the track has received.
bpm: The beats per minute of the track.
key: The key in which the track is composed.
mode: The modality (major or minor) of the track.
danceability%: A measure of how suitable a track is for dancing.

Data

#removing columns with apple music, deezer and shazam music data
music_select <- music %>% select(-matches("apple"),-matches("deezer"),-matches("shazam"))

#removing rows that contain no missing data by subsetting complete observations
complete.cases(music_select) %>% sum()
music_new <- music_select[complete.cases(music_select), ]
music_new$key <- as.factor(music_new$key) 
music_new$key %>% levels()
##  [1] "A"  "A#" "B"  "C#" "D"  "D#" "E"  "F"  "F#" "G"  "G#"
music_new$mode <- as.factor(music_new$mode) 
music_new$mode %>% levels()
## [1] "Major" "Minor"

Descriptive Statistics and Visualisation

music_new$key_mode <- paste(music_new$key, music_new$mode, sep=" ")
key_counts <- music_new %>% count(key_mode) 
key_counts %>% arrange(desc(n)) %>% head() %>% kable()
key_mode n
C# Major 73
D Major 66
G Major 66
G# Major 63
C# Minor 47
B Minor 46
ggplot(music_new, aes(x = key, fill = key)) + geom_bar() +  
  scale_fill_brewer(palette = "Paired" ) +  theme_minimal() + labs(title = "Most Common Key Used in Popular Spotify Tracks", x = "Key", y = "Count")

music_new %>% summarise(Min = min(bpm), Q1 = quantile(bpm,probs = .25),
                        Median = median(bpm, na.rm = TRUE),
                        Q3 = quantile(bpm,probs = .75,na.rm = TRUE),
                        Max = max(bpm,na.rm = TRUE),
                        Mean = mean(bpm, na.rm = TRUE),
                        SD = sd(bpm, na.rm = TRUE)) %>% kable()
Min Q1 Median Q3 Max Mean SD
65 100 121 142 206 122.8425 28.19657
ggplot(music_new, aes(x = bpm)) +  geom_histogram(binwidth = 5, fill = "skyblue", color = "black") +  
  theme_minimal() + labs(title = "Tempo Distribution of Spotify Popular Tracks", x = "Tempo (BPM)", y = "Count")

boxplot(music_new$bpm, main="Box Plot of BPM(Beats per Minute", xlab="BPM",horizontal=TRUE, col = "lightgreen")
music_new$bpm %>% mean() %>% abline(v=.,col='black', lty = "dashed")

music_new$streams_mil <- as.numeric(music_new$streams)/10000000
ggplot(music_new, aes(x = `danceability_%`, y=streams_mil)) +  geom_point(size=0.7) +  
  labs(title = "Danceability Percentage of Spotify Popular Tracks", x = "Danceability (%)", y = "Streams (in millions)")

Hypothesis Testing

# Filter the dataset for songs in key C and key G
songs_C <- subset(music_new, key == 'C#')$bpm
songs_G <- subset(music_new, key == 'G')$bpm

# Check for normality
shapiro_test_C <- shapiro.test(songs_C)
shapiro_test_G <- shapiro.test(songs_G)

# Check for equal variances
var_test <- var.test(songs_C, songs_G)

# Perform t-test based on variance test result
t_test_result <- t.test(songs_C, songs_G, var.equal = var_test$p.value > 0.05)

# Extract confidence interval
conf_interval <- t_test_result$conf.int
# Print results
print(shapiro_test_C)
## 
##  Shapiro-Wilk normality test
## 
## data:  songs_C
## W = 0.95763, p-value = 0.0008201

Hypothesis Testing

print(shapiro_test_G)
## 
##  Shapiro-Wilk normality test
## 
## data:  songs_G
## W = 0.95775, p-value = 0.00357
print(var_test)
## 
##  F test to compare two variances
## 
## data:  songs_C and songs_G
## F = 0.99917, num df = 119, denom df = 95, p-value = 0.9908
## alternative hypothesis: true ratio of variances is not equal to 1
## 95 percent confidence interval:
##  0.6781117 1.4600121
## sample estimates:
## ratio of variances 
##          0.9991732

Hypothesis Testing

print(t_test_result)
## 
##  Two Sample t-test
## 
## data:  songs_C and songs_G
## t = 0.034956, df = 214, p-value = 0.9721
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -7.385157  7.651824
## sample estimates:
## mean of x mean of y 
##  122.3417  122.2083
print(conf_interval)
## [1] -7.385157  7.651824
## attr(,"conf.level")
## [1] 0.95

Hypothesis Testing

Categorical association

keymodetab <- table(music_new$key,music_new$mode)

chi <- chisq.test(keymodetab)
chi
## 
##  Pearson's Chi-squared test
## 
## data:  keymodetab
## X-squared = 75.841, df = 10, p-value = 3.263e-12

Categorical association

chi$observed
##     
##      Major Minor
##   A     41    33
##   A#    27    30
##   B     35    46
##   C#    73    47
##   D     66    15
##   D#    12    21
##   E     17    45
##   F     44    45
##   F#    30    43
##   G     66    30
##   G#    63    28
chi$expected
##     
##         Major    Minor
##   A  40.92882 33.07118
##   A# 31.52625 25.47375
##   B  44.80047 36.19953
##   C# 66.37106 53.62894
##   D  44.80047 36.19953
##   D# 18.25204 14.74796
##   E  34.29172 27.70828
##   F  49.22520 39.77480
##   F# 40.37573 32.62427
##   G  53.09685 42.90315
##   G# 50.33139 40.66861

Categorical association

chi$p.value
## [1] 3.263108e-12

Categorical association

keymode_prop <- music_new %>% group_by(key, mode) %>%  summarise(n = n()) %>% mutate(prop = n / sum(n)) 

ggplot(data = keymode_prop, aes(x = key, y = prop, fill=mode)) +
  geom_bar(position="dodge", stat="identity") + scale_fill_brewer(palette = "PuBu") +   theme_classic() 

Discussion

Major Findings

Strengths and Limitations

Strengths

1.Comprehensive Dataset: The dataset from Kaggle provided a rich source of information, including various musical attributes that allowed for in-depth analysis.

2.Robust Statistical Methods: The use of multiple statistical tests (e.g., t-test, chi-squared test) ensured that the findings were rigorously validated.

3.Visualizations: Effective use of visualizations (bar plots, histograms, scatter plots) made it easier to identify and communicate key trends and patterns.

Limitations

1.Dataset Size: The dataset, although extensive, was limited to 953 tracks, which may not be fully representative of all popular music on Spotify.

2.Genre Information: The lack of genre information in the dataset limited the ability to analyze trends across different music genres.

3.Temporal Scope: The dataset did not account for changes over time, such as shifts in musical trends or listener preferences.

Future Directions

1.Genre-Specific Analysis: Future investigations could include genre information to explore how trends vary across different types of music.

2.Temporal Analysis: Analyzing how musical attributes and their popularity change over time could provide insights into evolving trends in the music industry.

3.Additional Attributes: Including more attributes, such as lyrics sentiment or production techniques, could enrich the analysis and uncover new patterns.

Broader Dataset: Expanding the dataset to include more tracks and other music attributes like genre could provide a more comprehensive view of the music landscape.

Conclusion

References