# load libraries
library(dplyr)
library(factoextra)
library(FactoMineR)
spotify <- read.csv("SpotifyFeatures.csv")
head(spotify)
#> ï..genre artist_name track_name
#> 1 Movie Henri Salvador C'est beau de faire un Show
#> 2 Movie Martin & les fées Perdu d'avance (par Gad Elmaleh)
#> 3 Movie Joseph Williams Don't Let Me Be Lonely Tonight
#> 4 Movie Henri Salvador Dis-moi Monsieur Gordon Cooper
#> 5 Movie Fabien Nataf Ouverture
#> 6 Movie Henri Salvador Le petit souper aux chandelles
#> track_id popularity acousticness danceability duration_ms
#> 1 0BRjO6ga9RKCKjfDqeFgWV 0 0.611 0.389 99373
#> 2 0BjC1NfoEOOusryehmNudP 1 0.246 0.590 137373
#> 3 0CoSDzoNIKCRs124s9uTVy 3 0.952 0.663 170267
#> 4 0Gc6TVm52BwZD07Ki6tIvf 0 0.703 0.240 152427
#> 5 0IuslXpMROHdEPvSl1fTQK 4 0.950 0.331 82625
#> 6 0Mf1jKa8eNAf1a4PwTbizj 0 0.749 0.578 160627
#> energy instrumentalness key liveness loudness mode speechiness tempo
#> 1 0.9100 0.000 C# 0.3460 -1.828 Major 0.0525 166.969
#> 2 0.7370 0.000 F# 0.1510 -5.559 Minor 0.0868 174.003
#> 3 0.1310 0.000 C 0.1030 -13.879 Minor 0.0362 99.488
#> 4 0.3260 0.000 C# 0.0985 -12.178 Major 0.0395 171.758
#> 5 0.2250 0.123 F 0.2020 -21.150 Major 0.0456 140.576
#> 6 0.0948 0.000 C# 0.1070 -14.970 Major 0.1430 87.479
#> time_signature valence
#> 1 4/4 0.814
#> 2 4/4 0.816
#> 3 5/4 0.368
#> 4 4/4 0.227
#> 5 4/4 0.390
#> 6 4/4 0.358
Berdasarkan data spotify di atas, saya ingin menganalisa fitur audio apa yang paling berpengaruh dalam popularitas sebuah lagu. Pertama-tama langsung melakukan proses EDA.
str(spotify)
#> 'data.frame': 232725 obs. of 18 variables:
#> $ ï..genre : chr "Movie" "Movie" "Movie" "Movie" ...
#> $ artist_name : chr "Henri Salvador" "Martin & les fées" "Joseph Williams" "Henri Salvador" ...
#> $ track_name : chr "C'est beau de faire un Show" "Perdu d'avance (par Gad Elmaleh)" "Don't Let Me Be Lonely Tonight" "Dis-moi Monsieur Gordon Cooper" ...
#> $ track_id : chr "0BRjO6ga9RKCKjfDqeFgWV" "0BjC1NfoEOOusryehmNudP" "0CoSDzoNIKCRs124s9uTVy" "0Gc6TVm52BwZD07Ki6tIvf" ...
#> $ popularity : int 0 1 3 0 4 0 2 15 0 10 ...
#> $ acousticness : num 0.611 0.246 0.952 0.703 0.95 0.749 0.344 0.939 0.00104 0.319 ...
#> $ danceability : num 0.389 0.59 0.663 0.24 0.331 0.578 0.703 0.416 0.734 0.598 ...
#> $ duration_ms : int 99373 137373 170267 152427 82625 160627 212293 240067 226200 152694 ...
#> $ energy : num 0.91 0.737 0.131 0.326 0.225 0.0948 0.27 0.269 0.481 0.705 ...
#> $ instrumentalness: num 0 0 0 0 0.123 0 0 0 0.00086 0.00125 ...
#> $ key : chr "C#" "F#" "C" "C#" ...
#> $ liveness : num 0.346 0.151 0.103 0.0985 0.202 0.107 0.105 0.113 0.0765 0.349 ...
#> $ loudness : num -1.83 -5.56 -13.88 -12.18 -21.15 ...
#> $ mode : chr "Major" "Minor" "Minor" "Major" ...
#> $ speechiness : num 0.0525 0.0868 0.0362 0.0395 0.0456 0.143 0.953 0.0286 0.046 0.0281 ...
#> $ tempo : num 167 174 99.5 171.8 140.6 ...
#> $ time_signature : chr "4/4" "4/4" "5/4" "4/4" ...
#> $ valence : num 0.814 0.816 0.368 0.227 0.39 0.358 0.533 0.274 0.765 0.718 ...
# Mengetahui apakah terdapat missing value di setiap kolom
colSums(is.na(spotify))
#> ï..genre artist_name track_name track_id
#> 0 0 0 0
#> popularity acousticness danceability duration_ms
#> 0 0 0 0
#> energy instrumentalness key liveness
#> 0 0 0 0
#> loudness mode speechiness tempo
#> 0 0 0 0
#> time_signature valence
#> 0 0
# Cek summary dari data spotify
summary(spotify)
#> ï..genre artist_name track_name track_id
#> Length:232725 Length:232725 Length:232725 Length:232725
#> Class :character Class :character Class :character Class :character
#> Mode :character Mode :character Mode :character Mode :character
#>
#>
#>
#> popularity acousticness danceability duration_ms
#> Min. : 0.00 Min. :0.0000 Min. :0.0569 Min. : 15387
#> 1st Qu.: 29.00 1st Qu.:0.0376 1st Qu.:0.4350 1st Qu.: 182857
#> Median : 43.00 Median :0.2320 Median :0.5710 Median : 220427
#> Mean : 41.13 Mean :0.3686 Mean :0.5544 Mean : 235122
#> 3rd Qu.: 55.00 3rd Qu.:0.7220 3rd Qu.:0.6920 3rd Qu.: 265768
#> Max. :100.00 Max. :0.9960 Max. :0.9890 Max. :5552917
#> energy instrumentalness key liveness
#> Min. :0.0000203 Min. :0.0000000 Length:232725 Min. :0.00967
#> 1st Qu.:0.3850000 1st Qu.:0.0000000 Class :character 1st Qu.:0.09740
#> Median :0.6050000 Median :0.0000443 Mode :character Median :0.12800
#> Mean :0.5709577 Mean :0.1483012 Mean :0.21501
#> 3rd Qu.:0.7870000 3rd Qu.:0.0358000 3rd Qu.:0.26400
#> Max. :0.9990000 Max. :0.9990000 Max. :1.00000
#> loudness mode speechiness tempo
#> Min. :-52.457 Length:232725 Min. :0.0222 Min. : 30.38
#> 1st Qu.:-11.771 Class :character 1st Qu.:0.0367 1st Qu.: 92.96
#> Median : -7.762 Mode :character Median :0.0501 Median :115.78
#> Mean : -9.570 Mean :0.1208 Mean :117.67
#> 3rd Qu.: -5.501 3rd Qu.:0.1050 3rd Qu.:139.05
#> Max. : 3.744 Max. :0.9670 Max. :242.90
#> time_signature valence
#> Length:232725 Min. :0.0000
#> Class :character 1st Qu.:0.2370
#> Mode :character Median :0.4440
#> Mean :0.4549
#> 3rd Qu.:0.6600
#> Max. :1.0000
# Cek faktor unik per kolom dari ï..genre, artist_name, key, mode, dan time_signature
length(unique(spotify$ï..genre))
#> [1] 27
length(unique(spotify$artist_name))
#> [1] 14564
length(unique(spotify$key))
#> [1] 12
length(unique(spotify$mode))
#> [1] 2
length(unique(spotify$time_signature))
#> [1] 5
Mengecek outlier karena outlier sangat mempengaruhi hasil k-means
clustering. Maka dari itu saya akan menghilangkan outlier yang ada pada
data spotify. Outlier dapat dideteksi menggunakan biplot
dari hasil PCA. Scaling dilakukan agar antar variabel memiliki skala
yang tidak jauh berbeda.
# Membuang kolom factor agar seluruh data menjadi kolom numerik
spotify_no_chr <- spotify %>%
select(-c("ï..genre", "artist_name", "track_name", "track_id", "key", "mode", "time_signature"))
# Sclaing dengan data spotify numerik
spotify_scale <- scale(spotify_no_chr)
PCA dengan data scale
spotify_scale_pca <- PCA(spotify_scale,
scale.unit = TRUE,
graph = FALSE,
ncp = 6)
# Ploting PCA
plot.PCA(
x = spotify_scale_pca,
choix = "ind",
select = "contrib 5"
)
Menghilangkan outliers yang ada pada data spotify
outliers <- c(218541, 210710, 210094,217130)
spotify_clean <- spotify_no_chr[-outliers,]
glimpse(spotify_clean)
#> Rows: 232,721
#> Columns: 11
#> $ popularity <int> 0, 1, 3, 0, 4, 0, 2, 15, 0, 10, 0, 2, 4, 3, 0, 0, 0, ~
#> $ acousticness <dbl> 0.61100, 0.24600, 0.95200, 0.70300, 0.95000, 0.74900,~
#> $ danceability <dbl> 0.389, 0.590, 0.663, 0.240, 0.331, 0.578, 0.703, 0.41~
#> $ duration_ms <int> 99373, 137373, 170267, 152427, 82625, 160627, 212293,~
#> $ energy <dbl> 0.9100, 0.7370, 0.1310, 0.3260, 0.2250, 0.0948, 0.270~
#> $ instrumentalness <dbl> 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.123~
#> $ liveness <dbl> 0.3460, 0.1510, 0.1030, 0.0985, 0.2020, 0.1070, 0.105~
#> $ loudness <dbl> -1.828, -5.559, -13.879, -12.178, -21.150, -14.970, -~
#> $ speechiness <dbl> 0.0525, 0.0868, 0.0362, 0.0395, 0.0456, 0.1430, 0.953~
#> $ tempo <dbl> 166.969, 174.003, 99.488, 171.758, 140.576, 87.479, 8~
#> $ valence <dbl> 0.8140, 0.8160, 0.3680, 0.2270, 0.3900, 0.3580, 0.533~
Melakukan scaling dengan data yang sudah tidak memiliki outlier.
spotify_clean_scale <- scale(spotify_clean)
Menggunakan elbow method untuk menentukan \(k\) optimum. Fungsi
kmeansTunning berikut dibuat untuk menampilkan penurunan
nilai WSS dari k-means clustering dengan nilai \(k\) dari 2 sampai maxK.
# fungsi untuk plot elbow method
RNGkind(sample.kind = "Rounding")
kmeansTunning <- function(data, maxK) {
withinall <- NULL
total_k <- NULL
for (i in 2:maxK) {
set.seed(567)
temp <- kmeans(data,i)$tot.withinss
withinall <- append(withinall, temp)
total_k <- append(total_k,i)
}
plot(x = total_k, y = withinall, type = "o", xlab = "Number of Cluster", ylab = "Total within")
}
kmeansTunning(data = spotify_clean_scale, maxK = 20)
Berdasarkan hasil kmeansTunning di atas, saya menentukan \(k\) optimum 15 yang menurut saya paling cocok untuk digunakan sebagai center dalam melakukan K-means clustering.
RNGkind(sample.kind = "Rounding")
set.seed(567)
# k-means clustering
spotify_km <- kmeans(x = spotify_clean_scale, centers = 15)
str(spotify_km)
#> List of 9
#> $ cluster : Named int [1:232721] 9 9 13 13 13 13 1 13 6 6 ...
#> ..- attr(*, "names")= chr [1:232721] "1" "2" "3" "4" ...
#> $ centers : num [1:15, 1:11] -1.1286 -0.5635 -0.6307 0.0163 0.3475 ...
#> ..- attr(*, "dimnames")=List of 2
#> .. ..$ : chr [1:15] "1" "2" "3" "4" ...
#> .. ..$ : chr [1:11] "popularity" "acousticness" "danceability" "duration_ms" ...
#> $ totss : num 2559920
#> $ withinss : num [1:15] 58710 47588 60622 63246 65367 ...
#> $ tot.withinss: num 934690
#> $ betweenss : num 1625230
#> $ size : int [1:15] 9564 11220 10941 10615 19482 11870 26693 25813 21561 104 ...
#> $ iter : int 9
#> $ ifault : int 0
#> - attr(*, "class")= chr "kmeans"
Melakukan profiling menggunakan data spotify_clean
karena data tersebut sudah tidak memiliki outlier namun belum dilakukan
scaling. Kemudian menambahkan kolom kelompok pada data tersebut.
spotify_clean$kelompok <- as.factor(spotify_km$cluster)
Melakukan profiling cluster dengan mencari nilai rata-rata untuk masing-masing produk.
spotify_centroid <- spotify_clean %>%
group_by(kelompok) %>%
summarise_all(mean)
spotify_centroid
#> # A tibble: 15 x 12
#> kelompok popularity acousticness danceability duration_ms energy
#> <fct> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 1 20.6 0.802 0.562 218932. 0.670
#> 2 2 30.9 0.902 0.226 217545. 0.0826
#> 3 3 29.7 0.775 0.402 203658. 0.278
#> 4 4 41.4 0.217 0.541 257891. 0.722
#> 5 5 47.4 0.121 0.436 258888. 0.707
#> 6 6 14.5 0.570 0.691 170511. 0.448
#> 7 7 59.5 0.179 0.737 217845. 0.587
#> 8 8 46.0 0.0932 0.521 235463. 0.758
#> 9 9 40.6 0.123 0.561 215561. 0.803
#> 10 10 18.1 0.689 0.541 3051794. 0.490
#> 11 11 26.9 0.818 0.288 697809. 0.211
#> 12 12 38.2 0.173 0.589 264108. 0.673
#> 13 13 16.4 0.912 0.325 219354. 0.166
#> 14 14 47.4 0.177 0.725 226473. 0.718
#> 15 15 49.2 0.682 0.533 235238. 0.351
#> # ... with 6 more variables: instrumentalness <dbl>, liveness <dbl>,
#> # loudness <dbl>, speechiness <dbl>, tempo <dbl>, valence <dbl>
Agar dapat lebih mudah memahami clustering data spotify, dilakukan visualisasi bar plot dari nilai centroid.
library(tidyr)
spotify_centroid %>%
pivot_longer(-kelompok) %>%
ggplot(aes(x = kelompok, y = value, fill = kelompok)) +
geom_col() +
facet_wrap(~name, scales = "free_y")
# menampilkan kelompok dengan nilai terendah dan tertinggi untuk masing-masing fitur audio
spotify_centroid %>%
pivot_longer(-kelompok) %>%
group_by(name) %>%
summarize(kelompok_min = which.min(value),
kelompok_max = which.max(value))
#> # A tibble: 11 x 3
#> name kelompok_min kelompok_max
#> <chr> <int> <int>
#> 1 acousticness 8 13
#> 2 danceability 2 7
#> 3 duration_ms 6 10
#> 4 energy 2 9
#> 5 instrumentalness 1 2
#> 6 liveness 2 1
#> 7 loudness 2 9
#> 8 popularity 6 7
#> 9 speechiness 2 1
#> 10 tempo 2 9
#> 11 valence 2 14
Insight:
Berdasarkan hasil dari tampilan 15 kelompok dari masing-masing fitur audio, menghasilkan:
danceability, energy, liveness,
loudness, speechiness, tempo, dan
valance. Hal tersebut dapat diartikan bahwa lagu dengan
cluster nomor 2 memiliki lagu yang lebih lembut dan memiliki nuansa
tenang.energy, loudness, dan tempo.instrumentalness, duration_ms,
dan speechiness.