OBJECTIVES

Pada latihan kali ini kita akan melakukan analisis Principal Component Analysis (PCA) untuk meringkas data besar menjadi komponen-komponen signifikan dari data tersebut. Data yang digunakan adalah Spotify data tracks, yang mana data tersebut terdiri dari komponen-komponen musik pada spotify, seperti genre, artists, popularity, energy, loudness, dan lain-lain. Selain itu juga akan dianalisis menggunakan metode unsupervised machine learning lain untuk klasifikasi, yaitu K-Means. Tujuan dari latihan ini adalah melihat komponen yang signifikan dan outliers menggunakan metode PCA dan clustering menggunakan K-Means. Latihan ini menggunakan R studio dan library yang digunakan adalah sebagai berikut:

library(dplyr)
library(FactoMineR)
library(factoextra)
library(tidyr)
library(tidyverse)

1. Data Import

Tahap pertama kita akan membaca data .csv dengan variabel bernama spotify

spotify <- read.csv("SpotifyFEatures.csv", stringsAsFactors = T)
head(spotify)

Berikut adalah keterangan dari tiap kolom yang ada pada data spotify

genre : The genre in which the track belongs artist_name : The artists’ names who performed the track track_name : Name of the track track_id: The Spotify ID for the track popularity : The popularity of a track is a value between 0 and 100, with 100 being the most popular acousticness : A confidence measure from 0.0 to 1.0 of whether the track is acoustic. 1.0 represents high confidence the track is acoustic danceability : Danceability describes how suitable a track is for dancing based on a combination of musical elements including tempo, rhythm stability, beat strength, and overall regularity. A value of 0.0 is least danceable and 1.0 is most danceable duration_ms : The track length in milliseconds energy : Energy is a measure from 0.0 to 1.0 and represents a perceptual measure of intensity and activity. Typically, energetic tracks feel fast, loud, and noisy. For example, death metal has high energy, while a Bach prelude scores low on the scale instrumentalness : Predicts whether a track contains no vocals. “Ooh” and “aah” sounds are treated as instrumental in this context. Rap or spoken word tracks are clearly “vocal”. The closer the instrumentalness value is to 1.0, the greater likelihood the track contains no vocal content key : The key the track is in liveness : Detects the presence of an audience in the recording. Higher liveness values represent an increased probability that the track was performed live. A value above 0.8 provides strong likelihood that the track is live loudness : The overall loudness of a track in decibels (dB) mode : Mode indicates the modality (major or minor) of a track, the type of scale from which its melodic content is derived. speechiness : Speechiness detects the presence of spoken words in a track. The more exclusively speech-like the recording (e.g. talk show, audio book, poetry), the closer to 1.0 the attribute value. Values above 0.66 describe tracks that are probably made entirely of spoken words. Values between 0.33 and 0.66 describe tracks that may contain both music and speech, either in sections or layered, including such cases as rap music. Values below 0.33 most likely represent music and other non-speech-like tracks tempo : The overall estimated tempo of a track in beats per minute (BPM). In musical terminology, tempo is the speed or pace of a given piece and derives directly from the average beat duration time_signature : An estimated time signature. The time signature (meter) is a notational convention to specify how many beats are in each bar (or measure). The time signature ranges from 3 to 7 indicating time signatures of 3/4, to 7/4 valence : A measure from 0.0 to 1.0 describing the musical positiveness conveyed by a track. Tracks with high valence sound more positive (e.g. happy, cheerful, euphoric), while tracks with low valence sound more negative (e.g. sad, depressed, angry)

2. Data Wrangling

a. Cek missing value

anyNA(spotify)
#> [1] FALSE

b. Subsetting

Dikarenakan untuk metode unsupervised harus numerik, kolom yang akan dibuang adalah kolom - genre - artist_name - track_name - track_id - key - mode - time_signature

# your code
# meng-assign nilai dari kolom track_name menjadi rownames
rownames(spotify) <- spotify$name

# membuang kolom yang tidak digunakan
spotify_clean <- spotify %>% 
  select(-c(genre, artist_name, track_name, track_id, key, mode, time_signature))

spotify_clean %>% glimpse()
#> Rows: 232,725
#> 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…

#3. Exploratory Data Analysis

Kita akan mengecek nilai angka dari seluruh kolom data frame spotify untuk melihat range dari tiap variabel

