Spotify Song Clustering with K-Means
Explanation
Permulaan
Hallo ini adalah Rpubs ke-tujuh saya mengenai Unsupervised Learning Dengan Metode Clustering menggunakan Algoritma K-Means, semoga bermanfaat :)
Tentang Data
Dataset ini adalah dataset yang berisikan pengukuran lagu pada spotify berdasarkan kriteria tertentu, mulai dari genre, popularity, time-signature, dll.
Beberapa kolom yang terdapat di dalam nya :
- genre : jenis / kategori lagu
- artist_name : nama artis pemilik lagu
- track_name : nama dari lagu
- track_id : id unik dari setiap track lagu
- popularity : angka popularitas dari setiap lagi, semakin besar semakin populer
- acousticness : ukuran kepercayaan dari 0,0 hingga 1,0 apakah trek akustik. 1.0 mewakili keyakinan tinggi bahwa trek akustik.
- danceability : danceability menggambarkan seberapa cocok trek untuk menari berdasarkan kombinasi elemen musik termasuk tempo, stabilitas ritme, kekuatan ketukan, dan keteraturan keseluruhan. Nilai 0,0 paling tidak dapat menari dan 1,0 paling dapat menari.
- duration_ms : durasi trek dalam milidetik.
- energy : energi adalah ukuran dari 0,0 hingga 1,0 dan mewakili ukuran persepsi intensitas dan aktivitas. Biasanya, trek energik terasa cepat, keras, dan berisik. Misalnya, death metal memiliki energi tinggi, sementara pendahuluan Bach mendapat skor rendah pada skala. Fitur persepsi yang berkontribusi pada atribut ini termasuk rentang dinamis, kenyaringan yang dirasakan, timbre, tingkat onset, dan entropi umum.
- instrumentalness : memprediksi apakah trek tidak berisi vokal. Suara “Ooh” dan “aah” diperlakukan sebagai instrumental dalam konteks ini. Rap atau trek kata yang diucapkan jelas “vokal”. Semakin dekat nilai instrumental menjadi 1,0, semakin besar kemungkinan trek tersebut tidak berisi konten vokal. Nilai di atas 0,5 dimaksudkan untuk mewakili trek instrumental, tetapi kepercayaan diri lebih tinggi saat nilainya mendekati 1,0.
- key : Kuncinya ada di trek. Integer memetakan ke nada menggunakan notasi Kelas Pitch standar. Misalnya. 0 = C, 1 = C♯/D♭, 2 = D, dan seterusnya. Jika tidak ada kunci yang terdeteksi, nilainya adalah -1.
- liveness : mendeteksi kehadiran penonton dalam rekaman. Nilai keaktifan yang lebih tinggi menunjukkan peningkatan kemungkinan bahwa lagu tersebut ditampilkan secara langsung. Nilai di atas 0,8 memberikan kemungkinan yang kuat bahwa trek itu hidup.
- mode : mode menunjukkan modalitas (mayor atau minor) dari sebuah trek, jenis tangga nada dari mana konten melodinya berasal. Mayor diwakili oleh 1 dan minor adalah 0.
- speechiness : speechiness mendeteksi keberadaan kata-kata yang diucapkan di trek. Semakin eksklusif rekaman seperti pidato (misalnya acara bincang-bincang, buku audio, puisi), semakin mendekati 1,0 nilai atributnya. Nilai di atas 0,66 menggambarkan trek yang mungkin seluruhnya terbuat dari kata-kata yang diucapkan. Nilai antara 0,33 dan 0,66 menggambarkan trek yang mungkin berisi musik dan ucapan, baik dalam bagian atau berlapis, termasuk kasus seperti musik rap. Nilai di bawah 0,33 kemungkinan besar mewakili musik dan trek non-suara lainnya.
- tempo : perkiraan tempo keseluruhan trek dalam ketukan per menit (BPM). Dalam terminologi musik, tempo adalah kecepatan atau kecepatan dari bagian tertentu dan berasal langsung dari durasi ketukan rata-rata.
- time_signature : perkiraan waktu tanda tangan. Tanda tangan waktu (meter) adalah konvensi notasi untuk menentukan berapa banyak ketukan di setiap bar (atau ukuran). Tanda tangan waktu berkisar dari 3 hingga 7 yang menunjukkan tanda waktu “3/4”, hingga “7/4”.
- valence : ukuran dari 0,0 hingga 1,0 menggambarkan kepositifan musik yang disampaikan oleh sebuah lagu. Trek dengan valensi tinggi terdengar lebih positif (misalnya bahagia, ceria, euforia), sedangkan trek dengan valensi rendah terdengar lebih negatif (misalnya sedih, tertekan, marah).
Business Goal
Disini saya membuat Role-Play yang dimana saya adalah sebagai song-writer sekaligus komposer pada suatu Studio Music. Disini saya ingin merancang sebuah System Recomendation bersama seorang teman saya yang bekerja sebagai Data-Scienctist, pada lagu yang ada di Spotify. Tujuan nya agar saya bisa mengatahui sekiranya mayoritas lagu yang banyak di Rekomendasikan kepada User berdasarkan Popularitas nya itu apa, sehingga saya bisa menciptakan lagu yang memiliki nuansa yang sama.
Setup Library
kita disini akan membutuhkan beberapa library baik itu untuk visualisasi maupun pembentukan model
library(readr)
library(tidyverse)
library(factoextra)
library(scales)
library(FactoMineR)Read Data
Pertama-tama mari baca data nya nya terlebih dahulu
song <- read_csv("data_input/SpotifyFeatures.csv")cek kolom dan tipe data yang terdapat pada song
glimpse(song)#> Rows: 232,725
#> Columns: 18
#> $ genre <chr> "Movie", "Movie", "Movie", "Movie", "Movie", "Movie",…
#> $ artist_name <chr> "Henri Salvador", "Martin & les fées", "Joseph Willia…
#> $ track_name <chr> "C'est beau de faire un Show", "Perdu d'avance (par G…
#> $ track_id <chr> "0BRjO6ga9RKCKjfDqeFgWV", "0BjC1NfoEOOusryehmNudP", "…
#> $ popularity <dbl> 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 <dbl> 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…
#> $ key <chr> "C#", "F#", "C", "C#", "F", "C#", "C#", "F#", "C", "G…
#> $ 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, -…
#> $ mode <chr> "Major", "Minor", "Minor", "Major", "Major", "Major",…
#> $ 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…
#> $ time_signature <chr> "4/4", "4/4", "5/4", "4/4", "4/4", "4/4", "4/4", "4/4…
#> $ valence <dbl> 0.8140, 0.8160, 0.3680, 0.2270, 0.3900, 0.3580, 0.533…
Disini banyak sekali tipe data yang belum sesuai yakni pada tipe Character yang seharusnya menjadi Faktor, tetapi disini karena kita ingin melakukan Clustering jadi biarkan saja tipe data Characternya tidak perlu di ubah karena nanti akan kita take-out dan kita satukan kembali.
Data Wrangling
Cek Missing Value
colSums(is.na(song))#> 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
anyNA(song)#> [1] FALSE
Yeayy tidak ada missing value, berarti kita bisa melanjutkan ke tahap berikutnya.
Cek Outlier
summary(song)#> 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
Terlihat dari hasil Summary banyak sekali nilai Q3 dan max yang terpaut jauh, dimana itu artinya terdapat Outlier pada data kita
boxplot(song$duration_ms)boxplot(song$liveness)boxplot(song$tempo)Dari contoh ketiga Variable diatas (duration_ms, liveness dan tempo) terlihat masih banyak sekali Outlier, tetapi karena Outlier nya tergolong banyak, sehingga kalaupun ingin di bersihkan, pasti akan menghilangkan banyak informasi dan disini saya menyimpulkan dari semua Outlier tersebut, sepertinya memang itulah pola data kita, yang cendrung tinggi, sehingga Outlier tersebut tidak apa kalau ingin di abaikan.
Mari kita hapus kolom Kategorik pada data Song, karena tidak akan kita gunakan pada Clustering yang akan kita lakukan. Seperti yang kita ketahui Algoritma K-Means tidak bisa memproses data Kategorik, oleh karena itu harus kita take-out untuk sekarang kolom kategorik nya dan nanti kita akan satukan kembali setelah melakukan Clustering.
song_cluster <- song %>%
select(-genre, -track_id, -key, -mode, -artist_name, -track_name, -time_signature)
glimpse(song_cluster)#> Rows: 232,725
#> Columns: 11
#> $ popularity <dbl> 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 <dbl> 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…
Cek Skala dari setiap nilai numerik nya, karena jika terdapat skala nilai yang jauh berbeda maka akan menutupi data point dengan skala nilai yang kecil sehingga akan menghasilkan Clusterisasi yang MisLeading karena cendrung mengelompokkan data dengan skala nilai besar.
summary(song_cluster)#> 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 liveness loudness
#> Min. :0.0000203 Min. :0.0000000 Min. :0.00967 Min. :-52.457
#> 1st Qu.:0.3850000 1st Qu.:0.0000000 1st Qu.:0.09740 1st Qu.:-11.771
#> Median :0.6050000 Median :0.0000443 Median :0.12800 Median : -7.762
#> Mean :0.5709577 Mean :0.1483012 Mean :0.21501 Mean : -9.570
#> 3rd Qu.:0.7870000 3rd Qu.:0.0358000 3rd Qu.:0.26400 3rd Qu.: -5.501
#> Max. :0.9990000 Max. :0.9990000 Max. :1.00000 Max. : 3.744
#> speechiness tempo valence
#> Min. :0.0222 Min. : 30.38 Min. :0.0000
#> 1st Qu.:0.0367 1st Qu.: 92.96 1st Qu.:0.2370
#> Median :0.0501 Median :115.78 Median :0.4440
#> Mean :0.1208 Mean :117.67 Mean :0.4549
#> 3rd Qu.:0.1050 3rd Qu.:139.05 3rd Qu.:0.6600
#> Max. :0.9670 Max. :242.90 Max. :1.0000
terlihat masih banyak skala nilai yang berbeda. Mari kita melakukan Scaling dengan Z-Score Standardization menggunakan Fungsi scale()
song_cluster_scale <- scale(song_cluster)
summary(song_cluster_scale)#> popularity acousticness danceability duration_ms
#> Min. :-2.2610 Min. :-1.0389 Min. :-2.68019 Min. :-1.8475
#> 1st Qu.:-0.6667 1st Qu.:-0.9329 1st Qu.:-0.64310 1st Qu.:-0.4394
#> Median : 0.1029 Median :-0.3849 Median : 0.08963 Median :-0.1236
#> Mean : 0.0000 Mean : 0.0000 Mean : 0.00000 Mean : 0.0000
#> 3rd Qu.: 0.7626 3rd Qu.: 0.9963 3rd Qu.: 0.74154 3rd Qu.: 0.2577
#> Max. : 3.2365 Max. : 1.7686 Max. : 2.34168 Max. :44.7114
#> energy instrumentalness liveness loudness
#> Min. :-2.1671 Min. :-0.4898 Min. :-1.0356 Min. :-7.1500
#> 1st Qu.:-0.7058 1st Qu.:-0.4898 1st Qu.:-0.5932 1st Qu.:-0.3670
#> Median : 0.1292 Median :-0.4897 Median :-0.4388 Median : 0.3014
#> Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000
#> 3rd Qu.: 0.8200 3rd Qu.:-0.3716 3rd Qu.: 0.2471 3rd Qu.: 0.6784
#> Max. : 1.6247 Max. : 2.8097 Max. : 3.9591 Max. : 2.2196
#> speechiness tempo valence
#> Min. :-0.53129 Min. :-2.82494 Min. :-1.74924
#> 1st Qu.:-0.45314 1st Qu.:-0.79963 1st Qu.:-0.83793
#> Median :-0.38091 Median :-0.06112 Median :-0.04198
#> Mean : 0.00000 Mean : 0.00000 Mean : 0.00000
#> 3rd Qu.:-0.08498 3rd Qu.: 0.69217 3rd Qu.: 0.78858
#> Max. : 4.56146 Max. : 4.05310 Max. : 2.09595
Bagus, sekarang data kita sudah memiliki skala nilai yang sama.
EDA (Exploratory Data Analysis)
Mari cari tahu lagu apa saja yang paling di sukai berdasarkan dari Popularitas dan Genre nya.
song_eda <- song %>%
group_by(track_id, genre, popularity) %>%
summarise(popularity = sum(popularity)) %>%
arrange(genre, desc(popularity))
song_eda_agg <- song_eda %>%
group_by(genre) %>%
summarise(popularity = sum(popularity))
ggplot(data = song_eda_agg,
mapping = aes(x = popularity, y = reorder(genre, popularity))) +
geom_col(aes(fill = genre), position = "dodge", show.legend = F) +
scale_x_continuous(labels = unit_format(scale = 10e-3, suffix = " K")) +
geom_label(aes(label = popularity), size = 3, fontface = "bold") +
labs(y = NULL, x = "Rating / Popularity", title = "Top Song Rating / Populartity by Genre") + theme_minimal()Dapat di lihat dari Rating / tingkat Popularitas dari setiap lagu tiap Genre nya, Pop, Hip-Hop, Rock, Indie dan Children’s Music merupakan Genre dengan tingkat Popularitas tertinggi. Hal ini dapat menjadi referensi utama kita di dalam menciptakan sebuah lagu agar memiliki kemungkinan besar untuk di sukai oleh banyak orang.
Clustering
Clustering adalah pengelompokan data berdasarkan karakteristiknya. Clustering bertujuan untuk menghasilkan cluster dimana:
tiap observasi di 1 cluster yang sama yang memiliki karakteristik yang mirip
tiap observasi dari cluster yang berbeda memiliki karakteristik yang berbeda.
Disini kita akan mencoba untuk melakukan Clusterisasi menggunakan K-means. K-Means adalah centroid-based clustering algorithms. Centroid adalah titik pusat.
K-means merupakan proses yang berulang dari:
Membuat pusat cluster (centroid) sebanyak k secara random
Menghitung jarak observasi ke pusat cluster
Assign tiap observasi ke masing-masing cluster
Menggeser centroid ke titik pusat cluster
Kembali ke tahap dua… . . Hingga observasi yang terassign ke tiap cluster tidak berubah lagi.
cluster k-means
RNGkind(sample.kind = "Rounding")
set.seed(100)
song_km <- kmeans(song_cluster_scale, centers = 5)song_km$withinss # wss#> [1] 368381.0 381937.3 264187.0 107129.7 285441.3
song_km$betweenss # bss#> [1] 1152888
song_km$betweenss/song_km$totss # (between_SS / total_SS)#> [1] 0.4503531
Terlihat dari hasil Algoritma K-Means kita diatas, dapat di lihat kita telah membuat 5 Cluster dan yang paling penting adalah hasil dari Total Sum of Square (between_SS / total_SS) yakni 45%. Yang kita inginkan adalah yang mendekati 1, artinya setiap cluster yang kita punya sudah tersebar dan terkelompok secara baik. Untuk WSS (Within Sum of Square) kita mengharap nilai yang kecil supaya jarak setiap observasi ke tiap Centroid Cluster semakin dekat dan terkahir BSS(Between Sum of Square) kita mengharap nilai besar supaya jarak centroid tiap cluster ke global sample mean (rata-rata data keseluruhan) semakin jauh.
Mari kita coba untuk melakukan meningkatkan Performasi dari K-Means kita, dengan merubah jumlah Cluster nya. Jumlah Cluster yang optimal dapat di cek melalui Elbow Method, dengan melihat Total Within Sum of Squares nya.
set.seed(123)
# menghitung jumlah k = 1 sampai dengan k = 30
k.max <- 30
data <- song_cluster_scale
wss <- sapply(1:k.max,
function(k){kmeans(data, k, iter.max = 30 )$tot.withinss})
plot(1:k.max, wss,
type="b", pch = 19, frame = FALSE,
xlab="Number of clusters K",
ylab="Total within-clusters sum of squares")terlihat pada plot Elbow Method di atas Total Within Sum of Squares semakin melandai pada jumlah cluster = 9-15. Tetapi disini saya ingin mengambil jumlah Cluster = 15 karena menurut saya pada jumlah 15 cluster tidak terdapat penurunan yang signifikan pada Total Within Sum of Squares nya dan seperti yang kita ketahui juga, data yang kita miliki cukup banyak jadi dengan Cluster = 15 sepertinya cocok dengan kondisi data kita.
Mari kita lakukan Clusterisasi ulang dengan jumlah cluster = 15
song_km_tunning <- kmeans(song_cluster_scale, centers = 15)song_km_tunning$betweenss/song_km_tunning$totss # (between_SS / total_SS)#> [1] 0.6282952
Dapat kita lihat dari hasil (between_SS / total_SS) sudah semakin mendakati 1 (62.8%). Artinya persebaran data tiap cluster kita sudah semakin baik.
Mari kita satukan kembil kolom kategorik yang sudah kita take-out tadi dengan hasil Clusterisasi yang sudah kita lakukan
song_cluster_profiling <- song %>%
mutate(cluster = as.factor(song_km_tunning$cluster))
head(song_cluster_profiling)#> # A tibble: 6 × 19
#> genre artist_name track_name track_id popularity acousticness danceability
#> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 Movie Henri Salva… C'est beau … 0BRjO6ga… 0 0.611 0.389
#> 2 Movie Martin & le… Perdu d'ava… 0BjC1Nfo… 1 0.246 0.59
#> 3 Movie Joseph Will… Don't Let M… 0CoSDzoN… 3 0.952 0.663
#> 4 Movie Henri Salva… Dis-moi Mon… 0Gc6TVm5… 0 0.703 0.24
#> 5 Movie Fabien Nataf Ouverture 0IuslXpM… 4 0.95 0.331
#> 6 Movie Henri Salva… Le petit so… 0Mf1jKa8… 0 0.749 0.578
#> # … with 12 more variables: duration_ms <dbl>, energy <dbl>,
#> # instrumentalness <dbl>, key <chr>, liveness <dbl>, loudness <dbl>,
#> # mode <chr>, speechiness <dbl>, tempo <dbl>, time_signature <chr>,
#> # valence <dbl>, cluster <fct>
Setelah kita satukan kembali kolom nya, kita dapat membuat System Recomendation berdasarkan Cluster dari setiap lagu yang ingin kita coba untuk berikan rekomendasi lagu serupa nya berdasarkan Cluster dari lagu itu sendiri.
Mari lakukan Profiling dari hasil clusterisasi yang sudah kita lakukan untuk mengtahui kriteria dari setiap Cluster.
song_cluster_profiling %>%
group_by(cluster) %>%
summarise_all(mean)#> # A tibble: 15 × 19
#> cluster genre artist_name track_name track_id popularity acousticness
#> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 1 NA NA NA NA 16.8 0.917
#> 2 2 NA NA NA NA 23.2 0.711
#> 3 3 NA NA NA NA 12.3 0.692
#> 4 4 NA NA NA NA 49.6 0.0941
#> 5 5 NA NA NA NA 41.2 0.219
#> 6 6 NA NA NA NA 43.5 0.150
#> 7 7 NA NA NA NA 45.1 0.628
#> 8 8 NA NA NA NA 57.5 0.174
#> 9 9 NA NA NA NA 20.6 0.802
#> 10 10 NA NA NA NA 27.7 0.163
#> 11 11 NA NA NA NA 44.9 0.0608
#> 12 12 NA NA NA NA 57.5 0.168
#> 13 13 NA NA NA NA 30.0 0.873
#> 14 14 NA NA NA NA 49.7 0.666
#> 15 15 NA NA NA NA 37.4 0.250
#> # … with 12 more variables: danceability <dbl>, duration_ms <dbl>,
#> # energy <dbl>, instrumentalness <dbl>, key <dbl>, liveness <dbl>,
#> # loudness <dbl>, mode <dbl>, speechiness <dbl>, tempo <dbl>,
#> # time_signature <dbl>, valence <dbl>
Dari hasil rata-rata setiap Cluster diatas dapat kita simpulkan :
Cluster 1 : cendrung memiliki popularitas yang rendah, tidak cocok untuk di gunakan sebagai musik dance karena dilihat dari danceability yang kecil. Lagu pada Cluster ini memiliki tingkat energy yang rendah tidak seperti lagu Metal dan sejenisnya serta positiveness yang dimiliki pun rendah jadi Cluster ini cendrung berisikan lagu yang bertemakan (sad, depressed, angry) Profiling disini hanya kita lakukan pada Cluster 1 saja karena cuman sebagai contoh. Mengingat jumlah kolom dan Cluster yang banyak akan memakan banyak waktu untuk melakukan Profiling pada semua Cluster. Tetapi kita dapat ide dari pada bagaimana cara untuk melakukan Profiling pada Cluster hasil K-Means
Mari kita lakukan Visualisasi terhadap hasil 15 Cluster yang kita miliki
fviz_cluster(song_km_tunning, data = song_cluster_scale)Principal Component Analysis (PCA)
PCA merupakan salah satu metode untuk melakukan Dimensionality Reduction untuk mengurangi Dimensi (kolom pada data). Dengan PCA data kita jauh lebih sedikit tetapi tetap mampu untuk mempertahankan Informasi yang terkandung di dalam nya. Dimana hal itu berguna untuk meringan kan daya komputasi terhadap data kita.
Kita bisa menggunakan PCA untuk menghasilkan Hasil Visualisasi yang lebih baik juga. Mari kita coba untuk membuat hasil visualisasi dengan PCA berdasarkan Genre dan Hasil Cluster, lihat mana yang lebih baik
# PCA dengan genre sebagai variable kategorik nya
song_with_pca <- song_cluster %>%
mutate(genre = as.factor(song$genre))
song_pca_genre <- PCA(X = song_with_pca,
scale.unit = T,
quali.sup = c(12),
graph = F,
ncp = 10)# hapus semua kolom kategorik selain dari pada kolom Cluster
song_cluster_profiling_pca<- song_cluster_profiling %>%
select(-genre, -track_id, -key, -mode, -artist_name, -track_name, -time_signature)
# PCA dengan hasil cluster sebagai variable kategorik nya
song_pca_cluster <- PCA(X = song_cluster_profiling_pca,
scale.unit = T,
quali.sup = c(12),
graph = F,
ncp = 10)
summary(song_pca_cluster)#>
#> Call:
#> PCA(X = song_cluster_profiling_pca, scale.unit = T, ncp = 10,
#> quali.sup = c(12), graph = F)
#>
#>
#> Eigenvalues
#> Dim.1 Dim.2 Dim.3 Dim.4 Dim.5 Dim.6 Dim.7
#> Variance 3.610 1.710 1.171 1.000 0.862 0.757 0.638
#> % of var. 32.822 15.546 10.648 9.089 7.834 6.880 5.799
#> Cumulative % of var. 32.822 48.368 59.016 68.105 75.939 82.819 88.617
#> Dim.8 Dim.9 Dim.10 Dim.11
#> Variance 0.485 0.375 0.277 0.115
#> % of var. 4.413 3.411 2.516 1.043
#> Cumulative % of var. 93.030 96.441 98.957 100.000
#>
#> Individuals (the 10 first)
#> Dist Dim.1 ctr cos2 Dim.2 ctr cos2
#> 1 | 4.033 | 0.990 0.000 0.060 | 0.999 0.000 0.061 |
#> 2 | 3.489 | 1.210 0.000 0.120 | 0.273 0.000 0.006 |
#> 3 | 3.503 | -2.112 0.001 0.364 | 0.353 0.000 0.010 |
#> 4 | 3.875 | -1.955 0.000 0.254 | -0.187 0.000 0.002 |
#> 5 | 4.023 | -2.936 0.001 0.532 | 0.392 0.000 0.009 |
#> 6 | 3.520 | -2.261 0.001 0.413 | 0.701 0.000 0.040 |
#> 7 | 5.377 | -0.757 0.000 0.020 | 3.417 0.003 0.404 |
#> 8 | 2.867 | -1.934 0.000 0.455 | 0.073 0.000 0.001 |
#> 9 | 3.115 | 0.768 0.000 0.061 | -0.056 0.000 0.000 |
#> 10 | 2.492 | 0.730 0.000 0.086 | 0.723 0.000 0.084 |
#> Dim.3 ctr cos2
#> 1 -0.160 0.000 0.002 |
#> 2 -0.684 0.000 0.038 |
#> 3 -1.867 0.001 0.284 |
#> 4 0.251 0.000 0.004 |
#> 5 -1.123 0.000 0.078 |
#> 6 -1.731 0.001 0.242 |
#> 7 -1.498 0.001 0.078 |
#> 8 -0.452 0.000 0.025 |
#> 9 -1.178 0.001 0.143 |
#> 10 -0.614 0.000 0.061 |
#>
#> Variables (the 10 first)
#> Dim.1 ctr cos2 Dim.2 ctr cos2 Dim.3 ctr
#> popularity | 0.449 5.588 0.202 | -0.390 8.908 0.152 | 0.108 0.989
#> acousticness | -0.798 17.659 0.638 | 0.245 3.524 0.060 | -0.226 4.349
#> danceability | 0.635 11.172 0.403 | 0.079 0.362 0.006 | -0.488 20.314
#> duration_ms | -0.114 0.360 0.013 | -0.043 0.107 0.002 | 0.642 35.227
#> energy | 0.848 19.917 0.719 | 0.127 0.939 0.016 | 0.267 6.100
#> instrumentalness | -0.611 10.356 0.374 | -0.239 3.345 0.057 | 0.076 0.488
#> liveness | 0.056 0.088 0.003 | 0.810 38.356 0.656 | 0.275 6.438
#> loudness | 0.887 21.812 0.788 | -0.032 0.061 0.001 | 0.166 2.354
#> speechiness | 0.056 0.088 0.003 | 0.844 41.675 0.713 | 0.031 0.081
#> tempo | 0.299 2.471 0.089 | -0.195 2.231 0.038 | 0.280 6.707
#> cos2
#> popularity 0.012 |
#> acousticness 0.051 |
#> danceability 0.238 |
#> duration_ms 0.413 |
#> energy 0.071 |
#> instrumentalness 0.006 |
#> liveness 0.075 |
#> loudness 0.028 |
#> speechiness 0.001 |
#> tempo 0.079 |
#>
#> Supplementary categories (the 10 first)
#> Dist Dim.1 cos2 v.test Dim.2 cos2
#> cluster_1 | 3.543 | -3.202 0.817 -188.136 | 0.351 0.010
#> cluster_2 | 13.792 | -3.026 0.048 -30.534 | 0.675 0.002
#> cluster_3 | 2.469 | -0.680 0.076 -35.358 | 0.518 0.044
#> cluster_4 | 1.563 | 0.829 0.281 69.394 | -0.371 0.056
#> cluster_5 | 2.726 | 0.811 0.088 44.509 | 1.563 0.329
#> cluster_6 | 2.218 | 1.685 0.577 120.514 | -0.265 0.014
#> cluster_7 | 1.769 | -0.879 0.247 -53.513 | -0.621 0.123
#> cluster_8 | 1.659 | 1.001 0.364 82.197 | -0.405 0.060
#> cluster_9 | 5.305 | -0.588 0.012 -30.927 | 5.116 0.930
#> cluster_10 | 1.866 | 1.239 0.441 95.717 | 0.140 0.006
#> v.test Dim.3 cos2 v.test
#> cluster_1 30.003 | -0.204 0.003 -21.080 |
#> cluster_2 9.903 | 8.070 0.342 142.971 |
#> cluster_3 39.191 | -1.759 0.508 -160.665 |
#> cluster_4 -45.142 | 0.461 0.087 67.716 |
#> cluster_5 124.703 | 1.062 0.152 102.416 |
#> cluster_6 -27.570 | 0.135 0.004 16.910 |
#> cluster_7 -54.941 | 0.378 0.046 40.409 |
#> cluster_8 -48.345 | -0.125 0.006 -17.985 |
#> cluster_9 391.219 | 0.239 0.002 22.067 |
#> cluster_10 15.676 | -0.665 0.127 -90.192 |
Dari hasil PCA diatas dapat kita lihat Cummulative of Variance nya, jika kita ingin mempertahankan Informasi sekitar 80% kita dapat menggunakan PC-1 sampai PC-6 / PC-7 dan jika kita ingin mempertahankan Informasi sekitar 90% kita dapat memilih PC-1 sampai PC-8
Visualisasi menggunakan PCA dengan variable kategorik Genre
plot.PCA(x = song_pca_genre, choix = "ind",label = "quali", habillage = 12)Visualisasi menggunakan PCA dengan variable kategorik Cluster
plot.PCA(x = song_pca_cluster, choix = "ind",label = "quali", habillage = 12)Terlihat dari hasil Visualisasi diatas menggunakan PCA dan kolom Cluster hasil nya jauh lebih rapi dari pada menggunakan kolom genre.
Mari kita coba explore lebih dalam hasil dari PCA kita untuk melihat dari segi High Dimensionality nya sehingga kita dapat lebih mudah melihat korelasi, variance dan outlier dari pada Data kita.
par(mfcol=c(1,2)) # graphical parameter to arrange plots
plot.PCA(
x = song_pca_cluster,
choix = "ind",
label = "quali",
habillage = 12,
title = "Individual Factor Map"
)plot.PCA(
x = song_pca_cluster,
choix = "var",
axes = c(1, 2),
title = "Variable Factor Map"
)Dari hasil kedua Plot diatas dapat kita simpulkan :
Beberapa Outlier berasl dari Instrumentalness jika di lihat dari tanda panah yang mengarah kepada Cluster 2
Dim / PC-2 banyak merangkum Informasi dari kolom liveness dan speechiness
DIM / PC-1 banyak merangkum Informasi dari kolom acousticness, valence, energy dan dancebility. Sisa nya banyak dirangkum oleh kedua PC(1/2)
liveness dan speechiness memilii korelasi Positif Kuat. Begitu juga dengan valence, energy dan dancebility serta tempo dan popularity. Tetapi masih harus di pastikan dengan perhitungan korelasi menggunakan fungsi cor()
Untuk lebih jelas dan objektif, kontribusi/korelasi tiap variable ke tiap PC dapat dilihat menggunakan dimdesc(). Semakin tinggi nilai korelasi, semakin banyak informasi yang dirangkum pada PC tersebut.
dim <- dimdesc(song_pca_cluster) # objek pca# variable yang berkontribusi untuk PC1
as.data.frame(dim$Dim.1$quanti) # quanti: variable numerik#> correlation
#> loudness 0.88742278
#> energy 0.84799976
#> danceability 0.63509756
#> valence 0.61536866
#> popularity 0.44917562
#> tempo 0.29865947
#> speechiness 0.05644642
#> liveness 0.05634070
#> duration_ms -0.11406412
#> instrumentalness -0.61147590
#> acousticness -0.79848490
#> p.value
#> loudness 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> energy 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> danceability 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> valence 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> popularity 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> tempo 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> speechiness 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001563996
#> liveness 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006302335
#> duration_ms 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> instrumentalness 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> acousticness 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
as.data.frame(dim$Dim.2$quanti) # quanti: variable numerik#> correlation
#> speechiness 0.84418926
#> liveness 0.80987677
#> acousticness 0.24548288
#> energy 0.12675019
#> valence 0.09164894
#> danceability 0.07871306
#> loudness -0.03224192
#> duration_ms -0.04283383
#> tempo -0.19533975
#> instrumentalness -0.23915416
#> popularity -0.39029083
#> p.value
#> speechiness 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> liveness 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> acousticness 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> energy 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> valence 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> danceability 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001758662
#> loudness 0.0000000000000000000000000000000000000000000000000000014047938341516833927857673965414972159570820804264435363004167830057336695727469415936416215294287692238212698779983344440041228451426140802493769399461598368361592292785644531250000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> duration_ms 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000605107615128800385942661564260539745469232518290857440183851218400082945253755652079762763518362441145690031850391696953106297741029068563088425774694303964542155732289598638486877582370329798050195326962723540717386701925744035
#> tempo 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> instrumentalness 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#> popularity 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Bisa dilihat dari hasil korelasi variable diatas pada PC-1, variable loudness, energy, danceaility dan valence memiliki kontribusi paling besar pada PC-1. Dan pada PC-2 speechiness dan liveness merupakan variable yang paling banyak di rangkum / berkontribusi pada PC-2.
System Recomendation Base on Cluster
Mari kita coba memasukan beberapa lagu untuk melihat rekomendasi lagu yang serupa dari Cluster yang di tampilkan.
song_cluster_profiling %>%
filter(track_name == "I Fall Apart", artist_name == "Post Malone") %>%
select("cluster", "genre")#> # A tibble: 2 × 2
#> cluster genre
#> <fct> <chr>
#> 1 8 Rap
#> 2 8 Pop
Mari kita cek lagu yang serupa dengan Post Malone - I Fall Apart (Cluster 8)
song_cluster_profiling %>%
filter(cluster == 8, genre == "Rap") %>%
select(track_name, genre)#> # A tibble: 3,840 × 2
#> track_name genre
#> <chr> <chr>
#> 1 Wow. Rap
#> 2 MIDDLE CHILD Rap
#> 3 SICKO MODE Rap
#> 4 Thotiana Rap
#> 5 a lot Rap
#> 6 Drip Too Hard (Lil Baby & Gunna) Rap
#> 7 Money Rap
#> 8 Swervin (feat. 6ix9ine) Rap
#> 9 Better Now Rap
#> 10 Pure Water (with Migos) Rap
#> # … with 3,830 more rows
Terlihat banyak lagu yang dapat di rekomendasikan yang sesuai dengan Cluster 8 dan ber-genre RAP.
Summary
Dari hasil clusterisasi dan EDA kita diatas, kita mengatahui jenis Genre yang paling banyak di minati oleh orang banyak berdasarkan rating / tingkat popularitas nya. Kita juga sudah mencoba untuk melakukan Clusterisasi menggunakan K-Means untuk melakukan Pengelompokan lagu berdasarkan tiap cluster dan kita juga sudah mencoba untuk melakukan PCA untuk mengurangi Dimensionality dari data kita agar dapat meringankan komputasi dan memperbagus hasil visualisasi. Dari hasil pengelompokkan yang telah kita lakukan kita sudah mencoba untuk memberikan System Recomendation lagu serupa (Post-Malone I Fall a Part) dari Cluter 8 dan ber-genre RAP. Terlihat kita mendapatkan banyak lagu serupa dengan Genre RAP dan berasal dari Cluster 8, yang dimana jika di lihat dari Profiling Cluster 8 yang cendrung berisikan lagu ber-energy, cocok digunakan untuk berdansa dan memiliki tingkat popularitas yang tinggi, cocok dengan Genre RAP dari setiap lagu nya. Dari sini saya sebagai seorang song-writer dan komposer dapat mengtahui Genre apa yang paling populer atau di sukai banyak orang serta saya juga mengatahui rekomendasi lagu serupa, misalnya berdasarkan salah satu Genre yang paling Populer yakni RAP. Maka dari situ saya bisa membuat lagu dengan vibes dan nuansa yang sama dan berkemungkinan besar untuk di sukai oleh banyak orang.