Pendahulan

Dalam era digital saat ini, industri musik telah mengalami transformasi yang signifikan. Layanan streaming musik seperti Spotify telah menjadi platform populer bagi pengguna untuk menikmati musik favorit mereka. Dengan jutaan lagu yang tersedia, Spotify mengumpulkan data yang meliputi berbagai aspek dari setiap lagu, termasuk genre, artis, popularitas, akustik, danceability, energi, dan banyak lagi.

Dalam proyek ini, bertujuan untuk menganalisis data Spotify untuk mendapatkan wawasan yang berharga tentang preferensi musik pengguna dan pola yang mungkin terkait dengan fitur-fitur tertentu. Saya akan menerapkan teknik unsupervised learning untuk mengidentifikasi pola tersembunyi dalam dataset dan menggunakan visualisasi untuk membantu memahami data dengan lebih baik.

Tujuan

Laporan ini bertujuan untuk memberikan pemahaman yang komprehensif tentang proses analisis data Spotify menggunakan teknik unsupervised learning. Laporan ini akan menjelaskan langkah-langkah yang tepat yang diambil untuk memproses data, memilih parameter yang sesuai, dan menerapkan algoritma clustering atau reduksi dimensi (PCA). Selain itu, akan ada visualisasi sederhana untuk memberikan wawasan tambahan yang berguna dalam menarik kesimpulan dari data Spotify yang ada.

knitr::include_graphics("assets/spotfy.jpg")

EDA & Data Wrangling

Untuk memulai, kita perlu membaca data dari file CSV ke dalam sebuah dataframe menggunakan fungsi read.csv().

data <- read.csv("data_input/SpotifyFeatures.csv")
glimpse(data)
#> 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       <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.00e+00, 0.00e+00, 0.00e+00, 0.00e+00, 1.23e-01, 0.0…
#> $ 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…

Deskripsi Kolom

  • genre: Informasi tentang genre musik dari setiap lagu.
  • artist_name:Berisi nama artis yang melakukan rekaman lagu.
  • track_name: Menyimpan judul atau nama dari setiap lagu.
  • track_id: Merupakan ID unik untuk setiap lagu dalam data.
  • popularity: Tingkat popularitas lagu, dengan nilai yang lebih tinggi menunjukkan popularitas yang lebih tinggi.
  • acousticness: Menunjukkan seberapa akustik suara lagu, dengan nilai yang lebih tinggi menunjukkan lebih banyak unsur akustik.
  • danceability: Mengindikasikan sejauh mana lagu cocok untuk menari, dengan nilai yang lebih tinggi menunjukkan lagu yang lebih mudah untuk diikuti irama.
  • duration_ms: Menyimpan durasi lagu dalam milidetik (ms).
  • energy: Menggambarkan tingkat energi dan intensitas suara lagu, dengan nilai yang lebih tinggi menunjukkan energi yang lebih tinggi.
  • instrumentalness: Menunjukkan sejauh mana lagu bersifat instrumental, dengan nilai yang lebih tinggi menunjukkan lebih sedikit vokal dan lebih banyak unsur instrumental.
  • key: Merupakan kunci musik atau skala utama dari lagu.
  • liveness: Mengindikasikan sejauh mana lagu terdengar seperti rekaman langsung, dengan nilai yang lebih tinggi menunjukkan elemen yang lebih hidup atau rekaman langsung.
  • loudness: Menggambarkan tingkat kebisingan atau volume suara lagu dalam desibel (dB).
  • mode: Menyatakan apakah lagu tersebut dalam mode mayor (Major) atau minor (Minor).
  • speechiness: Menunjukkan tingkat kehadiran unsur pidato atau pengucapan kata dalam lagu, dengan nilai yang lebih tinggi menunjukkan lebih banyak pengucapan kata daripada musik instrumental.
  • tempo:Mengindikasikan kecepatan atau tempo musik dalam ketukan per menit (BPM).
  • time_signature: Merupakan tanda waktu atau pola ritme dalam lagu, seperti “4/4” yang menunjukkan pola ritme
  • valence: Menggambarkan tingkat keceriaan atau positivitas lagu, dengan nilai yang lebih tinggi menunjukkan lagu yang lebih ceria atau positif.