summary(spotify)
#>         genre                         artist_name       track_name    
#>  Comedy    :  9681   Giuseppe Verdi         :  1394   Home   :   100  
#>  Soundtrack:  9646   Giacomo Puccini        :  1137   You    :    71  
#>  Indie     :  9543   Kimbo Children's Music :   971   Intro  :    69  
#>  Jazz      :  9441   Nobuo Uematsu          :   825   Stay   :    63  
#>  Pop       :  9386   Richard Wagner         :   804   Wake Up:    59  
#>  Electronic:  9377   Wolfgang Amadeus Mozart:   800   Closer :    58  
#>  (Other)   :175651   (Other)                :226794   (Other):232305  
#>                    track_id        popularity      acousticness   
#>  0UE0RhnRaEYsiYgXpyLoZc:     8   Min.   :  0.00   Min.   :0.0000  
#>  0wY9rA9fJkuESyYm9uzVK5:     8   1st Qu.: 29.00   1st Qu.:0.0376  
#>  3R73Y7X53MIQZWnKloWq5i:     8   Median : 43.00   Median :0.2320  
#>  3uSSjnDMmoyERaAK9KvpJR:     8   Mean   : 41.13   Mean   :0.3686  
#>  6AIte2Iej1QKlaofpjCzW1:     8   3rd Qu.: 55.00   3rd Qu.:0.7220  
#>  6sVQNUvcVFTXvlk3ec0ngd:     8   Max.   :100.00   Max.   :0.9960  
#>  (Other)               :232677                                    
#>   danceability     duration_ms          energy          instrumentalness   
#>  Min.   :0.0569   Min.   :  15387   Min.   :0.0000203   Min.   :0.0000000  
#>  1st Qu.:0.4350   1st Qu.: 182857   1st Qu.:0.3850000   1st Qu.:0.0000000  
#>  Median :0.5710   Median : 220427   Median :0.6050000   Median :0.0000443  
#>  Mean   :0.5544   Mean   : 235122   Mean   :0.5709577   Mean   :0.1483012  
#>  3rd Qu.:0.6920   3rd Qu.: 265768   3rd Qu.:0.7870000   3rd Qu.:0.0358000  
#>  Max.   :0.9890   Max.   :5552917   Max.   :0.9990000   Max.   :0.9990000  
#>                                                                            
#>       key           liveness          loudness          mode       
#>  C      :27583   Min.   :0.00967   Min.   :-52.457   Major:151744  
#>  G      :26390   1st Qu.:0.09740   1st Qu.:-11.771   Minor: 80981  
#>  D      :24077   Median :0.12800   Median : -7.762                 
#>  C#     :23201   Mean   :0.21501   Mean   : -9.570                 
#>  A      :22671   3rd Qu.:0.26400   3rd Qu.: -5.501                 
#>  F      :20279   Max.   :1.00000   Max.   :  3.744                 
#>  (Other):88524                                                     
#>   speechiness         tempo        time_signature    valence      
#>  Min.   :0.0222   Min.   : 30.38   0/4:     8     Min.   :0.0000  
#>  1st Qu.:0.0367   1st Qu.: 92.96   1/4:  2608     1st Qu.:0.2370  
#>  Median :0.0501   Median :115.78   3/4: 24111     Median :0.4440  
#>  Mean   :0.1208   Mean   :117.67   4/4:200760     Mean   :0.4549  
#>  3rd Qu.:0.1050   3rd Qu.:139.05   5/4:  5238     3rd Qu.:0.6600  
#>  Max.   :0.9670   Max.   :242.90                  Max.   :1.0000  
#> 

Dari hasil di atas, masih terdapat nilai yang rangenya terlampau jauh. Terdapat nilai negatif, terdapat nilai desimal, bahkan terdapat nilai ratusan hingga ribuan. Karena angka-angka tersebut cukup signifikan untuk menentukan karakteristik dari data yang akan kita buat, maka kita perlu melakukan scaling pada data frame tersebut

Scaling

Scaling menggunakan fungsi scaling

# your code here
spotify_scale <- scale(spotify_clean)

Data spotify setelah dilakukan scaling ada sebagai berikut

