library(tidyverse)
library(ggplot2)
library(GGally)
library(FactoMineR)
library(factoextra)
options(scipen=999)R.version## _
## platform x86_64-w64-mingw32
## arch x86_64
## os mingw32
## crt ucrt
## system x86_64, mingw32
## status
## major 4
## minor 2.1
## year 2022
## month 06
## day 23
## svn rev 82513
## language R
## version.string R version 4.2.1 (2022-06-23 ucrt)
## nickname Funny-Looking Kid
LBB ini mendemonstrasikan penggunaan PCA dan KMeans (unsupervised learning).
.Rmd atau styles.css bisa melihat
github proyek ini di taruma/mls-lbb-4.
|> yang tersedia di R 4.1+
untuk versi selain itu gunakan %>%.
Data yang digunakan adalah data Spotify Tracks DB yang diperoleh di Kaggle. Dataset ini berisikan sejumlah lagu dengan karakteristik musiknya. Berikut 6 baris pertama dari dataset:
spotify <- read.csv("data/SpotifyFeatures.csv")
spotify |> head()Dan berikut struktur data:
spotify |> str()## 'data.frame': 232725 obs. of 18 variables:
## $ genre : chr "Movie" "Movie" "Movie" "Movie" ...
## $ artist_name : chr "Henri Salvador" "Martin & les fées" "Joseph Williams" "Henri Salvador" ...
## $ track_name : chr "C'est beau de faire un Show" "Perdu d'avance (par Gad Elmaleh)" "Don't Let Me Be Lonely Tonight" "Dis-moi Monsieur Gordon Cooper" ...
## $ track_id : chr "0BRjO6ga9RKCKjfDqeFgWV" "0BjC1NfoEOOusryehmNudP" "0CoSDzoNIKCRs124s9uTVy" "0Gc6TVm52BwZD07Ki6tIvf" ...
## $ popularity : int 0 1 3 0 4 0 2 15 0 10 ...
## $ acousticness : num 0.611 0.246 0.952 0.703 0.95 0.749 0.344 0.939 0.00104 0.319 ...
## $ danceability : num 0.389 0.59 0.663 0.24 0.331 0.578 0.703 0.416 0.734 0.598 ...
## $ duration_ms : int 99373 137373 170267 152427 82625 160627 212293 240067 226200 152694 ...
## $ energy : num 0.91 0.737 0.131 0.326 0.225 0.0948 0.27 0.269 0.481 0.705 ...
## $ instrumentalness: num 0 0 0 0 0.123 0 0 0 0.00086 0.00125 ...
## $ key : chr "C#" "F#" "C" "C#" ...
## $ liveness : num 0.346 0.151 0.103 0.0985 0.202 0.107 0.105 0.113 0.0765 0.349 ...
## $ loudness : num -1.83 -5.56 -13.88 -12.18 -21.15 ...
## $ mode : chr "Major" "Minor" "Minor" "Major" ...
## $ speechiness : num 0.0525 0.0868 0.0362 0.0395 0.0456 0.143 0.953 0.0286 0.046 0.0281 ...
## $ tempo : num 167 174 99.5 171.8 140.6 ...
## $ time_signature : chr "4/4" "4/4" "5/4" "4/4" ...
## $ valence : num 0.814 0.816 0.368 0.227 0.39 0.358 0.533 0.274 0.765 0.718 ...
Untuk keterangan setiap kolomnya bisa dilihat disini. Berikut penjelasan beberapa kolom yang diambil dari halaman tersebut.
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.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.
Perceptual features contributing to this attribute include dynamic
range, perceived loudness, timbre, onset rate, and general
entropy.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. Values above 0.5 are intended to represent
instrumental tracks, but confidence is higher as the value approaches
1.0.key: The key the track is in. Integers map to
pitches using standard Pitch Class notation. E.g. 0 = C, 1 = C♯/D♭, 2 =
D, and so on. If no key was detected, the value is -1.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). Loudness values are averaged across the entire track and
are useful for comparing relative loudness of tracks. Loudness is the
quality of a sound that is the primary psychological correlate of
physical strength (amplitude). Values typically range between -60 and 0
db.mode: Mode indicates the modality (major or minor)
of a track, the type of scale from which its melodic content is derived.
Major is represented by 1 and minor is 0.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).Dilihat secara sekilas datasetnya:
spotify |> glimpse()## 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.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…
Setelah ini masuk ketahapan R1 - Pemrosesan Data
# untuk menyimpan objek original
spotify_original <- spotifyPada bab ini akan dievaluasi datasetnya seperti kelengkapannya, tipe data sebelum memasuki eksplorasi dataset maupun pemodelan.
Karena kolom dataset masih tergolong sedikit, jadi untuk pemisahan kolom numerik dan kategori bisa dilakukan secara manual.
Dari kilasan data diatas, bisa disimpulkan bahwa kolom
genre, key, mode, dan
time_signature merupakan kolom yang berupa kategori. Dan
dengan informasi tersebut, kolom diperbarui dengan mengubah kolom
tersebut menjadi factor dan dsimpan sebagai objek
spotify_factored.
factor_columns <- c("genre", "key", "mode", "time_signature")
spotify_factored <- spotify |>
mutate(across(all_of(factor_columns), as.factor))
spotify_factored |> head()Perlu dicatat bahwa kolom seperti artist_name,
track_name, track_id bukanlah sebuah kategori
meski bertipe character. Hal tersebut dikarenakan nilainya
sangat unik untuk setiap observasi. Untuk pemeriksaan melalui kode, bisa
diperiksa setiap kolom dengan unique() |> length(). Tapi
karena ini dataset yang cukup familiar dan bisa ditebak dengan nalar,
proses yang dilakukan manual. Setelah menentukan kolom kategori
dilanjutkan ke kolom berupa angka/numerik.
Menggunakan is.numeric, dibuat variabel baru bernama
numeric_columns yang berisikan nama-nama kolom yang berupa
angka. Berikut variabel numeric_columns:
numeric_columns <- spotify |> select(where(is.numeric)) |> colnames()
numeric_columns## [1] "popularity" "acousticness" "danceability" "duration_ms"
## [5] "energy" "instrumentalness" "liveness" "loudness"
## [9] "speechiness" "tempo" "valence"
Setelah selesai dengan mengubah dataset menjadi tipe data yang seharusnya, bisa dilanjutkan dengan mengecek kelengkapan datanya.
Pada langkah ini akan diperiksa kelengkapan data. Kelengkapan data di
cek dengan anyNA. Berikut hasilnya:
anyNA(spotify_factored)## [1] FALSE
Diperoleh FALSE, maka data spotify tidak ada memiliki
nilai yang kosong. Dan bisa dilanjutkan ke proses berikutnya, yaitu
mengeksplorasi dataset.
Hasil dari kelengkapan data,
digunakan untuk eksplorasi dataset. Diperiksa dimensi (observasi dan
variabel/baris dan kolom) dengan dim():
dim(spotify_factored)## [1] 232725 18
Diketahui terdapat \(232,725\) baris
dan \(18\) kolom, yang masing-masing
menunjukkan observasi dan variabel. Selanjutnya dilihat ringkasan
dataset spotify_factored. Pertama, dilihat ringkasan untuk
kolom berkategori:
spotify_factored |>
select(all_of(factor_columns)) |>
summary()## genre key mode time_signature
## Comedy : 9681 C :27583 Major:151744 0/4: 8
## Soundtrack: 9646 G :26390 Minor: 80981 1/4: 2608
## Indie : 9543 D :24077 3/4: 24111
## Jazz : 9441 C# :23201 4/4:200760
## Pop : 9386 A :22671 5/4: 5238
## Electronic: 9377 F :20279
## (Other) :175651 (Other):88524
Dari summary() diatas, bisa sedikit diterka bahwa
genre dan key lumayan merata (hampir 5
kategori terbanyak memiliki jumlah yang tidak jauh berbeda). Akan
tetapi, hal tersebut masih perlu diinvestigasi lebih lanjut. Diketahui
juga dari ringkasan diatas, pada time_signature, tanda
birama \(\frac{4}{4}\) lebih banyak dan
\(\frac{0}{4}\) sangat sedikit dan bisa
dihapus (untuk sementara tetap digunakan.
Selanjutnya melihat ringkasan untuk setiap kolom numerik dengan cara yang serupa:
spotify_factored |>
select(all_of(numeric_columns)) |>
summary()## 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
Dari penjelasan tiap kolom di halaman bantuan spotify, bisa diketahui
juga batasan nilai setiap kolom. Tapi tidak semua nilai numerik ini bisa
merepresentasikan karakteristik musik. Semisal informasi
duration_ms bukanlah karakteristik dari musik sendiri.
Sehingga, masih bisa untuk dieksplorasi lebih lanjut terkait dataset
numerik. Karakteristik musik disini diasumsikan sebagai suatu nilai yang
bisa merepresentasikan/merefleksikan musik dengan range nilai
\([0, 1]\) (seperti
energy, instrumentalness,
liveness, dll).
Sebagai langkah percobaan, kolom numerik yang memiliki range
\([0, 1]\) dipisahkan dengan kolom
numerik yang diluar tersebut. Hal tersebut, dikarenakan karakteristik
musik/lagu, biasanya memiliki range \([0,1]\). Nama kolom yang memiliki
range tersebut disimpan sebagai variabel
num_compare_columns.
num_compare_columns <- spotify_factored |>
select(all_of(numeric_columns)) |>
select_if(function(.) max(.) <= 1) |>
colnames()
num_compare_columns## [1] "acousticness" "danceability" "energy" "instrumentalness"
## [5] "liveness" "speechiness" "valence"
Karena diketahui dataset memiliki lebih dari \(200,000\) (dua ratus ribu) baris, maka
digunakan random sampling sebanyak \(1,000\) baris dengan asumsinya \(1,000\) baris tersebut mampu mewakili
dataset. (Saat melakukan random sampling seperti ini bisa juga
menggunakan teknik lain. Akan tetapi, dengan waktu yang terbatas, untuk
sementara digunakan sampling yang sederhana menggunakan
slice_sample())
spotify_sample_eda <- spotify_factored |>
slice_sample(n = 1000)
spotify_sample_eda |> head()Setelah melakukan sampling, data bisa divisualisasikan untuk melihat seperti apa data yang diperoleh dan hubungannya antar variabel.
Salah satu visualisasi yang bisa digunakan untuk kolom
num_compare_columns yaitu boxplot. Berikut boxplot untuk
kolom yang berupak karakteristik musik.
spotify_sample_eda |> select(all_of(num_compare_columns)) |>
stack() |>
ggplot(aes(x = ind, y = values)) +
geom_boxplot() +
labs(
title = "Boxplot Karakteristik",
x = "Karakteristik",
y = "Nilai"
) +
theme_bw() +
coord_flip()Boxplot Karakteristik Musik
Dari boxplot sederhana diatas, bisa menduga seperti apa distribusi
untuk setiap kolom numerik pada range \([0,
1]\). Dilihat dari instrumentalness, bisa diduga
bahwa dataset yang diperoleh ini cenderung sebuah lagu (musik yang
diiringi vokal). Tapi bisa dilihat juga memiliki beberapa data yang
berpotensi outlier yang dapat diartikan sebagai di dataset
terdapat beberapa track yang memiliki musik instrumental.
Interpretasi tersebut bisa juga diterapkan pada kolom
speechiness yang mengartikan 0 sebagai bukan spoken
track, sedangkan 1 menunjukkan track tersebut berupa
spoken track seperti talk show atau podcast1.
Correlogram merupakan visualisasi untuk melihat korelasi antar kolom. Korelasi ini bisa mengidentifikasi apakah terjadi multicollinearity atau melihat hubungan korelasi positif/negatif terhadap setiap variabel yang ada di dataset.
Matriks korelasi dibangkitkan menggunakan ggcorr().
ggcorr(spotify_sample_eda |> select(all_of(numeric_columns)),
method = "pairwise", geom = "tile",
label = TRUE, label_size = 3, label_alpha = TRUE,
hjust = 0.9, layout.exp = 2,
nbreaks = 7,
low = "#2980B9", mid = "#ECF0F1", high = "#2C3E50") +
labs(title = "Matriks Korelasi")Matriks Korelasi Kolom Numerik
Dari hasil diatas, yang paling terlihat jelas hubungannya adalah
energy dan loudness yang berkorelasi positif.
Terdapat juga yang berkorelasi negatif seperti energy dan
acousticness.
Karakteristik musik didefinisikan sebelumnya sebagai kolom yang
termasuk num_compare_columns (yang memiliki nilai pada
range \([0, 1]\)). Dari grafik
sebelumnya, bisa juga divisualisasikan lebih menarik lagi dengan
ggpairs(). Berikut hasilnya pada kolom karakteristik
musik.
ggpairs(spotify_sample_eda |> select(all_of(num_compare_columns)),
title = "Correlogram Antar Karakteristik Musik")Hubungan antara karakteristik musik
Nilai korelasi dari grafik diatas sama saja dengan grafik sebelumnya yang membedakan hanya pembulatannya saja.
Berikut korelasi untuk kolom numerik yang selain dari karakteristik musik.
ggpairs(spotify_sample_eda |> select(all_of(numeric_columns)) |> select(-all_of(num_compare_columns)),
title = "Correlogram Antar Deskripsi Musik")Korelasi Deskripsi Musik
Setelah selesai mengeksplorasi untuk data numerik, dilanjutkan untuk data kategori.
Pada bagian ini divisualisasikan dan dieksplorasi mengenai kolom yang
berupa kategori. Berikut kolom yang berupa kategori (dalam variabel
factor_columns).
factor_columns## [1] "genre" "key" "mode" "time_signature"
Karena dari summary() sebelumnya diketahui bahwa
genre memiliki kategori yang banyak. Sebagai memperjelas,
disimpan genre musik tersebut sebagai variabel
genre_music kemudian dicek jumlah genre yang
tersedia di dataset.
genre_music <- spotify_factored$genre |> levels()
length(genre_music)## [1] 27
Terdapat \(27\) kategori musik.
Untuk data tipe kategori juga bisa divisualisasikan dengan melihat
jumlahnya setiap observasi, baik dari kolom tunggal maupun lebih. Jumlah
penghitungan disimpan dalam objek spotify_factored_count
yang dibuat menggunakan count(). Berikut sampel 10 baris
dari kategorinya.
spotify_factored_count <- spotify_factored |> select(all_of(factor_columns)) |>
count(genre, key, mode, time_signature)
spotify_factored_count |> slice_sample(n = 10)Selain itu, visualisasinya bisa berupa barplot dengan berbagai konfigurasi. Berikut beberapa konfigurasi visualisasi untuk kolom kategori.
Visualisasi ini untuk melihat seberapa banyak musik yang
dikelompokkan berdasarkan genre.
spotify_factored_count |> group_by(genre) |> summarise(freq = sum(n)) |>
ggplot(aes(genre, freq)) +
geom_col(aes(fill = freq), show.legend = FALSE) + theme_bw() + coord_flip() +
labs(title = "Banyaknya musik untuk setiap genre", y = 'Frekuensi')Jumlah musik untuk setiap genre
Dari grafik diatas diketahui bahwa musik berjenis Children’s Music dan A Capella cukup sedikit dibandingkan jenis lainnya. Dari informasi ini, bisa dilakukan tindakan untuk mengabaikan jenis musik tersebut, tapi untuk sementara jenis musik tersebut akan dilibatkan dalam proses analisis ataupun pemodelan.
Berikut jumlah musik yang digunakan untuk setiap kunci
key.
spotify_factored_count |> group_by(key) |> summarise(freq = sum(n)) |>
ggplot(aes(key, freq)) +
geom_col(aes(fill = freq), show.legend = FALSE) + theme_bw() +
labs(title = "Banyaknya musik untuk setiap key", y = "Frekuensi")Jumlah musik untuk setiap key
Dari gambar diatas, diketahui kunci D# jarang digunakan. Dan yang paling populer digunakan adalah kunci C.
Visualisasi ini berdasarkan mode.
spotify_factored_count |> group_by(mode) |> summarise(freq = sum(n)) |>
ggplot(aes(mode, freq)) +
geom_col(aes(fill = freq), show.legend = FALSE) + theme_bw() +
labs(title = "Banyaknya musik untuk setiap mode", y = "Frekuensi")Jumlah musik untuk setiap mode
Kebanyakan musik menggunakan mayor.
Visualisasi ini berdasarkan tanda birama.
spotify_factored_count |> group_by(time_signature) |> summarise(freq = sum(n)) |>
ggplot(aes(time_signature, freq)) +
geom_col(aes(fill = freq), show.legend = FALSE) + theme_bw() +
labs(title = "Banyaknya musik untuk setiap tanda birama", y = "Frekuensi")Jumlah musik untuk setiap time_signature
Dari sini juga diperoleh tanda birama \(\frac{4}{4}\) lebih banyak digunakan.
Visualisasi ini meringkas semua tipe kategori berdasarkan
genre. genre yang digunakan sebagai contoh
adalah Pop dan Classical. Berikut visualisasi untuk
genre Pop.
# genre_i <- 7
genre_picked <- "Pop"
spotify_factored_count |>
filter(genre == genre_picked) |>
ggplot(aes(x = key, y = n, fill = mode)) +
geom_bar(position = "dodge", stat = "identity") +
facet_wrap(~time_signature, ncol = 3) +
labs(
title = paste("Musik", genre_picked),
x = "Key",
y = "Frekuensi"
) +
theme_bw()Distribusi di musik Pop
Banyak yang dapat diceritakan terkait grafik diatas. Untuk mempersingkat, dapat diketahui bahwa musik Pop sering menggunakan tanda birama \(\frac{4}{4}\), dengan penggunaan mode Major tertinggi di kunci C.
Berikut visualisasi yang serupa tapi pada genre
Classical.
genre_picked <- "Classical"
spotify_factored_count |>
filter(genre == genre_picked) |>
ggplot(aes(x = key, y = n, fill = mode)) +
geom_bar(position = "dodge", stat = "identity") +
facet_wrap(~time_signature, ncol = 2) +
labs(
title = paste("Musik", genre_picked),
x = "Key",
y = "Frekuensi"
) +
theme_bw()Distribusi di musik Classical
Berbeda dengan pop, untuk musik Classical tidak didominasi oleh tanda birama \(\frac{4}{4}\). Dan pada tanda birama lain dapat terlihat meski jumlahnya tidak sebanyak \(\frac{4}{4}\). Dan di mode Major, ada tiga kunci yang populer yaitu C, D, G.
Dari eksplorasi diatas, dapat beberapa informasi mengenai dataset. Hanya saja, fokus untuk LBB ini yaitu pemodelan menggunakan PCA dan K-means. Sehingga, sebagai catatan saja, eksplorasi dataset masih bisa di jelajah lebih lanjut sebelum menyentuh pemodelan.
Pada bab ini akan ditentukan pemilihan parameter untuk PCA dan
K-means. Dataset yang telah diolah dan dieksplorasi sebelumnya disimpan
sebagai objek baru bernama spotify_r2. Dan berikut
datasetnya secara sekilas.
spotify_r2 <- spotify_factored
spotify_r2 |> glimpse()## Rows: 232,725
## Columns: 18
## $ genre <fct> Movie, Movie, Movie, Movie, Movie, Movie, Movie, Movi…
## $ 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.00000000, 0.00000000, 0.00000000, 0.00000000, 0.123…
## $ key <fct> C#, F#, C, C#, F, C#, C#, F#, C, G, E, C, F#, D#, 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 <fct> Major, Minor, Minor, Major, Major, Major, Major, Majo…
## $ 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 <fct> 4/4, 4/4, 5/4, 4/4, 4/4, 4/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…
Sebelumnya sudah dibahas mana saja kolom yang berupa numerik dan kategori. Karena PCA dan K-means fokus pada yang bersifat numerik. Maka data yang digunakan nanti sebagai input adalah kolom numerik. Sekedar mengingat saja, berikut variabel kolom-kolom yang menunjukkan jenis kolomnya.
info_columns <- c("artist_name", "track_name", "track_id")
# info_columns
# factor_columns
# numeric_columns
# num_compare_columns
sq <- seq(length(numeric_columns))
data.frame(info_columns[sq], factor_columns[sq], numeric_columns[sq], num_compare_columns[sq])Tahap ini dilakukan pertama untuk melihat karakteristik musik dan
yang lainnya bisa diekspresikan sebagai Principal Component.
Berikut dataset spotify_r2 secara sekilas.
glimpse(spotify_r2)## Rows: 232,725
## Columns: 18
## $ genre <fct> Movie, Movie, Movie, Movie, Movie, Movie, Movie, Movi…
## $ 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.00000000, 0.00000000, 0.00000000, 0.00000000, 0.123…
## $ key <fct> C#, F#, C, C#, F, C#, C#, F#, C, G, E, C, F#, D#, 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 <fct> Major, Minor, Minor, Major, Major, Major, Major, Majo…
## $ 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 <fct> 4/4, 4/4, 5/4, 4/4, 4/4, 4/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…
Karena untuk PCA hanya menggunakan kolom yang berupa angka, dipilih
kolom yang numerik saja. Dan disimpan sebagai objek
spotify_pca.
spotify_pca <- spotify_r2 |> select(all_of(numeric_columns))
spotify_pca |> 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…
Dilakukan normalisasi menggunakan scale().
spotify_pca_scaled <- spotify_pca |> scale() |> as.data.frame()
spotify_pca_scaled |> glimpse()## Rows: 232,725
## Columns: 11
## $ popularity <dbl> -2.261002, -2.206026, -2.096075, -2.261002, -2.041100…
## $ acousticness <dbl> 0.68337483, -0.34546644, 1.64456626, 0.94269920, 1.63…
## $ danceability <dbl> -0.8909329, 0.1919933, 0.5852948, -1.6936990, -1.2034…
## $ duration_ms <dbl> -1.14136546, -0.82186566, -0.54529654, -0.69529329, -…
## $ energy <dbl> 1.2869052, 0.6302479, -1.6699502, -0.9297874, -1.3131…
## $ instrumentalness <dbl> -0.48981747, -0.48981747, -0.48981747, -0.48981747, -…
## $ liveness <dbl> 0.66065975, -0.32283477, -0.56492573, -0.58762176, -0…
## $ loudness <dbl> 1.2907007, 0.6686811, -0.7184009, -0.4348159, -1.9305…
## $ speechiness <dbl> -0.3679692, -0.1830817, -0.4558311, -0.4380431, -0.40…
## $ tempo <dbl> 1.59560388, 1.82324947, -0.58832454, 1.75059318, 0.74…
## $ valence <dbl> 1.38074126, 1.38843163, -0.33421142, -0.87638256, -0.…
Setelah selesai memilih kolom mana saja yang akan digunakan,
dilakukan PCA menggunakan PCA(). Berikut ringkasan dari
PCA.
pca_spotify <- PCA(spotify_pca_scaled,
scale.unit=FALSE, graph = FALSE, ncp = length(numeric_columns))
pca_spotify |> summary()##
## Call:
## PCA(X = spotify_pca_scaled, scale.unit = FALSE, ncp = length(numeric_columns),
## graph = FALSE)
##
##
## 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 |
Setelah melakukan PCA, baru dievaluasi setiap komponen yang ada di PCA.
Untuk melihat kontribusi setiap kolom/variabel terhadap Principal
Component, dapat diketahui langsung dengan mengambil nilai
contrib dari pca_spotify$var.
pca_var_contrib <- pca_spotify$var$contrib |> as.data.frame()
pca_var_contribSebelum memasuki secara visual, bisa juga dilihat menggunakan tabel. Setelah menyimpan nilai contrib dari PCA, bisa dilihat kontribusinya dengan mengurutkannya untuk komponen yang dipilih. Berikut contoh melihat kontribusi terbesar pada PC1.
pca_var_contrib |>
arrange(desc(Dim.1))Diketahui bahwa loudness, energy,
acousticness merupakan tiga variabel yang paling
berpengaruh pada PC1. Dari sini juga bisa dilihat nilai eigen
dari PCA dengan get_eig().
pca_eig <- get_eig(pca_spotify) |> as.data.frame()
pca_eigJika diinginkan untuk mempertahankan informasi dari dataset aslinya sebesar \(80\%\), bisa diambil berdasarkan nilai cumulative variance. Berikut dimensi yang setidaknya memiliki cumulative variance 80%.
pca_eig |>
mutate(cumul = round(cumulative.variance.percent,-1)) |>
filter(cumul < 90) |>
select(-cumul)Disini 6 komponen bisa mewakili hampir 80% informasi yang tersedia
dari kolom numerik. Eksplorasi diatas juga bisa dipermudah dengan
dibuatkan grafik menggunakan fviz_screeplot().
fviz_screeplot(pca_spotify, addlabels = TRUE) + theme_bw()Screeplot PCA
Dari scree plot diatas sama saja dengan pernyataan sebelumnya menggunakan tabel. Berikut visualisasi pengaruh tiap variabel/kolom yang ada di dataset terhadap komponen PC1 dan PC2.
plot.PCA(pca_spotify, choix = "var")Grafik PCA variabel terhadap 2 PC
Berdasarkan pernyataan sebelumnya, pada PC1 tiga terbesar
kontribusinya yaitu loudness, energy,
acousticness. Tapi dengan melihat grafik diatas, bisa
dilihat korelasi antara komponen tersebut negatif ataupun positif. Untuk
melihat besarnya korelasi bisa diakses di $var$corr
pca_spotify$var$cor |> as.data.frame()Terdapat fungsi bernama fviz_contrib() untuk melihat
kontribusinya, seperti scree plot diatas. Berikut untuk
komponen pertama PC1 axes = 1.
fviz_contrib(pca_spotify, choice = "var", axes = 1,
ggtheme = theme_bw())Grafik kontribusi setiap variabel di PC1
Dari grafik diatas diketahui yang bisa dibilang cukup mempengaruhi
yaitu \(6\) variabel yaitu
loudness, energy, acousticness,
danceability, valence, dan
instrumentalness. Akan tetapi yang terlihat jelas dan
dominan adalah tiga pertama. Berikut untuk PC2
axes = 2.
fviz_contrib(pca_spotify, choice = "var", axes = 2, top = 10,
ggtheme = theme_bw())Grafik kontribusi setiap variabel di PC2
Berbeda dengan PC1, disini yang dianggap signifikan hanya
speechiness dan liveness.
Evaluasi ini bisa dilanjutkan hingga menemukan seluruh PC yang
mewakili signifikannya terhadap semua variabel. Dan tergantung kebutuhan
juga, penggunaan PC untuk analisis/pemodelan berikutnya bisa ditentukan
tergantung tujuan analisis. Misal, saya tidak perlu PC yang variabel
speechiness dan liveness yang signifikan.
Mungkin saja, diinginkan popularity yang signifikan. Bisa
dieksplorasi dengan melihat tabel contrib tadi.
pca_var_contrib |>
rownames_to_column(var = 'indicator') |>
filter(indicator %in% "popularity") |>
column_to_rownames("indicator") |> t() |> as.data.frame() |>
arrange(desc(popularity)) |> head()Dari informasi diatas diketahui bahwa PC5, PC7, dan PC4 merupakan
komponen dimana popularity memiliki pengaruh yang besar
dengan PC5 yang terbesar. Berikut dievaluasi kontribusi di PC5.
fviz_contrib(pca_spotify, choice = "var", axes = 5, top = 10,
ggtheme = theme_bw())Grafik kontribusi setiap variabel di PC5
Disini diperoleh bahwa popularity di PC5 lebih tinggi
daripada PC yang lain, tapi tidak lebih signifikan daripada
duration_ms dan valance. Dari sini, bisa
diduga bahwa variabel popularity tidak begitu berpengaruh
di dataset kita. Selain popularity muncul tertingginya
mulai dari di PC5 (yang informasinya sudah tergolong kecil),
popularity juga bukan yang mendominasi di PC tersebut
(PC5).
Dari sini akan dilanjutkan dengan informasi PC1 dan PC2 untuk Kmeans.
Dengan dataset yang sama, diinginkan untuk dilakukan Kmeans dan
membandingkannya antara dengan pengelompokkan musik dengan klusternya.
Oleh karena itu, informasi dan data yang telah dirangkum oleh PCA akan
digunakan di bagian K-Means. Yang perlu dicatat adalah pada bagian ini
hanya fokus menggunakan PC1 dan PC2. Informasi koordinat dari PCA untuk
PC1 dan PC2 ditambahkan ke dataset dan disimpan kedalam objek
spotify_km. Berikut beberapa baris pertamanya (PC1 dan PC2
disimpan di kolom terakhir):
spotify_km <- spotify_r2
pca_spotify_ind <- get_pca_ind(pca_spotify)
spotify_km$PC1 <- pca_spotify_ind$coord[,"Dim.1"]
spotify_km$PC2 <- pca_spotify_ind$coord[,"Dim.2"]
spotify_km |> head()Karena untuk K-Means ini dieksekusi dan diplotkan setiap individual
observasinya. Sehingga untuk menampikan lebih dari \(200,000\) baris itu sangat memberatkan dan
menyulitkan untuk dibaca. Oleh karena itu dilakukan random
sampling seperti pada bagian awal.
Hanya saja pengambilannya berdasarkan genre dan diambil
sebanyak \(100\) data untuk setiap
genre. Pengambilan sebanyak \(100\) dikarenakan pada genre
A Capella tidak memiliki observasi lebih dari \(100\). Berikut 6 baris data yang diambil
secara acak setelah menyimpan hasil sampling di objek
spotify_km_sample:
set.seed(41908481)
spotify_km_sample <- spotify_km |>
group_by(genre) |>
slice_sample(n = 100) |> # sample ini bisa diperbesar jika mengabaikan A Capella
ungroup()
spotify_km_sample |> slice_sample(n = 6)Dan berikut dimensi untuk sample.
dim(spotify_km_sample)## [1] 2700 20
Karena objek spotify_km_sample menyimpan seluruh kolom
seperti original dataset, harus dibuat objek baru lagi yang hanya
melihat dua variabel (atau lebih) yang akan dievaluasi menggunakan
K-means. Karena fokus di LBB ini hanya dua variabel/komponen yaitu PC1
dan PC2. Objek input yaitu spotify_km_sample_input dengan 6
baris sampel sebagai berikut:
spotify_km_sample_input <- spotify_km_sample |> select(PC1, PC2)
spotify_km_sample_input |> slice_sample(n = 10)Berikut visualisasinya dataset yang digunakan untuk input K-Means.
spotify_km_sample_input |> ggplot(aes(PC1, PC2)) +
geom_point() +
theme_bw()Grafik PC1 vs PC2 untuk analisis K-Means
Dalam mencari nilai K Optimum, bisa dibantu dengan fungsi
fviz_nbclust(). Berikut mencari optimum berdasarkan WSS
(Weight Sum of Square).
fviz_nbclust(spotify_km_sample_input, kmeans, method = "wss")Mencari nilai K optimal dengan WSS
Dari grafik diatas, menggunakan elbow method, bisa diduga
jumlah kluster yang tepat itu 3. Untuk meastikan juga bisa menggunakan
method silhouette di fviz_nbclust.
fviz_nbclust(spotify_km_sample_input, kmeans, method = "silhouette")Mencari nilai K optimal dengan Silhouette
Dari dua grafik diatas disimpulkan menggunakan \(K = 3\) sebagai jumlah kluster yang digunakan pada pemodelan K-Means.
K-Means dilakukan menggunakan fungsi kmeans() yang
hasilnya disimpan pada objek km_spotify. Berikut hasil dari
km_spotify.
set.seed(41608481)
km_spotify <- kmeans(spotify_km_sample_input, 3)
km_spotify## K-means clustering with 3 clusters of sizes 688, 130, 1882
##
## Cluster means:
## PC1 PC2
## 1 -2.8301908 -0.2355595
## 2 -0.4910752 4.3917534
## 3 0.9703514 -0.2237285
##
## Clustering vector:
## [1] 1 3 1 1 1 1 1 1 3 1 3 1 1 3 1 3 3 1 1 1 3 1 1 1 3 1 1 1 1 1 1 1 3 3 1 1 1
## [38] 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 3 1 1 1 1 1 3 1 1 1 1 1 1 1 3 1 1 3 1 1 1
## [75] 1 1 1 3 3 1 1 1 1 1 3 1 1 1 1 1 3 3 1 3 3 3 1 1 1 1 3 3 3 3 3 3 3 3 3 3 3
## [112] 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [149] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [186] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 1 3 3 1 3 3 3 1 3 3 3 3 3 3 3 3 3 1 1 1
## [223] 3 1 3 3 3 3 3 3 3 3 1 3 1 3 1 3 1 3 1 3 3 3 3 1 1 3 3 3 3 1 3 3 1 3 3 1 3
## [260] 3 3 1 3 3 3 3 1 1 1 3 3 3 1 3 3 3 3 3 3 1 3 1 3 3 3 1 1 3 3 3 3 3 1 3 3 3
## [297] 3 3 3 3 3 3 3 3 1 3 3 1 3 2 3 2 3 3 3 3 3 3 3 2 3 3 3 1 3 3 3 3 3 3 3 3 3
## [334] 3 3 3 3 3 3 3 3 1 3 1 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3
## [371] 3 2 3 3 3 2 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 2 3 3 3 3 1 3 1
## [408] 1 3 2 3 3 3 3 1 3 3 1 3 3 3 3 3 3 1 1 3 3 3 3 1 2 3 3 1 3 1 3 3 1 1 3 3 1
## [445] 3 1 2 3 3 3 1 1 3 1 1 3 2 3 3 3 1 2 1 1 3 1 3 3 3 1 1 3 1 3 3 3 1 3 3 1 3
## [482] 1 3 3 1 1 3 3 2 3 3 1 2 3 3 1 1 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [519] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [556] 3 3 3 3 3 1 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [593] 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [630] 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 3 1 1 1 1 1 1
## [667] 1 1 3 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 3 1 2 2 2
## [704] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2
## [741] 2 3 2 2 2 2 2 2 2 2 2 2 2 2 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2
## [778] 2 2 2 2 2 2 2 2 2 2 2 2 3 2 2 2 2 2 3 2 2 2 1 3 3 3 3 3 1 1 3 3 3 1 3 3 3
## [815] 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3
## [852] 3 3 3 3 3 3 1 3 3 3 3 3 3 1 3 3 3 3 1 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1
## [889] 3 3 3 3 3 1 3 3 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [926] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [963] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [1000] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [1037] 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 1 3 3 3 3 3 1 3 3 3 1 3 3
## [1074] 3 1 3 3 3 3 3 3 3 1 1 1 3 3 3 3 3 3 3 3 3 3 1 3 3 2 3 3 1 3 3 1 1 3 3 1 1
## [1111] 3 3 3 1 1 3 3 3 1 1 1 2 3 3 1 3 3 1 3 3 1 3 1 3 3 3 1 3 3 3 3 3 3 3 3 1 3
## [1148] 1 3 1 3 3 3 1 3 3 3 1 3 3 3 1 1 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 1 3 3 3 3 1
## [1185] 3 3 1 3 3 3 3 3 3 1 3 1 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [1222] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [1259] 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [1296] 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 1 1 3 3 3 3 3 1 1 3 3 1 3 3 3 3 3 3 3 3 3
## [1333] 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 1 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 1
## [1370] 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 1 3 3 3 3 1 3 1 3 3 1 3 3 3 1 3 3 3 3 1
## [1407] 3 3 3 1 3 3 1 3 3 3 3 3 3 3 3 1 3 1 1 3 3 1 3 3 3 1 3 3 1 1 3 1 1 1 1 3 1
## [1444] 3 3 3 1 3 3 1 3 1 1 1 3 3 1 3 3 3 3 1 1 3 3 1 3 3 1 3 1 1 3 3 3 3 1 3 1 3
## [1481] 3 3 3 1 3 3 3 1 1 3 3 3 1 3 1 3 1 3 1 3 1 1 3 1 1 1 1 1 3 1 2 1 1 3 1 1 3
## [1518] 1 3 3 1 1 1 1 2 1 3 3 1 2 1 3 3 2 2 1 1 3 3 3 2 3 2 1 1 1 1 1 1 1 1 3 2 2
## [1555] 1 1 3 1 1 1 2 2 1 3 1 1 3 1 1 1 1 1 3 1 3 1 1 1 3 1 1 3 3 1 2 1 2 1 1 1 1
## [1592] 1 1 1 1 1 3 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1629] 1 1 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1666] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3
## [1703] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [1740] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3
## [1777] 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3
## [1814] 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 1 3 3 3 1 1 3 1 3 3 3 3
## [1851] 3 3 1 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 1 1 3 3 3 3 3
## [1888] 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [1925] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 3 3 3 3 3 3 3 3 3 3 3 3 3
## [1962] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [1999] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [2036] 3 3 3 3 3 3 3 3 3 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [2073] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 3 3 3 3 3 3 3 3 3 3 3
## [2110] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 3 3 3 3 3 3 3 3
## [2147] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [2184] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 1 1 3 3
## [2221] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 1 3 3 3 3 3 3 3 3 3
## [2258] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [2295] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [2332] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2
## [2369] 3 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1
## [2406] 3 3 1 3 3 3 3 1 1 3 3 3 1 3 1 3 3 3 3 3 1 3 1 3 3 3 3 1 3 1 3 1 3 3 1 3 3
## [2443] 3 1 3 3 3 3 3 3 3 1 3 1 3 3 3 1 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3
## [2480] 3 1 3 3 3 3 3 3 1 3 1 3 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2517] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2554] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2591] 1 1 1 1 1 1 1 1 1 1 3 1 3 2 3 3 1 3 3 3 3 1 1 1 3 3 1 3 1 1 3 1 3 1 1 3 1
## [2628] 3 1 3 1 1 1 3 1 1 3 3 3 3 3 3 1 3 1 3 3 3 3 1 3 3 3 3 1 3 3 1 3 3 3 1 1 1
## [2665] 3 1 3 3 3 3 3 3 3 3 1 3 3 1 3 3 3 3 3 3 3 3 3 1 3 1 3 3 3 3 3 1 1 3 3 3
##
## Within cluster sum of squares by cluster:
## [1] 1568.3207 320.3051 2266.4622
## (between_SS / total_SS = 70.5 %)
##
## Available components:
##
## [1] "cluster" "centers" "totss" "withinss" "tot.withinss"
## [6] "betweenss" "size" "iter" "ifault"
Hasil $cluster diatas digabungkan di
spotify_km_sample untuk visualisasi dan memperoleh
insight yang bisa ditemukan. Berikut baris awal dari
spotify_km_sample dengan kolom cluster.
spotify_km_sample$cluster <- km_spotify$cluster
spotify_km_sample |> head()Dari grafik antara PC1 vs PC2 juga bisa divisualisasikan dengan
menampilkan hasil cluster dengan bantuan fungsi
fviz_cluster().
fviz_cluster(km_spotify, data = spotify_km_sample_input,
repel = TRUE, ellipse = TRUE, ellipse.type = "norm", ellipse.alpha = 0.1) +
theme_bw()Kluster PC1 vs PC2
Karena K-Means yang dilakukan hanya dua variabel, rasanya sudah cukup melakukan visualiasasi diatas. Selanjutnya, memperoleh insight dari hasil analisis melalui grafik/visualisasi ataupun tabel/angka.
Pada bab ini, dari analisis maupun pemodelan yang dilakukan sebelumnya ingin diperoleh insight atau informasi yang tidak dapat diperoleh sebelumnya melalui eksplorasi dataset.
genreDisimpan objek spotify_km_sample ke nama baru yaitu
spotify_r3_sample. Berikut beberapa baris pertamanya:
spotify_r3_sample <- spotify_km_sample
spotify_r3_sample |> head()Setelah memperoleh informasi cluster sebanyak tiga, ingin diketahui
jika cluster tersebut dikelompokkan berdasarkan genre.
Berikut frekuensi untuk setiap genre dan
cluster.
spotify_r3_genre <- table(spotify_r3_sample |> select(genre, cluster)) |> as.data.frame() |>
arrange(genre, cluster)
spotify_r3_genreUntuk memudahkan memperoleh gambaran besarnya, dilakukan visualisasi sebagai berikut:
spotify_r3_genre |>
ggplot(aes(x = genre, y = Freq, fill = cluster)) +
geom_col() +
labs(
title = "Klustering untuk setiap genre",
y = "Frekuensi"
) +
theme_bw() +
coord_flip()Proporsi cluster untuk setiap genre
Dari grafik diatas bisa dilihat lebih jelas mengenai pembagian cluster untuk setiap genre. Berikut informasi yang bisa diperoleh dari grafik diatas:
genre Comedy.genre Soundtrack,
Opera, Classical, dan Acapella.Berikut jika dalam bentuk dataframe:
spotify_r3_genre |>
group_by(genre) |>
mutate(prop = Freq / sum(Freq)) |>
slice_max(Freq == max(Freq)) |>
ungroup() |>
arrange(cluster)Dari informasi diatas bisa berhipotesis bahwa bagi yang menyukai
genre tertentu, kemungkinan menyukai genre
yang lain dalam satu kluster. Tapi pernyataan itu cukup meragukan
karena, pada cluster 3, kategorinya melingkupi hampir musik pada
umumnya. Sehingga, belum tentu orang yang menyukai genre di cluster 3
menyukai genre lainnya meskipun di satu cluster.
Hal ini bisa diteliti lebih jauh jika melakukan pemecahan / breakdown terhadap komponen-komponen lainnya. Misalkan, dibandingkan melakukan K-Means menggunakan PCA, bisa juga langsung menggunakan variabel aslinya. Atau bisa dilihat karakteristik tiap PC yang digunakan di K-Means, untuk diekstrak lagi informasi terkait variabel mana yang signifikan di komponen tersebut.
keySekarang dilakukan hal yang serupa tetapi pada kolom
key.
spotify_r3_key <- table(spotify_r3_sample |> select(key, cluster)) |> as.data.frame() |>
arrange(key, cluster)
spotify_r3_keyDalam bentuk visualisasinya:
spotify_r3_key |>
ggplot(aes(x = key, y = Freq, fill = cluster)) +
geom_col() +
labs(
title = "Klustering untuk setiap key",
y = "Frekuensi"
) +
theme_bw()Proporsi cluster untuk setiap genre
Untuk key sendiri, tidak dapat informasi menarik yang bisa diperoleh.
Karena, tidak bisa melihat proporsi yang konsisten antar
key. Mengingat dilakukannya random sampling yang
mengacu pada genre, informasi diatas sulit untuk diambil
kesimpulannya. Sehingga, ide untuk eksplorasi selanjutnya bisa mengacu
pada key.
Informasi diatas bisa digunakan untuk berbagai hal semisal:
Classical, tidak ada salahnya
merekomendasikan dengan musik Opera atau
Soundtrack.Comedy tidak dapat ditawarkan
berdasarkan kemiripan karakteristik musiknya karena pada cluster
tersebut hanya Comedy saja yang dominan.Berikut yang bisa dilakukan selanjutnya:
Terdapat informasi lebih detail di referensi API-nya.↩︎