Check Missing Value

colSums(is.na(data))
#>            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

Insight : Data yang kita punya tidak missing value.

library(dplyr)

data <- data %>%
  select(-track_id) %>%
  mutate(across(c(genre, popularity, key, mode, time_signature), as.factor))

Check Skala

summary(data)
#>         genre        artist_name         track_name          popularity    
#>  Comedy    :  9681   Length:232725      Length:232725      0      :  6312  
#>  Soundtrack:  9646   Class :character   Class :character   50     :  5415  
#>  Indie     :  9543   Mode  :character   Mode  :character   53     :  5414  
#>  Jazz      :  9441                                         51     :  5401  
#>  Pop       :  9386                                         52     :  5342  
#>  Electronic:  9377                                         49     :  5266  
#>  (Other)   :175651                                         (Other):199575  
#>   acousticness     danceability     duration_ms          energy        
#>  Min.   :0.0000   Min.   :0.0569   Min.   :  15387   Min.   :2.03e-05  
#>  1st Qu.:0.0376   1st Qu.:0.4350   1st Qu.: 182857   1st Qu.:3.85e-01  
#>  Median :0.2320   Median :0.5710   Median : 220427   Median :6.05e-01  
#>  Mean   :0.3686   Mean   :0.5544   Mean   : 235122   Mean   :5.71e-01  
#>  3rd Qu.:0.7220   3rd Qu.:0.6920   3rd Qu.: 265768   3rd Qu.:7.87e-01  
#>  Max.   :0.9960   Max.   :0.9890   Max.   :5552917   Max.   :9.99e-01  
#>                                                                        
#>  instrumentalness         key           liveness          loudness      
#>  Min.   :0.0000000   C      :27583   Min.   :0.00967   Min.   :-52.457  
#>  1st Qu.:0.0000000   G      :26390   1st Qu.:0.09740   1st Qu.:-11.771  
#>  Median :0.0000443   D      :24077   Median :0.12800   Median : -7.762  
#>  Mean   :0.1483012   C#     :23201   Mean   :0.21501   Mean   : -9.570  
#>  3rd Qu.:0.0358000   A      :22671   3rd Qu.:0.26400   3rd Qu.: -5.501  
#>  Max.   :0.9990000   F      :20279   Max.   :1.00000   Max.   :  3.744  
#>                      (Other):88524                                      
#>     mode         speechiness         tempo        time_signature
#>  Major:151744   Min.   :0.0222   Min.   : 30.38   0/4:     8    
#>  Minor: 80981   1st Qu.:0.0367   1st Qu.: 92.96   1/4:  2608    
#>                 Median :0.0501   Median :115.78   3/4: 24111    
#>                 Mean   :0.1208   Mean   :117.67   4/4:200760    
#>                 3rd Qu.:0.1050   3rd Qu.:139.05   5/4:  5238    
#>                 Max.   :0.9670   Max.   :242.90                 
#>                                                                 
#>     valence      
#>  Min.   :0.0000  
#>  1st Qu.:0.2370  
#>  Median :0.4440  
#>  Mean   :0.4549  
#>  3rd Qu.:0.6600  
#>  Max.   :1.0000  
#> 

Insight : Perlu dilakukan scaling pada data yang ada, agar variansnya sama

Unsupervised Learning

PCA

Pertama, kita akan menerapkan Principal Component Analysis (PCA) untuk reduksi dimensi. Hal ini dilakukan dengan tujuan mengurangi kompleksitas data dan mempertahankan informasi yang relevan.

Buat objek baru untuk analisis PCA, gunakan kolom yang bertipe numerik

data_pca <- data %>% 
  select_if(is.numeric)

plot(prcomp(data_pca))

Scaling