head(spotify_scale)
#>      popularity acousticness danceability duration_ms     energy
#> [1,]  -2.261002    0.6833748   -0.8909329  -1.1413655  1.2869052
#> [2,]  -2.206026   -0.3454664    0.1919933  -0.8218657  0.6302479
#> [3,]  -2.096075    1.6445663    0.5852948  -0.5452965 -1.6699502
#> [4,]  -2.261002    0.9426992   -1.6936990  -0.6952933 -0.9297874
#> [5,]  -2.041100    1.6389288   -1.2034190  -1.2821808 -1.3131538
#> [6,]  -2.261002    1.0723614    0.1273410  -0.6263486 -1.8073548
#>      instrumentalness    liveness   loudness speechiness      tempo    valence
#> [1,]      -0.48981747  0.66065975  1.2907007  -0.3679692  1.5956039  1.3807413
#> [2,]      -0.48981747 -0.32283477  0.6686811  -0.1830817  1.8232495  1.3884316
#> [3,]      -0.48981747 -0.56492573 -0.7184009  -0.4558311 -0.5883245 -0.3342114
#> [4,]      -0.48981747 -0.58762176 -0.4348159  -0.4380431  1.7505932 -0.8763826
#> [5,]      -0.08356631 -0.06561313 -1.9305971  -0.4051623  0.7414313 -0.2496173
#> [6,]      -0.48981747 -0.54475148 -0.9002886   0.1198533 -0.9769791 -0.3726633

4. Principal Component Analysis (PCA)

a. Pembuatan Model PCA

Kita akan membuat model PCA, yang mana adalah model yang idenya sama seperti menentukan model eigenvalue dan eigenvector, yang mana kita menentukan karakteristik data kita dalam berupa angka yang sebenarnya adalah pembuatan sumbu baru dari data yang telah kita miliki sebelumnya.

pca_spotify <- PCA(X = spotify_scale,
                scale.unit = FALSE,
                # quali.sup = qualivar, # index variabel kategorikal (qualivar)
                graph = F,
                ncp = 11) # memasukkan dengan jumlah dimensi data awal kita
  1. Eigenvalue

Berikut adalah nilai eigen dari hasil pemodelan PCA menggunakan FactoMiner

# $eig ekuivalen -> `summary(prcomp())`
pca_spotify$eig
#>         eigenvalue percentage of variance cumulative percentage of variance
#> comp 1   3.6104430              32.822350                          32.82235
#> comp 2   1.7100248              15.545747                          48.36810
#> comp 3   1.1712427              10.647707                          59.01580
#> comp 4   0.9998305               9.089407                          68.10521
#> comp 5   0.8617186               7.833839                          75.93905
#> comp 6   0.7567533               6.879605                          82.81866
#> comp 7   0.6378529               5.798688                          88.61734
#> comp 8   0.4853764               4.412532                          93.02988
#> comp 9   0.3751899               3.410832                          96.44071
#> comp 10  0.2767438               2.515864                          98.95657
#> comp 11  0.1147767               1.043429                         100.00000
  1. Eigenvector

Eigenvector adalah vektor karakteristik dari tiap PC

# $var$coord ekuivalen -> `prcomp()$rotation`
pca_spotify$var$coord
#>                        Dim.1       Dim.2       Dim.3        Dim.4         Dim.5
#> popularity        0.44917466 -0.39028999  0.10760956  0.422083815 -0.4397128927
#> acousticness     -0.79848319  0.24548236 -0.22570238  0.006878892  0.0262409864
#> danceability      0.63509619  0.07871289 -0.48777270  0.239667940  0.2205899208
#> duration_ms      -0.11406387 -0.04283374  0.64233627  0.418290717  0.5984681336
#> energy            0.84799794  0.12674992  0.26729741 -0.097170181  0.0002286136
#> instrumentalness -0.61147458 -0.23915364  0.07559677 -0.132945278  0.1457079168
#> liveness          0.05634058  0.80987503  0.27460405 -0.056653595 -0.1159456730
#> loudness          0.88742087 -0.03224185  0.16603160  0.011411355 -0.0587820238
#> speechiness       0.05644629  0.84418745  0.03086711  0.068073171 -0.0886035122
#> tempo             0.29865883 -0.19533933  0.28027575 -0.733159532  0.0530787746
#> valence           0.61536734  0.09164875 -0.44559995 -0.128869764  0.4605008930
#>                        Dim.6       Dim.7        Dim.8        Dim.9       Dim.10
#> popularity        0.27909886  0.33729483  0.218855862  0.152032169 -0.008042847
#> acousticness      0.26175820 -0.03749408  0.107202305  0.160097978  0.368013093
#> danceability      0.19371407  0.23273855 -0.126586501 -0.358408124  0.078796508
#> duration_ms       0.19755256 -0.01300927  0.010087935  0.009425555  0.006027828
#> energy           -0.30449316  0.03118583 -0.083559300  0.137511526  0.074861212
#> instrumentalness -0.38612700  0.60200934 -0.048901792 -0.007026588  0.093374835
#> liveness         -0.09725448  0.10451736  0.421855399 -0.224176578  0.005442736
#> loudness         -0.13496823 -0.11298891 -0.080988800  0.007920115  0.327619910
#> speechiness       0.18411893  0.23125097 -0.360378345  0.206644719 -0.084061096
#> tempo             0.47856910  0.14701952 -0.007557944 -0.053725723  0.011777191
#> valence          -0.03707389  0.07856797  0.293318718  0.288156865 -0.078152551
#>                         Dim.11
#> popularity        0.0094461784
#> acousticness      0.0894468972
#> danceability      0.0636252634
#> duration_ms       0.0003046402
#> energy            0.2423947898
#> instrumentalness -0.0401377523
#> liveness         -0.0153104453
#> loudness         -0.1880124675
#> speechiness      -0.0608448983
#> tempo             0.0045824622
#> valence          -0.0544607858
  1. Nilai baru di setiap PC

Adalah hasil dari perkalian eigenvector

# $ind$coord ekuivalen -> `prcomp()$x`
as.data.frame(pca_spotify$ind$coord) %>% head()

b. Individual & Variable Factor Map

Untuk memudahkan kita membayangkan hasil perhitungan eigenvalue dan eigenvector, kita akan membuat plot dari model pca yang telah kita buat

plot.PCA(
  x = pca_spotify,           
  choix = "ind",      
  # habillage = "not_paid",
  select = "contrib 10",
  invisible = "quali"
)

Dari plot di atas, kita mengetahui bahwa PC1 dan PC2 cukup menggambarkan karakteristik data secara keseluruhan. Meskipun dari plot tampak bahwa data terlalu banyak, amun bisa disimpulkan bahwa terdapat karakteristik yang lebih banyak di bawah sumbu x, dan juga terdapat beberapa outliers dari karakter yang mayoritas sejenis. Hal ini belum bisa menyimpulkan apa-apa, sehingga kita akan dibantu oleh bentuk plot lainnya

Variable Factor Map

Kita akan melihat korelasi antar variabel dari tiap PC.

plot.PCA(x = pca_spotify, # objek PCA factominer
         choix = "var") # var: variable factor map -> ind: individual factor map

Dari plot di atas, PC1 paling banyak merangkum energy, danceability, dan loudness, instrumentalness, dan acousticness. Sedangkan PC2 paling banyak merangkum spechiness, liveness, dan popularity. Pasangan variabel yang saling berkorelasi tinggi positif adalah speechiness dan liveness, dan danceability dan loudness.

Dimension Description

Selanjutnya kita akan menggunakan fungsi fviz_contrib untuk melihat urutan kontribusi variabel ke tiap PC

fviz_contrib(X = pca_spotify,
             choice = "var",
             axes = 2) # PC1 = 1, PC2 = 2.

Berdasarkan plot di atas, dapat disimpulkan bahwa variabel yang melebihi garis putus-putus merah, variabel tersebut dirangkum paling banyak pada PC 1 dan PC 2. Pada kasus ini, speechiness dan liveness adalah variabel yang paling banyak dirangkum.

c. Reduce Dimension

Untuk mengurangi jumlah dimensi pada PCA, pertama-tama kita akan mengecek ulang nilai eigenvalue dari model pca.

# PCA summary -> summary(prcomp())
pca_spotify$eig
#>         eigenvalue percentage of variance cumulative percentage of variance
#> comp 1   3.6104430              32.822350                          32.82235
#> comp 2   1.7100248              15.545747                          48.36810
#> comp 3   1.1712427              10.647707                          59.01580
#> comp 4   0.9998305               9.089407                          68.10521
#> comp 5   0.8617186               7.833839                          75.93905
#> comp 6   0.7567533               6.879605                          82.81866
#> comp 7   0.6378529               5.798688                          88.61734
#> comp 8   0.4853764               4.412532                          93.02988
#> comp 9   0.3751899               3.410832                          96.44071
#> comp 10  0.2767438               2.515864                          98.95657
#> comp 11  0.1147767               1.043429                         100.00000