Selanjutnya, kita perlu melakukan scaling pada data agar variabel memiliki skala yang sama. Hal ini penting karena beberapa variabel dalam data memiliki rentang nilai yang berbeda

data_scaled <- scale(data_pca)
summary(data_scaled)
#>   acousticness      danceability       duration_ms          energy       
#>  Min.   :-1.0389   Min.   :-2.68019   Min.   :-1.8475   Min.   :-2.1671  
#>  1st Qu.:-0.9329   1st Qu.:-0.64310   1st Qu.:-0.4394   1st Qu.:-0.7058  
#>  Median :-0.3849   Median : 0.08963   Median :-0.1236   Median : 0.1292  
#>  Mean   : 0.0000   Mean   : 0.00000   Mean   : 0.0000   Mean   : 0.0000  
#>  3rd Qu.: 0.9963   3rd Qu.: 0.74154   3rd Qu.: 0.2577   3rd Qu.: 0.8200  
#>  Max.   : 1.7686   Max.   : 2.34168   Max.   :44.7114   Max.   : 1.6247  
#>  instrumentalness     liveness          loudness        speechiness      
#>  Min.   :-0.4898   Min.   :-1.0356   Min.   :-7.1500   Min.   :-0.53129  
#>  1st Qu.:-0.4898   1st Qu.:-0.5932   1st Qu.:-0.3670   1st Qu.:-0.45314  
#>  Median :-0.4897   Median :-0.4388   Median : 0.3014   Median :-0.38091  
#>  Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.00000  
#>  3rd Qu.:-0.3716   3rd Qu.: 0.2471   3rd Qu.: 0.6784   3rd Qu.:-0.08498  
#>  Max.   : 2.8097   Max.   : 3.9591   Max.   : 2.2196   Max.   : 4.56146  
#>      tempo             valence        
#>  Min.   :-2.82494   Min.   :-1.74924  
#>  1st Qu.:-0.79963   1st Qu.:-0.83793  
#>  Median :-0.06112   Median :-0.04198  
#>  Mean   : 0.00000   Mean   : 0.00000  
#>  3rd Qu.: 0.69217   3rd Qu.: 0.78858  
#>  Max.   : 4.05310   Max.   : 2.09595
plot(prcomp(data_scaled))

pca <-  prcomp(data_scaled)

PCA untuk Dimensionality Reduction

Berikutnya, kita akan menggunakan PCA untuk mereduksi dimensi data. Kita dapat memilih jumlah dimensi yang ingin direduksi dan mempertahankan seberapa banyak informasi yang ingin kita pertahankan.

Case : Ingin merduksi 10 dimensi yang ada, dengan mempertahankan informasi yang ada.