Apabila kita ingin mengambil minimal 80% informasi, maka kita membutuhkan 6 PC

Maka kita akan mengambil 6 PC pertama

pca_spotify$ind$coord %>% head()
#>        Dim.1      Dim.2      Dim.3      Dim.4       Dim.5       Dim.6
#> 1  0.9904481  0.9993903 -0.1597105 -3.0962912  0.71325308 -0.74345195
#> 2  1.2096105  0.2730414 -0.6842276 -2.7298245  1.28269932 -0.12134335
#> 3 -2.1123978  0.3531611 -1.8668172 -0.2666744  0.70972338  0.39952609
#> 4 -1.9545010 -0.1868432  0.2513141 -2.6613814 -0.02211554  0.60691571
#> 5 -2.9355815  0.3915754 -1.1231315 -2.0937683 -0.02518811  0.41551544
#> 6 -2.2612410  0.7007202 -1.7307093 -0.1446502  0.52331036  0.03778243
#>        Dim.7      Dim.8        Dim.9      Dim.10      Dim.11
#> 1 -1.3200974  0.4246202  0.576058781  1.16234095  0.04739096
#> 2 -0.9083572 -0.4478824 -0.057434104  0.09104371 -0.13059333
#> 3 -1.4628733 -0.4420655 -0.872496223  0.59542626 -0.09975048
#> 4 -1.7930776 -0.5882799 -0.041750169  0.17564117 -0.22897820
#> 5 -1.1238702  0.2555989 -0.007753993 -0.30349575  0.41629524
#> 6 -1.5234275 -0.7598055 -0.626642226 -0.09946476 -0.44224934
spot_keep <- as.data.frame(pca_spotify$ind$coord[,1:6])

spot_keep %>% head()

d. Biplot

Selanjutnya kita akan mencoba membuat Biplot. Biplot berguna untuk kita menganalisa adanya korelasi, dan juga outliers

# subset 100 data pertama agar tidak terlalu menumpuk
spot_small <- spotify_clean %>% head(100)

# melakukan PCA
pca_small <- prcomp(spot_small, scale = TRUE)

# membuat biplot
biplot(x = pca_small,
       cex = 0.7,
       scale = FALSE)

Dari plot di atas, outliers yang sangat terlihat adalah data nomor 55

spotify_clean[55,]

Ternyata data nomor 55 memiliki popularity nol, yang mana membuat dia jauh dari panah yang lain

Selain data no 55, kita bisa lebih jelas melihat terdapat outliers pada data no 16 dan no 97

spotify_clean[16,]
spotify_clean[97,]

Yang menyebabkan dua data di atas termasuk outliers adalah selain karena popularitynya nol, melainkan juga dikarenakan nilai spechiness yang terlampau sangat tinggi dibandingkan data sekitarnya.

5. K-Means Clustering

K-Means Clusteringa adalah clustering dengan menyamakan jenis karakteristik berdasarkan kemiripan data yang ditranslate dalam bentuk jarak. Maka dari itu untuk pemodelan ini dibutuhkan data numerik.

a. Data pre-processing

Dengan clustering kita dapat membuat kelompok musik yang memiliki karakteristik genre yang sama pada tiap clusternya

Selanjutnya kita akan mengulangi proses persiapan data, dikarenakan kita memerlukan index yang baru yaitu kolom track_name untuk membantu mengklasifikasikan kelompok musik yang kita inginkan.

#menghilangkan nama lagu yang sama sehingga menjadi unique
spotify <- spotify[!duplicated(spotify$track_name), ]

#set kolom track_name sebagai rownames
rownames(spotify) <- spotify$track_name

#remove original track_name column from data frame
spotify$track_name <- NULL

#view updated data frame
head(spotify)

Selanjutnya kita proses ulang data spotify dengan index baru, sama seperti pre-processing data untuk PCA, bedanya kita tidak perlu menuliskan kolom track_name saat menghilangkan kolom yang tidak akan kita gunakan

# membuang kolom yang tidak digunakan
spotify_clean <- spotify %>% 
  select(-c(genre, artist_name, track_id, key, mode, time_signature))

Kemudian kita scale ulang data spotify

spotify_scale <- scale(spotify_clean)

Berikut adalah data spotify setelah dilakukan scaling

head(spotify_scale)
#>                                  popularity acousticness danceability
#> C'est beau de faire un Show       -2.049284    0.5280107   -0.7598805
#> Perdu d'avance (par Gad Elmaleh)  -1.991859   -0.4557947    0.2819632
#> Don't Let Me Be Lonely Tonight    -1.877008    1.4471276    0.6603442
#> Dis-moi Monsieur Gordon Cooper    -2.049284    0.7759836   -1.5321925
#> Ouverture                         -1.819583    1.4417368   -1.0605120
#> Le petit souper aux chandelles    -2.049284    0.8999700    0.2197635
#>                                  duration_ms     energy instrumentalness
#> C'est beau de faire un Show       -1.0129040  1.2731734       -0.5505784
#> Perdu d'avance (par Gad Elmaleh)  -0.7352963  0.6573688       -0.5505784
#> Don't Let Me Be Lonely Tonight    -0.4949902 -1.4997272       -0.5505784
#> Dis-moi Monsieur Gordon Cooper    -0.6253198 -0.8056122       -0.5505784
#> Ouverture                         -1.1352560 -1.1651282       -0.1781535
#> Le petit souper aux chandelles    -0.5654149 -1.6285834       -0.5505784
#>                                    liveness   loudness speechiness      tempo
#> C'est beau de faire un Show       0.5443334  1.2923966 -0.37223500  1.5947595
#> Perdu d'avance (par Gad Elmaleh) -0.3614760  0.7286643 -0.20858210  1.8189239
#> Don't Let Me Be Lonely Tonight   -0.5844445 -0.5284391 -0.45000591 -0.5557715
#> Dis-moi Monsieur Gordon Cooper   -0.6053478 -0.2714279 -0.43426088  1.7473787
#> Ouverture                        -0.1245720 -1.6270447 -0.40515643  0.7536492
#> Le petit souper aux chandelles   -0.5658638 -0.6932828  0.05956055 -0.9384826
#>                                     valence
#> C'est beau de faire un Show       1.3543538
#> Perdu d'avance (par Gad Elmaleh)  1.3617662
#> Don't Let Me Be Lonely Tonight   -0.2986175
#> Dis-moi Monsieur Gordon Cooper   -0.8211936
#> Ouverture                        -0.2170808
#> Le petit souper aux chandelles   -0.3356796

b. K-means clustering

Membuat cluster menggunakan fungsi kmeans. Kita akan menggunakan 5 centers sebagai acuan kita dalam pembentukan cluster. Memilih 5 centers dengan pertimbangan banyaknya track yang dianalisis.

RNGkind(sample.kind = "Rounding")
set.seed(100)

spotify_cluster <- kmeans(x = spotify_scale,
                       centers = 5)
  • Menghitung banyaknya observasi pada tiap cluster

Selanjutnya kita akan mengecek banyaknya data pada tiap cluster

spotify_cluster$size
#> [1] 26320 52693 21679 39014  8909
  • Mencari letak pusat cluster/centroid
spotify_cluster$centers
#>   popularity acousticness danceability duration_ms     energy instrumentalness
#> 1 -0.5240686    1.0008838   -0.2892148 -0.15984313 -0.9489471       -0.2876111
#> 2  0.4854918   -0.5522806    0.8273668 -0.09261711  0.4094024       -0.3477326
#> 3 -0.4446846    1.1683417   -1.3240279  0.24579113 -1.3999888        1.7608657
#> 4  0.1439168   -0.8132621   -0.2201466  0.08160808  0.7749498       -0.1896271
#> 5 -0.8713644    1.0279705    0.1468290  0.06453936  0.3951186       -0.5480635
#>     liveness   loudness speechiness      tempo      valence
#> 1 -0.1502987 -0.4891925  -0.3107940 -0.1685249 -0.343655524
#> 2 -0.2589489  0.4879831  -0.1438535 -0.2523826  0.656898563
#> 3 -0.3983677 -1.6483999  -0.4081409 -0.4891775 -1.127646374
#> 4  0.1405609  0.6435782  -0.1892740  0.8652927 -0.001440749
#> 5  2.3294451 -0.2481362   3.5910418 -0.6082932 -0.119708997
  • Label cluster untuk tiap observasi

Kita akan melihat hasil clustering dari setiap observasi