#> Importance of components:
#>                           PC1    PC2    PC3     PC4     PC5     PC6    PC7
#> Standard deviation     1.8598 1.2736 1.0804 0.97566 0.89128 0.83406 0.7225
#> Proportion of Variance 0.3459 0.1622 0.1167 0.09519 0.07944 0.06957 0.0522
#> Cumulative Proportion  0.3459 0.5081 0.6248 0.72001 0.79945 0.86901 0.9212
#>                            PC8     PC9    PC10
#> Standard deviation     0.62911 0.52615 0.33951
#> Proportion of Variance 0.03958 0.02768 0.01153
#> Cumulative Proportion  0.96079 0.98847 1.00000
#>   acousticness danceability duration_ms     energy instrumentalness    liveness
#> 1    0.6833748   -0.8909329  -1.1413655  1.2869052      -0.48981747  0.66065975
#> 2   -0.3454664    0.1919933  -0.8218657  0.6302479      -0.48981747 -0.32283477
#> 3    1.6445663    0.5852948  -0.5452965 -1.6699502      -0.48981747 -0.56492573
#> 4    0.9426992   -1.6936990  -0.6952933 -0.9297874      -0.48981747 -0.58762176
#> 5    1.6389288   -1.2034190  -1.2821808 -1.3131538      -0.08356631 -0.06561313
#> 6    1.0723614    0.1273410  -0.6263486 -1.8073548      -0.48981747 -0.54475148
#>     loudness speechiness      tempo    valence
#> 1  1.2907007  -0.3679692  1.5956039  1.3807413
#> 2  0.6686811  -0.1830817  1.8232495  1.3884316
#> 3 -0.7184009  -0.4558311 -0.5883245 -0.3342114
#> 4 -0.4348159  -0.4380431  1.7505932 -0.8763826
#> 5 -1.9305971  -0.4051623  0.7414313 -0.2496173
#> 6 -0.9002886   0.1198533 -0.9769791 -0.3726633
#>         PC1         PC2        PC3        PC4          PC5        PC6
#> 1  1.608076  0.01943355  0.2377522  2.0359381 -0.215300882 -0.1935772
#> 2  1.785895 -0.72321578 -0.2759833  1.5082760 -0.930143964 -0.0875709
#> 3 -1.672205 -0.10314197 -1.7397396 -0.2095422 -0.412506014  1.1438302
#> 4 -1.477940 -0.79746031  0.5679776  2.0632945 -0.434136141  1.3961619
#> 5 -2.503036 -0.06224634 -0.9024316  1.7928093 -0.480396112  0.6771452
#> 6 -1.777048  0.23939801 -1.6212937 -0.2843979  0.008036554  1.0854009
#>           PC7         PC8         PC9        PC10
#> 1 -1.47148538 -0.88782639  1.14696819  0.09475008
#> 2 -0.39089788 -0.45548384  0.06303431 -0.07113580
#> 3 -0.12679229  0.42884934  0.56224901 -0.04344974
#> 4 -0.30266666 -0.47042801  0.14984911 -0.17574421
#> 5 -0.82964336 -0.20107899 -0.31772582  0.44803838
#> 6  0.06521505  0.08761386 -0.13223759 -0.38429350
pc_keep <-  as.data.frame(pca$x[,1:4])
head(pc_keep)
#>         PC1         PC2        PC3        PC4
#> 1  1.608076  0.01943355  0.2377522  2.0359381
#> 2  1.785895 -0.72321578 -0.2759833  1.5082760
#> 3 -1.672205 -0.10314197 -1.7397396 -0.2095422
#> 4 -1.477940 -0.79746031  0.5679776  2.0632945
#> 5 -2.503036 -0.06224634 -0.9024316  1.7928093
#> 6 -1.777048  0.23939801 -1.6212937 -0.2843979

Setelah tahapan ini, rangkum informasi yang dibutuhkan dengan menggabungkan data awal dengan variabel numerik awal.

head(data %>% 
  select_if(~!is.numeric(.)) %>% 
  cbind(pc_keep))
#>   genre       artist_name                       track_name popularity key  mode
#> 1 Movie    Henri Salvador      C'est beau de faire un Show          0  C# Major
#> 2 Movie Martin & les fées Perdu d'avance (par Gad Elmaleh)          1  F# Minor
#> 3 Movie   Joseph Williams   Don't Let Me Be Lonely Tonight          3   C Minor
#> 4 Movie    Henri Salvador   Dis-moi Monsieur Gordon Cooper          0  C# Major
#> 5 Movie      Fabien Nataf                        Ouverture          4   F Major
#> 6 Movie    Henri Salvador   Le petit souper aux chandelles          0  C# Major
#>   time_signature       PC1         PC2        PC3        PC4
#> 1            4/4  1.608076  0.01943355  0.2377522  2.0359381
#> 2            4/4  1.785895 -0.72321578 -0.2759833  1.5082760
#> 3            5/4 -1.672205 -0.10314197 -1.7397396 -0.2095422
#> 4            4/4 -1.477940 -0.79746031  0.5679776  2.0632945
#> 5            4/4 -2.503036 -0.06224634 -0.9024316  1.7928093
#> 6            4/4 -1.777048  0.23939801 -1.6212937 -0.2843979

Untuk lanjut ketahapan selanjutnya, perlu dilakukan pemisahan target variabel