head(spotify_cluster$cluster)
#>      C'est beau de faire un Show Perdu d'avance (par Gad Elmaleh) 
#>                                4                                4 
#>   Don't Let Me Be Lonely Tonight   Dis-moi Monsieur Gordon Cooper 
#>                                1                                1 
#>                        Ouverture   Le petit souper aux chandelles 
#>                                1                                1
  • Menghitung banyaknya iterasi

Kita akan melihat jumlah iterasi algoritma k-means sampai dihasilkan cluster yang stabil

spotify_cluster$iter
#> [1] 6

Hasil iterasi yang dibutuhkan untuk mencapai cluster yang stabil adalah 6 kali.

c. Goodness of Fit

Kita akan menentukan hasil clustering yang kita tentukan apakah baik atau tidak, ditentukan dari tig anilai berikut:

  • Within Sum of Squares ($withinss) jumlah jarak kuadrat dari tiap data ke centroid tiap cluster

  • Between Sum of Squares ($betweenss): jumlah jarak kuadrat dari tiap centroid ke rata-rata global.

  • Total Sum of Squares ($totss): jumlah jarak kuadrat dari tiap observasi ke rata-rata global.

Cek nilai wss

spotify_cluster$withinss
#> [1] 165036.36 232618.54 160962.65 227131.46  79456.46

Cek nilai bss dan tss

spotify_cluster$betweenss
#> [1] 769548.5
spotify_cluster$totss
#> [1] 1634754

Cek rasio bss/tss

spotify_cluster$betweenss / spotify_cluster$totss
#> [1] 0.4707427

Clustering yang baik adalah yang memiliki nilai wss yang rendah, dikarenakan jarak antar data di dalam cluster semakin kecil, yang mana menunjukkan adanya karakteristik cluster yang kuat. Namun nilai wss yang ditemukan memiliki range 79.000 - 227.000, yang mana cukup jauh.

Kemudian nilai bss/tss juga menentukan baik atau tidaknya clustering. Clustering yang baik adalah yang memiliki nilai bss/tss mendekati 1. Sementara nilai bss/tss yang kita miliki sebesar 0.47, yang mana masih jauh dari angka 1. Selanjutnya akan kita buat lagi jumlah cluster yang lebih besar, kali ini berdasarkan jumlah genre pada data di spotify.

unique(spotify$genre)
#>  [1] Movie            R&B              A Capella        Alternative     
#>  [5] Country          Dance            Electronic       Anime           
#>  [9] Folk             Blues            Opera            Hip-Hop         
#> [13] Children's Music Children’s Music Rap              Indie           
#> [17] Classical        Pop              Reggae           Reggaeton       
#> [21] Jazz             Rock             Ska              Comedy          
#> [25] Soul             Soundtrack       World           
#> 27 Levels: A Capella Alternative Anime Blues ... World
RNGkind(sample.kind = "Rounding")
set.seed(100)

spotify_cluster27 <- kmeans(x = spotify_scale,
                       centers = 27)

Cek rasio wss

spotify_cluster27$withinss
#>  [1] 13844.072 19599.227 19572.562 10043.623 27943.862 20159.921 14350.700
#>  [8] 16953.158 33351.575 18829.397 20165.779 30506.812  9984.040 12662.742
#> [15] 23397.159 17668.778 22942.454 13862.741 21697.649 19130.523 14628.544
#> [22] 13975.522  4658.234 14617.270 11110.254 14615.444 21514.630

Cek rasio bss/tss

spotify_cluster27$betweenss / spotify_cluster$totss
#> [1] 0.7052849

Cek jumlah data dalam setiap cluster

spotify_cluster27$size 
#>  [1]  3750  8395  2117  2455  5736  9371  5302  7482  7452  7843  7886  8703
#> [13]  2718  3820  5737  6414  5093  5149 10064  7757  5065  5425   103  2872
#> [25]  2153  4874  4879

Dengan memilih nilai cluster yang lebih besar, yaitu 27 cluster, menunjukkan nilai wss yang lebih rendah, dengan range 9.000 - 33.000, dengan nilai bss/tss mencapai nilai 0.7. Hal ini menjadi parameter yang lebih baik dari yang sebelumnya, dan selanjutnya kita akan menggunakan 27 cluster ini.

d. Interpretation: Cluster Profiling

Membuat kolom baru yang berisikan informasi label dari cluster yang terbentuk menggunakan k = 27. Kita akan memasukkan label cluster ke data awal spotify_clean dengan nama kolom kelompok.

spotify_clean$cluster <- as.factor(spotify_cluster27$cluster)

spotify_clean %>% head()

Selanjutnya kita akan melakukan grouping berdasarkan cluster yang terbentuk, untuk mengetahui karakteristik dari masing-masing cluster

# melakukan profiling 
spotify_centroid <- spotify_clean %>% 
  group_by(cluster) %>% 
  summarise_all(mean)

spotify_centroid

Untuk mempermudah profiling, kita akanmembuat tabel yang menampilkan cluster dengan nilai terendah dan tertinggi untuk masing-masing karakteristik lagu

spotify_centroid %>% 
  pivot_longer(-cluster) %>% 
  group_by(name) %>% 
  summarize(
    kelompok_min = which.min(value),
    kelompok_max = which.max(value))

Berikut adalah contoh beberapa sample analisis profiling tiap cluster dan perkiraan label pade masing-masing cluster

Cluster 15 : - Paling tinggi liveness dan speechiness - Label : Musik live

Cluster 12 : - Paling rendah di: danceability, energy, liveness, loudness, speechiness, tempo, dan valence - Label : Musik Lo-fi

e. Visualisasi Clustering

Kita akan membuat visualisasi clustering. Karena untuk kebutuhan visualisasi, kita akan membuat model kmeans baru dengan 100 data pertama saja dengan 5 cluster

spot_small_cluster <- spotify_clean %>% select(-cluster) %>% head(100)
spotify_cluster_small <- kmeans(x = spotify_scale %>% head(100),
                       centers = 5)
# visualisasi 2 dimensi
fviz_cluster(object = spotify_cluster_small, 
             data = spot_small_cluster )

Berdasarkan visualisasi di atas, dapat dilihat bahwa terdapat beberapa cluster yang menumpuk, sehingga sulit terbaca. Sedikit hal yang bisa disimpulkan adalah terdapat kesamaan judul lagu berbahasa Perancis yang membuat beberapa lagu tersebut terdapat di cluster yang sama.

6. Visualisasi Biplot dan Clustering

KIta akan membuat visualisasi yang menampilkan tampilan individual dan variables factor map menjadi satu menggunakan fungsi fviz_pca_biplot() . Untuk kemudahan pembuatan visualisasi, kita akan membuat model PCA lagi dengan 100 data pertama dari data spotify yang sudah dilakukan scaling.

# buat model PCA
spot_pca <- PCA(X = spotify_scale %>% head(100),
               scale.unit = F,
               graph = F)

Selanjutnya kita akan membuat visualisasi dari fungsi PCA yang telah dibuat dari 100 data pertama (spot_pca)

fviz_pca_biplot(X = spot_pca, 
                geom.ind = "point",
                addEllipses = T)

Dari plot di atas, kita dapat melihat bahwa dari seluruh variabel dalam 100 data pertama, mayoritas dipengaruhi oleh variabel-variabel seperti energy, valence, danceablity, loudness, instrumentalness, dan acousticness, dan lain-lain sesuai plot, kecuali duration_ms. Dari plot ini, dapat disimpulkan bahwa variabel-variable ini membentuk 1 cluster, dan tidak tampak cluster yang lain yang terbentuk.

CONCLUSION

Berdasarkan latihan kali ini menggunakan model spotify, terdapat beberapa evaluasi. Evaluasi yang pertama, alangkah baiknya apabila unsupervised learning menggunakan data yang tidak terlalu banyak, namun cukup. Karena dengan data yang berlebih, tidak mudah untuk divisualisasikan. Meskipun begitu, bukan menjadi hal yang tidak mungkin untuk dianalisis. Berdasarkan model PCA dan K-means, hal yang dapat dibandingkan adalah variabel-variabel yang mempengaruhi terbentuknya suatu cluster. Sebagai contoh, dari hasil visualisasi PCA, terdapat korelasi yang sangat kuat antara speechiness - liveness dan danceability - loudness. Sementara dari hasil clustering, sebagai contoh cluster 15 memiliki speechiness - liveness tertinggi. Hal ini menunjukkan kedua model ini bekerja dengan baik menentukan klasifikasi menggunakan metode eigenvalue - eigenvector maupun berdasarkan jarak kuadrat dengan penentuan jumlah cluster pada K-means.