library(GGally)
# Korelasi sebelum PCA
ggcorr(data, label = T, hjust =1)

#korelasi setelah PCA
ggcorr(data = pc_keep, label = T, hjust =1)

Setelah melakukan reduksi dimensi menggunakan PCA, kita dapat melihat bahwa variabel-variabel dalam data terdistribusi dengan baik dalam ruang dua dimensi. Hal ini menunjukkan bahwa fitur-fitur dalam data memiliki kemampuan yang baik untuk membedakan lagu-lagu berdasarkan karakteristik mereka.

K-Means Clustering

Selanjutnya, kita akan menerapkan algoritma k-means untuk mengelompokkan lagu berdasarkan fitur-fiturnya. Kita perlu menentukan jumlah cluster yang optimal terlebih dahulu. Berikut adalah tahapan untuk menentukan jumlah cluster yang optimal:

library(cluster)

data_cluster <- data %>% 
  select_if(is.numeric)
head(data_cluster)
#>   acousticness danceability duration_ms energy instrumentalness liveness
#> 1        0.611        0.389       99373 0.9100            0.000   0.3460
#> 2        0.246        0.590      137373 0.7370            0.000   0.1510
#> 3        0.952        0.663      170267 0.1310            0.000   0.1030
#> 4        0.703        0.240      152427 0.3260            0.000   0.0985
#> 5        0.950        0.331       82625 0.2250            0.123   0.2020
#> 6        0.749        0.578      160627 0.0948            0.000   0.1070
#>   loudness speechiness   tempo valence
#> 1   -1.828      0.0525 166.969   0.814
#> 2   -5.559      0.0868 174.003   0.816
#> 3  -13.879      0.0362  99.488   0.368
#> 4  -12.178      0.0395 171.758   0.227
#> 5  -21.150      0.0456 140.576   0.390
#> 6  -14.970      0.1430  87.479   0.358

Tahapan selanjutnya adalah menentukan jumlah cluster yang diinginkan menggunakan metode elbow.

wss <- function(k) {
  kmeans_result <- kmeans(data_cluster, centers = k)
  return(sum(kmeans_result$withinss))
}

# Set the maximum number of clusters to test
max_k <- 10

# Calculate within-cluster sum of squares for each value of k
wss_values <- sapply(1:max_k, wss)

# Plot the elbow curve
plot(1:max_k, wss_values, type = "b", pch = 19, frame = FALSE, xlab = "Number of Clusters (k)", ylab = "Within-Cluster Sum of Squares", main = "Elbow Curve")

Dalam grafik elbow curve di atas, kita mencari elbow point, yaitu titik di mana penurunan dalam within-cluster sum of squares mulai merata atau melambat. Pada titik tersebut, kita dapat memilih jumlah cluster yang optimal.

Dalam kasus ini, kita dapat memilih k = 7 sebagai jumlah cluster yang optimal.

RNGkind(sample.kind = "Rounding")
set.seed(100)
k <- 7
kmeans_result <- kmeans(data_cluster,centers = k) #kita mau coba buat 7 cluster
data_cluster$cluster_label <- as.factor(kmeans_result$cluster)

head(data_cluster)
#>   acousticness danceability duration_ms energy instrumentalness liveness
#> 1        0.611        0.389       99373 0.9100            0.000   0.3460
#> 2        0.246        0.590      137373 0.7370            0.000   0.1510
#> 3        0.952        0.663      170267 0.1310            0.000   0.1030
#> 4        0.703        0.240      152427 0.3260            0.000   0.0985
#> 5        0.950        0.331       82625 0.2250            0.123   0.2020
#> 6        0.749        0.578      160627 0.0948            0.000   0.1070
#>   loudness speechiness   tempo valence cluster_label
#> 1   -1.828      0.0525 166.969   0.814             1
#> 2   -5.559      0.0868 174.003   0.816             1
#> 3  -13.879      0.0362  99.488   0.368             2
#> 4  -12.178      0.0395 171.758   0.227             1
#> 5  -21.150      0.0456 140.576   0.390             1
#> 6  -14.970      0.1430  87.479   0.358             2
library(tidyr)

cluster_profile <- data_cluster %>% 
  group_by(cluster_label) %>% 
  summarize(acousticness_mean = mean(acousticness),
            danceability_mean = mean(danceability),
            energy_mean = mean(energy),
            instrumentalness_mean = mean(instrumentalness),
            valence_mean = mean(valence))

sample(cluster_profile)
#> # A tibble: 7 × 6
#>   danceability_mean valence_mean cluster_label acousticness_mean instr…¹ energ…²
#>               <dbl>        <dbl> <fct>                     <dbl>   <dbl>   <dbl>
#> 1             0.520        0.458 1                         0.577  0.263    0.452
#> 2             0.588        0.498 2                         0.314  0.0932   0.611
#> 3             0.339        0.227 3                         0.739  0.463    0.327
#> 4             0.529        0.471 4                         0.704  0.137    0.469
#> 5             0.403        0.275 5                         0.590  0.386    0.418
#> 6             0.563        0.447 6                         0.307  0.114    0.604
#> 7             0.491        0.362 7                         0.414  0.240    0.523
#> # … with abbreviated variable names ¹​instrumentalness_mean, ²​energy_mean

Selama analisis k-means clustering, kita berhasil mengelompokkan lagu-lagu dalam 7 kluster berdasarkan fitur-fiturnya. Setiap kluster memiliki profil unik yang mencerminkan kombinasi tertentu dari fitur-fitur musik. Misalnya, kluster 1 cenderung memiliki tingkat akustik yang tinggi dan energi yang rendah, sementara kluster 2 memiliki tingkat akustik yang rendah dan energi yang tinggi.

Visualisasi

Visualisasi PCA

Pertama, kita akan melakukan visualisasi menggunakan PCA untuk mereduksi dimensi data menjadi dua dimensi. Kita akan menggunakan biplot untuk menunjukkan hubungan antara variabel-variabel dalam data.

data_small <-  data_pca %>% head(100)

pca_small <- prcomp(data_small, scale = T)

biplot(x = pca_small,
       cex = 0.6,
       scale = FALSE)

💡 Dari biplot diatas, terdapat outlier terluar, observasi 55 dan 15 ekstrim pada variable duration_ms . dari panah merah, kita tahu variabel mana yang paling banyak berkontribusi, untuk lebih jelasnya dapat dilihat dalam urutan kontribusi berikut :

data_small[c(15,55),]
#>    acousticness danceability duration_ms energy instrumentalness liveness
#> 15        0.970        0.400      159253  0.174            0.933    0.130
#> 55        0.924        0.683      101653  0.147            0.000    0.606
#>    loudness speechiness   tempo valence
#> 15  -13.869      0.0458 115.022   0.270
#> 55  -21.998      0.8220  32.244   0.595

Selanjutnya, kita dapat melihat kontribusi variabel terhadap setiap komponen utama (PC) dengan menggunakan grafik kontribusi:

library(factoextra)

fviz_contrib(X = pca_small,
             choice = "var",
             axes = 1)

Untuk visualisasi yang lebih menarik, kita dapat menggunakan fungsi fancy_biplot yang telah dibuat sebelumnya:

source("R/biplot.R")
fancy_biplot(pca_small)

K-Means Clustering

Selanjutnya, kita akan melakukan visualisasi untuk hasil dari algoritma k-means clustering. Kita akan menggunakan grafik batang untuk menunjukkan profil masing-masing kluster berdasarkan nilai rata-rata dari fitur-fiturnya.

# Reshape the data frame for plotting
cluster_profile_melted <- cluster_profile %>% 
  gather(variable, value, -cluster_label)

ggplot(cluster_profile_melted, aes(x = cluster_label, y = value, fill = variable)) +
  geom_bar(stat = "identity", position = "dodge") +
  labs(x = "Cluster Label", y = "Mean Value", fill = "Variable") +
  ggtitle("Cluster Profiles")

Selain itu, kita juga dapat menggunakan grafik radar interaktif untuk memvisualisasikan profil kluster:

library(ggiraphExtra)

ggRadar(data = cluster_profile,
        aes(colours=cluster_label),
        interactive = TRUE)
library(factoextra)

# Perform PCA on the scaled data
pca_result <- prcomp(data_scaled)

# Subset the cluster labels to match the number of data points in PCA
cluster_labels <- as.factor(kmeans_result$cluster)[1:nrow(pca_result$x)]

# Add cluster labels to PCA results
pca_result$cluster_label <- cluster_labels

# Create the combined plot
fviz_pca_biplot(pca_result, habillage = cluster_labels, addEllipses = TRUE, col.var = "navy")

library(corrplot)
# Analisis hubungan antara variabel dalam kluster
cluster_correlation <- data_cluster %>% 
  select(acousticness, danceability, energy, instrumentalness, valence, cluster_label)

# Korelasi antara variabel dalam kluster
cor_matrix <- cor(cluster_correlation[, -6])
print(cor_matrix)
#>                  acousticness danceability     energy instrumentalness
#> acousticness        1.0000000   -0.3645456 -0.7255764        0.3161541
#> danceability       -0.3645456    1.0000000  0.3258070       -0.3649412
#> energy             -0.7255764    0.3258070  1.0000000       -0.3789569
#> instrumentalness    0.3161541   -0.3649412 -0.3789569        1.0000000
#> valence            -0.3257982    0.5471540  0.4367712       -0.3075218
#>                     valence
#> acousticness     -0.3257982
#> danceability      0.5471540
#> energy            0.4367712
#> instrumentalness -0.3075218
#> valence           1.0000000
# Visualisasi korelasi antara variabel dalam kluster
corrplot(cor_matrix, method = "color")

💡 Insight :

  • Acousticness dan Energy memiliki korelasi negatif yang kuat (nilai -0.73), yang menunjukkan bahwa lagu-lagu dengan tingkat akustik yang tinggi cenderung memiliki tingkat energi yang rendah, dan sebaliknya.

  • Danceability dan Valence memiliki korelasi positif yang moderat (nilai 0.55), menunjukkan bahwa lagu-lagu yang mudah untuk ditari cenderung memiliki tingkat keceriaan atau positivitas yang tinggi.

  • Acousticness dan Instrumentalness memiliki korelasi positif yang moderat (nilai 0.32), yang menunjukkan bahwa lagu-lagu dengan tingkat akustik yang tinggi juga cenderung memiliki tingkat unsur instrumental yang lebih tinggi.

  • Danceability dan Acousticness memiliki korelasi negatif yang moderat (nilai -0.36), menunjukkan bahwa lagu-lagu yang mudah untuk ditari cenderung memiliki tingkat akustik yang lebih rendah.

  • Energy dan Valence memiliki korelasi positif yang moderat (nilai 0.44), yang menunjukkan bahwa lagu-lagu dengan tingkat energi yang tinggi juga cenderung memiliki tingkat keceriaan atau positivitas yang tinggi.

Kesimpulan

Dalam proyek ini, saya berhasil menganalisis data Spotify menggunakan teknik unsupervised learning. saya melakukan reduksi dimensi menggunakan PCA dan mengelompokkan lagu menggunakan algoritma k-means clustering. saya juga melakukan visualisasi data untuk memahami pola dan profil kluster.

Dalam visualisasi PCA, dapat dilihat hubungan antara variabel-variabel dalam data dan melihat kontribusi variabel terhadap komponen utama. Selain itu, visualisasi kluster memberikan gambaran tentang profil masing-masing kluster berdasarkan nilai rata-rata dari fitur-fiturnya.

Dengan menggunakan teknik unsupervised learning dan visualisasi, kita dapat memahami preferensi musik pengguna dan pola tersembunyi dalam data Spotify. Informasi ini dapat berguna untuk mengembangkan rekomendasi musik yang lebih personal dan memahami tren dalam industri musik.