EKSPLORASI DAN REDUKSI DATA PADA DATASET SPOTIFY

Menggunakan Feature Engineering, Feature Selection,

dan Principal Component Analysis (PCA)


Tugas ini disusun untuk memenuhi tugas mata kuliah
Eksplorasi dan Visualisasi Data



Dosen Pengampu:
Agung Satrio Wicaksono, S.Mat., M.Si.





Disusun oleh:

Nazwa Khoirina 3338240005
Tadzkiah Fayrooz Ridwan 3338240029
Delvira Damayanti 3338240042




PROGRAM STUDI STATISTIKA
FAKULTAS TEKNIK
UNIVERSITAS SULTAN AGENG TIRTAYASA
2026

Dataset Spotify

library(readr)
library(tidyverse)

# =====================================================
# 1. IMPORT DATA
# =====================================================

spotify <- read_csv("spotify tracks dataset.csv")

# =====================================================
# 2. MENGAMBIL 1000 DATA SECARA ACAK
# =====================================================

set.seed(123)
spotify <- spotify[sample(nrow(spotify), 1000), ]

Dalam analisis ini digunakan sebanyak 1000 observasi yang dipilih secara acak dari total 114.000 observasi pada dataset Spotify Tracks. Pemilihan 1000 observasi dilakukan agar proses pengolahan data menjadi lebih efisien, namun tetap mampu merepresentasikan karakteristik data secara keseluruhan untuk keperluan analisis multivariat dan Principal Component Analysis (PCA).

# =====================================================
# 3. MELIHAT DATA AWAL
# =====================================================

head(spotify)
## # A tibble: 6 × 22
##     ...1 `Unnamed: 0` track_id          artists album_name track_name popularity
##    <dbl>        <dbl> <chr>             <chr>   <chr>      <chr>           <dbl>
## 1  51662        51662 78PDj0uocOvtSJCd… Yo Yo … Bhaag Joh… Aankhon A…         59
## 2  57869        57869 1hP0hDL5VREpThmM… Shakti… Ailesa     Ailesa             42
## 3   2985         2985 5EL8He4J2hrTWl9r… Babasó… Desde Ade… El Maestr…         44
## 4  29924        29924 46VgwcEKnjPCcSEs… Contro… Sable Val… No Chill           39
## 5  95245        95245 0WjreSYeix5YZRbw… LA IND… The Great… Nunca Voy…         34
## 6 103064       103064 07U6dNp0xM9htc4A… Omar A… All That … Archetype           4
## # ℹ 15 more variables: duration_ms <dbl>, explicit <lgl>, danceability <dbl>,
## #   energy <dbl>, key <dbl>, loudness <dbl>, mode <dbl>, speechiness <dbl>,
## #   acousticness <dbl>, instrumentalness <dbl>, liveness <dbl>, valence <dbl>,
## #   tempo <dbl>, time_signature <dbl>, track_genre <chr>
library(DT)
datatable(head(spotify))
str(spotify)
## tibble [1,000 × 22] (S3: tbl_df/tbl/data.frame)
##  $ ...1            : num [1:1000] 51662 57869 2985 29924 95245 ...
##  $ Unnamed: 0      : num [1:1000] 51662 57869 2985 29924 95245 ...
##  $ track_id        : chr [1:1000] "78PDj0uocOvtSJCdOt65qy" "1hP0hDL5VREpThmMOa1pta" "5EL8He4J2hrTWl9rxbE3DY" "46VgwcEKnjPCcSEsJ5fWY2" ...
##  $ artists         : chr [1:1000] "Yo Yo Honey Singh" "Shakti Sivamani" "Babasónicos" "Control Freak" ...
##  $ album_name      : chr [1:1000] "Bhaag Johnny" "Ailesa" "Desde Adentro - Impuesto de Fe (En Vivo)" "Sable Valley Summer Vol. 2" ...
##  $ track_name      : chr [1:1000] "Aankhon Aankhon" "Ailesa" "El Maestro - En Vivo" "No Chill" ...
##  $ popularity      : num [1:1000] 59 42 44 39 34 4 0 55 35 57 ...
##  $ duration_ms     : num [1:1000] 244831 250601 170866 178285 299613 ...
##  $ explicit        : logi [1:1000] FALSE FALSE FALSE FALSE FALSE FALSE ...
##  $ danceability    : num [1:1000] 0.746 0.628 0.65 0.593 0.546 0.676 0.787 0.603 0.49 0.791 ...
##  $ energy          : num [1:1000] 0.988 0.79 0.915 0.937 0.628 0.338 0.872 0.525 0.0614 0.808 ...
##  $ key             : num [1:1000] 10 5 4 1 6 9 9 2 0 7 ...
##  $ loudness        : num [1:1000] -4.57 -7.16 -5.76 -6.7 -10.29 ...
##  $ mode            : num [1:1000] 0 0 0 0 1 1 1 1 1 1 ...
##  $ speechiness     : num [1:1000] 0.0637 0.0655 0.0368 0.0566 0.0441 0.0284 0.143 0.0412 0.0397 0.0334 ...
##  $ acousticness    : num [1:1000] 0.29 0.27 0.446 0.000397 0.625 0.372 0.0067 0.135 0.979 0.347 ...
##  $ instrumentalness: num [1:1000] 2.18e-03 6.83e-04 0.00 6.28e-01 1.10e-02 5.57e-06 1.93e-04 0.00 8.48e-01 2.14e-02 ...
##  $ liveness        : num [1:1000] 0.131 0.606 0.91 0.302 0.0801 0.468 0.136 0.0714 0.111 0.0877 ...
##  $ valence         : num [1:1000] 0.61 0.47 0.885 0.0333 0.708 0.321 0.696 0.273 0.232 0.745 ...
##  $ tempo           : num [1:1000] 130 133 112 140 174 ...
##  $ time_signature  : num [1:1000] 4 4 4 4 4 4 4 4 4 4 ...
##  $ track_genre     : chr [1:1000] "hip-hop" "indie" "alt-rock" "dubstep" ...
dim(spotify)
## [1] 1000   22
colnames(spotify)
##  [1] "...1"             "Unnamed: 0"       "track_id"         "artists"         
##  [5] "album_name"       "track_name"       "popularity"       "duration_ms"     
##  [9] "explicit"         "danceability"     "energy"           "key"             
## [13] "loudness"         "mode"             "speechiness"      "acousticness"    
## [17] "instrumentalness" "liveness"         "valence"          "tempo"           
## [21] "time_signature"   "track_genre"
summary(spotify)
##       ...1          Unnamed: 0       track_id           artists         
##  Min.   :    30   Min.   :    30   Length:1000        Length:1000       
##  1st Qu.: 28046   1st Qu.: 28046   Class :character   Class :character  
##  Median : 56187   Median : 56187   Mode  :character   Mode  :character  
##  Mean   : 56077   Mean   : 56077                                        
##  3rd Qu.: 83659   3rd Qu.: 83659                                        
##  Max.   :113841   Max.   :113841                                        
##   album_name         track_name          popularity     duration_ms    
##  Length:1000        Length:1000        Min.   : 0.00   Min.   : 39775  
##  Class :character   Class :character   1st Qu.:17.00   1st Qu.:173465  
##  Mode  :character   Mode  :character   Median :35.00   Median :214433  
##                                        Mean   :33.72   Mean   :228868  
##                                        3rd Qu.:51.00   3rd Qu.:264231  
##                                        Max.   :93.00   Max.   :716079  
##   explicit        danceability        energy             key        
##  Mode :logical   Min.   :0.0000   Min.   :0.00189   Min.   : 0.000  
##  FALSE:911       1st Qu.:0.4540   1st Qu.:0.46775   1st Qu.: 2.000  
##  TRUE :89        Median :0.5780   Median :0.68400   Median : 5.000  
##                  Mean   :0.5647   Mean   :0.64169   Mean   : 5.211  
##                  3rd Qu.:0.6930   3rd Qu.:0.85800   3rd Qu.: 8.000  
##                  Max.   :0.9450   Max.   :0.99900   Max.   :11.000  
##     loudness            mode        speechiness       acousticness    
##  Min.   :-41.739   Min.   :0.000   Min.   :0.00000   Min.   :0.00000  
##  1st Qu.: -9.896   1st Qu.:0.000   1st Qu.:0.03530   1st Qu.:0.02368  
##  Median : -6.756   Median :1.000   Median :0.04875   Median :0.17000  
##  Mean   : -8.093   Mean   :0.648   Mean   :0.08537   Mean   :0.32469  
##  3rd Qu.: -4.911   3rd Qu.:1.000   3rd Qu.:0.08123   3rd Qu.:0.61675  
##  Max.   : -0.250   Max.   :1.000   Max.   :0.94800   Max.   :0.99500  
##  instrumentalness       liveness         valence           tempo       
##  Min.   :0.0000000   Min.   :0.0212   Min.   :0.0000   Min.   :  0.00  
##  1st Qu.:0.0000000   1st Qu.:0.0974   1st Qu.:0.2552   1st Qu.: 99.49  
##  Median :0.0000174   Median :0.1345   Median :0.4585   Median :122.01  
##  Mean   :0.1462549   Mean   :0.2194   Mean   :0.4743   Mean   :121.26  
##  3rd Qu.:0.0269250   3rd Qu.:0.2850   3rd Qu.:0.6870   3rd Qu.:139.68  
##  Max.   :0.9990000   Max.   :0.9910   Max.   :0.9830   Max.   :208.28  
##  time_signature  track_genre       
##  Min.   :0.000   Length:1000       
##  1st Qu.:4.000   Class :character  
##  Median :4.000   Mode  :character  
##  Mean   :3.909                     
##  3rd Qu.:4.000                     
##  Max.   :5.000
# =====================================================
# 4. MEMILIH VARIABEL NUMERIK
# =====================================================

spotify_num <- spotify[, c("popularity",
                           "duration_ms",
                           "danceability",
                           "energy",
                           "loudness",
                           "speechiness",
                           "acousticness",
                           "instrumentalness",
                           "liveness",
                           "valence",
                           "tempo")]

head(spotify_num)
## # A tibble: 6 × 11
##   popularity duration_ms danceability energy loudness speechiness acousticness
##        <dbl>       <dbl>        <dbl>  <dbl>    <dbl>       <dbl>        <dbl>
## 1         59      244831        0.746  0.988    -4.57      0.0637     0.29    
## 2         42      250601        0.628  0.79     -7.16      0.0655     0.27    
## 3         44      170866        0.65   0.915    -5.76      0.0368     0.446   
## 4         39      178285        0.593  0.937    -6.70      0.0566     0.000397
## 5         34      299613        0.546  0.628   -10.3       0.0441     0.625   
## 6          4      168828        0.676  0.338    -7.96      0.0284     0.372   
## # ℹ 4 more variables: instrumentalness <dbl>, liveness <dbl>, valence <dbl>,
## #   tempo <dbl>
datatable(head(spotify_num))
str(spotify_num)
## tibble [1,000 × 11] (S3: tbl_df/tbl/data.frame)
##  $ popularity      : num [1:1000] 59 42 44 39 34 4 0 55 35 57 ...
##  $ duration_ms     : num [1:1000] 244831 250601 170866 178285 299613 ...
##  $ danceability    : num [1:1000] 0.746 0.628 0.65 0.593 0.546 0.676 0.787 0.603 0.49 0.791 ...
##  $ energy          : num [1:1000] 0.988 0.79 0.915 0.937 0.628 0.338 0.872 0.525 0.0614 0.808 ...
##  $ loudness        : num [1:1000] -4.57 -7.16 -5.76 -6.7 -10.29 ...
##  $ speechiness     : num [1:1000] 0.0637 0.0655 0.0368 0.0566 0.0441 0.0284 0.143 0.0412 0.0397 0.0334 ...
##  $ acousticness    : num [1:1000] 0.29 0.27 0.446 0.000397 0.625 0.372 0.0067 0.135 0.979 0.347 ...
##  $ instrumentalness: num [1:1000] 2.18e-03 6.83e-04 0.00 6.28e-01 1.10e-02 5.57e-06 1.93e-04 0.00 8.48e-01 2.14e-02 ...
##  $ liveness        : num [1:1000] 0.131 0.606 0.91 0.302 0.0801 0.468 0.136 0.0714 0.111 0.0877 ...
##  $ valence         : num [1:1000] 0.61 0.47 0.885 0.0333 0.708 0.321 0.696 0.273 0.232 0.745 ...
##  $ tempo           : num [1:1000] 130 133 112 140 174 ...
summary(spotify_num)
##    popularity     duration_ms      danceability        energy       
##  Min.   : 0.00   Min.   : 39775   Min.   :0.0000   Min.   :0.00189  
##  1st Qu.:17.00   1st Qu.:173465   1st Qu.:0.4540   1st Qu.:0.46775  
##  Median :35.00   Median :214433   Median :0.5780   Median :0.68400  
##  Mean   :33.72   Mean   :228868   Mean   :0.5647   Mean   :0.64169  
##  3rd Qu.:51.00   3rd Qu.:264231   3rd Qu.:0.6930   3rd Qu.:0.85800  
##  Max.   :93.00   Max.   :716079   Max.   :0.9450   Max.   :0.99900  
##     loudness        speechiness       acousticness     instrumentalness   
##  Min.   :-41.739   Min.   :0.00000   Min.   :0.00000   Min.   :0.0000000  
##  1st Qu.: -9.896   1st Qu.:0.03530   1st Qu.:0.02368   1st Qu.:0.0000000  
##  Median : -6.756   Median :0.04875   Median :0.17000   Median :0.0000174  
##  Mean   : -8.093   Mean   :0.08537   Mean   :0.32469   Mean   :0.1462549  
##  3rd Qu.: -4.911   3rd Qu.:0.08123   3rd Qu.:0.61675   3rd Qu.:0.0269250  
##  Max.   : -0.250   Max.   :0.94800   Max.   :0.99500   Max.   :0.9990000  
##     liveness         valence           tempo       
##  Min.   :0.0212   Min.   :0.0000   Min.   :  0.00  
##  1st Qu.:0.0974   1st Qu.:0.2552   1st Qu.: 99.49  
##  Median :0.1345   Median :0.4585   Median :122.01  
##  Mean   :0.2194   Mean   :0.4743   Mean   :121.26  
##  3rd Qu.:0.2850   3rd Qu.:0.6870   3rd Qu.:139.68  
##  Max.   :0.9910   Max.   :0.9830   Max.   :208.28

1. Deskripsi Dataset

Sumber Data

Dataset yang digunakan adalah Spotify Tracks Dataset yang diperoleh dari Kaggle. Dataset ini berisi data lagu dari platform Spotify yang mencakup berbagai genre musik. Setiap baris pada dataset merepresentasikan satu lagu, sedangkan setiap kolom berisi informasi mengenai karakteristik lagu tersebut.

Dataset ini memiliki berbagai variabel numerik, seperti popularity, duration_ms, danceability, energy, loudness, speechiness, acousticness, instrumentalness, liveness, valence, dan tempo. Variabel-variabel tersebut digunakan untuk menggambarkan karakteristik audio dari setiap lagu. Selain itu, dataset ini memiliki jumlah observasi yang besar dan banyak variabel numerik yang saling berhubungan sehingga sesuai digunakan untuk analisis multivariat, khususnya Feature Engineering, Feature Selection, dan Principal Component Analysis (PCA).

Sumber dataset: Spotify Tracks Dataset

Jumlah Observasi

nrow(spotify)
## [1] 1000

Berdasarkan hasil pengolahan data, dataset yang digunakan terdiri atas 1000 observasi. Observasi tersebut dipilih secara acak dari dataset asli yang berjumlah 114.000 observasi.

Jumlah Variabel

ncol(spotify)
## [1] 22

Berdasarkan hasil pengolahan data menggunakan fungsi ncol(), diperoleh bahwa dataset yang digunakan memiliki 22 variabel, yang terdiri atas informasi identitas lagu dan berbagai karakteristik audio.

Tujuan Analisis

Analisis ini bertujuan untuk mengidentifikasi karakteristik audio lagu pada dataset Spotify berdasarkan berbagai variabel numerik yang digunakan, yaitu popularity, duration_ms, danceability, energy, loudness, speechiness, acousticness, instrumentalness, liveness, valence, dan tempo. Selain itu, dilakukan feature engineering untuk menghasilkan fitur baru, feature selection untuk memilih variabel yang relevan, serta Principal Component Analysis (PCA) untuk mereduksi dimensi data dan memperoleh informasi utama yang terkandung dalam dataset.

2. Exploratory Data Analysis (EDA)

library(ggplot2)
library(dplyr)
library(corrplot)
library(car)
library(reshape2)
library(gridExtra)

Statistik Deskriptif

summary(spotify_num)
##    popularity     duration_ms      danceability        energy       
##  Min.   : 0.00   Min.   : 39775   Min.   :0.0000   Min.   :0.00189  
##  1st Qu.:17.00   1st Qu.:173465   1st Qu.:0.4540   1st Qu.:0.46775  
##  Median :35.00   Median :214433   Median :0.5780   Median :0.68400  
##  Mean   :33.72   Mean   :228868   Mean   :0.5647   Mean   :0.64169  
##  3rd Qu.:51.00   3rd Qu.:264231   3rd Qu.:0.6930   3rd Qu.:0.85800  
##  Max.   :93.00   Max.   :716079   Max.   :0.9450   Max.   :0.99900  
##     loudness        speechiness       acousticness     instrumentalness   
##  Min.   :-41.739   Min.   :0.00000   Min.   :0.00000   Min.   :0.0000000  
##  1st Qu.: -9.896   1st Qu.:0.03530   1st Qu.:0.02368   1st Qu.:0.0000000  
##  Median : -6.756   Median :0.04875   Median :0.17000   Median :0.0000174  
##  Mean   : -8.093   Mean   :0.08537   Mean   :0.32469   Mean   :0.1462549  
##  3rd Qu.: -4.911   3rd Qu.:0.08123   3rd Qu.:0.61675   3rd Qu.:0.0269250  
##  Max.   : -0.250   Max.   :0.94800   Max.   :0.99500   Max.   :0.9990000  
##     liveness         valence           tempo       
##  Min.   :0.0212   Min.   :0.0000   Min.   :  0.00  
##  1st Qu.:0.0974   1st Qu.:0.2552   1st Qu.: 99.49  
##  Median :0.1345   Median :0.4585   Median :122.01  
##  Mean   :0.2194   Mean   :0.4743   Mean   :121.26  
##  3rd Qu.:0.2850   3rd Qu.:0.6870   3rd Qu.:139.68  
##  Max.   :0.9910   Max.   :0.9830   Max.   :208.28

Berdasarkan statistik deskriptif di atas, dapat diperoleh gambaran awal mengenai distribusi setiap variabel numerik. Variabel popularity memiliki rentang nilai yang cukup lebar (0–93), sehingga menunjukkan bahwa terdapat lagu yang sangat tidak populer hingga sangat populer.

Variabel duration_ms menunjukkan variasi durasi lagu dalam milidetik dengan rata-rata sekitar 228.868 ms atau sekitar 3,8 menit. Variabel loudness bernilai negatif karena diukur dalam satuan desibel (dB) relatif terhadap 0 dB (nilai semakin mendekati 0 berarti semakin keras).

Variabel instrumentalness dan speechiness cenderung memiliki nilai rendah pada sebagian besar observasi, yang menunjukkan bahwa sebagian besar lagu mengandung vokal dan bukan sepenuhnya instrumental. Selain itu, rata-rata nilai danceability dan energy yang cukup tinggi menunjukkan bahwa mayoritas lagu pada dataset memiliki karakter yang cukup enerjik dan mudah untuk diikuti iramanya.

Visualisasi Distribusi Variabel

par(mfrow = c(3, 4))
for (var in colnames(spotify_num)) {
  hist(spotify_num[[var]],
       main = paste("Histogram:", var),
       xlab = var,
       col = "#1DB954",
       border = "white")
}
par(mfrow = c(1, 1))

Berdasarkan visualisasi histogram di atas, beberapa variabel menunjukkan distribusi yang tidak simetris (skewed). Variabel instrumentalness dan speechiness cenderung memiliki distribusi right-skewed, karena sebagian besar lagu memiliki nilai yang sangat rendah pada kedua variabel tersebut.

Variabel popularity dan liveness juga cenderung right-skewed, yang menunjukkan bahwa sebagian besar lagu memiliki nilai rendah hingga sedang. Sebaliknya, variabel energy menunjukkan distribusi left-skewed, karena sebagian besar lagu memiliki nilai energi yang tinggi.

Variabel danceability terlihat lebih mendekati distribusi normal dibanding variabel lainnya, sementara tempo menunjukkan distribusi yang cenderung unimodal dengan puncak di sekitar 120–130 BPM dan sedikit condong ke kanan.

Deteksi Outlier Visual (Boxplot)

spotify_long <- melt(spotify_num)

ggplot(spotify_long, aes(x = variable, y = value, fill = variable)) +
  geom_boxplot(outlier.colour = "red", outlier.size = 1.5, alpha = 0.7) +
  facet_wrap(~ variable, scales = "free", ncol = 4) +
  labs(title = "Boxplot Variabel Numerik Spotify",
       x = "", y = "Nilai") +
  theme_minimal(base_size = 11) +
  theme(legend.position = "none",
        plot.title = element_text(face = "bold", hjust = 0.5))

Boxplot di atas memperlihatkan sebaran nilai serta keberadaan outlier (titik merah) pada setiap variabel. Terlihat bahwa variabel duration_ms, instrumentalness, speechiness, dan liveness memiliki cukup banyak outlier pada nilai yang tinggi. Hal ini konsisten dengan histogram sebelumnya yang menunjukkan bahwa variabel-variabel tersebut cenderung memiliki distribusi right-skewed.

Variabel loudness juga memiliki cukup banyak outlier pada nilai yang rendah, yang menunjukkan adanya beberapa lagu dengan tingkat volume yang jauh lebih kecil dibanding mayoritas data. Sementara itu, variabel tempo memiliki outlier pada kedua sisi distribusi, baik pada nilai yang sangat rendah maupun sangat tinggi. Keberadaan outlier ini perlu diperhatikan karena dapat memengaruhi hasil analisis atau pemodelan selanjutnya.

Missing Value

# Cek jumlah missing value per variabel
missing_val <- colSums(is.na(spotify_num))
missing_val
##       popularity      duration_ms     danceability           energy 
##                0                0                0                0 
##         loudness      speechiness     acousticness instrumentalness 
##                0                0                0                0 
##         liveness          valence            tempo 
##                0                0                0
# Persentase missing value
missing_pct <- round(colMeans(is.na(spotify_num)) * 100, 2)
missing_pct
##       popularity      duration_ms     danceability           energy 
##                0                0                0                0 
##         loudness      speechiness     acousticness instrumentalness 
##                0                0                0                0 
##         liveness          valence            tempo 
##                0                0                0

Berdasarkan hasil pengecekan missing value menggunakan fungsi is.na(), diperoleh bahwa seluruh variabel numerik tidak memiliki nilai yang hilang (missing value = 0 untuk semua variabel). Hal ini menunjukkan bahwa data sudah bersih dan tidak memerlukan proses imputasi sebelum analisis lebih lanjut dilakukan.

Deteksi Outlier (Metode IQR)

# Fungsi hitung outlier dengan metode IQR
hitung_outlier <- function(x) {
  Q1 <- quantile(x, 0.25, na.rm = TRUE)
  Q3 <- quantile(x, 0.75, na.rm = TRUE)
  IQR_val <- Q3 - Q1
  lower <- Q1 - 1.5 * IQR_val
  upper <- Q3 + 1.5 * IQR_val
  sum(x < lower | x > upper, na.rm = TRUE)
}

outlier_count <- sapply(spotify_num, hitung_outlier)
outlier_df <- data.frame(
  Variabel = names(outlier_count),
  Jumlah_Outlier = outlier_count
)
outlier_df
##                          Variabel Jumlah_Outlier
## popularity             popularity              0
## duration_ms           duration_ms             50
## danceability         danceability              4
## energy                     energy              0
## loudness                 loudness             49
## speechiness           speechiness            115
## acousticness         acousticness              0
## instrumentalness instrumentalness            232
## liveness                 liveness             78
## valence                   valence              0
## tempo                       tempo              5

Deteksi outlier dilakukan menggunakan metode Interquartile Range (IQR), di mana sebuah observasi dianggap outlier apabila nilainya berada di luar batas bawah (Q1 − 1.5 × IQR) atau batas atas (Q3 + 1.5 × IQR).Berdasarkan hasil di atas, variabel yang memiliki jumlah outlier terbanyak adalah instrumentalness (232), speechiness (115), liveness (78), loudness (49), dan duration_ms (50). Keberadaan outlier pada variabel-variabel tersebut perlu dipertimbangkan dalam tahap feature selection dan pemodelan, meskipun dalam konteks data audio Spotify, nilai-nilai ekstrem tersebut bisa jadi merupakan karakteristik lagu tertentu yang memang berbeda dari umumnya (misalnya lagu podcast atau lagu instrumental murni).

Analisis Korelasi

cor_matrix <- cor(spotify_num, use = "complete.obs")

corrplot(cor_matrix,
         method = "color",
         type = "upper",
         tl.cex = 0.85,
         addCoef.col = "black",
         number.cex = 0.65,
         col = colorRampPalette(c("#2166AC", "white", "#D6604D"))(200),
         title = "Matriks Korelasi Variabel Numerik Spotify",
         mar = c(0, 0, 2, 0))

Matriks korelasi di atas menunjukkan hubungan linier antarvariabel numerik. Korelasi positif yang cukup kuat terlihat antara variabel energy dan loudness (r = 0.76), yang berarti lagu dengan energi tinggi cenderung memiliki volume lebih keras. Sebaliknya, variabel energy dan acousticness memiliki korelasi negatif yang kuat (r = −0.73), sehingga lagu yang lebih akustik umumnya memiliki tingkat energi yang lebih rendah.

Selain itu, terdapat korelasi negatif sedang antara loudness dan acousticness (r = −0.59), serta korelasi positif sedang antara danceability dan valence (r = 0.47). Hubungan antarvariabel ini dapat menjadi pertimbangan dalam tahap feature selection karena variabel dengan korelasi tinggi berpotensi membawa informasi yang mirip atau redundan.

Identifikasi Multikolinearitas (VIF)

# Regresi dummy untuk menghitung VIF
# Menggunakan popularity sebagai variabel dependen
model_vif <- lm(popularity ~ ., data = spotify_num)
vif_vals <- vif(model_vif)

vif_df <- data.frame(
  Variabel = names(vif_vals),
  VIF = round(vif_vals, 3)
)
vif_df
##                          Variabel   VIF
## duration_ms           duration_ms 1.066
## danceability         danceability 1.482
## energy                     energy 4.146
## loudness                 loudness 3.146
## speechiness           speechiness 1.115
## acousticness         acousticness 2.377
## instrumentalness instrumentalness 1.429
## liveness                 liveness 1.139
## valence                   valence 1.595
## tempo                       tempo 1.085
# Visualisasi VIF
ggplot(vif_df, aes(x = reorder(Variabel, VIF), y = VIF, fill = VIF > 5)) +
  geom_col(width = 0.6) +
  geom_hline(yintercept = 5, linetype = "dashed", color = "red", linewidth = 0.8) +
  geom_hline(yintercept = 10, linetype = "dashed", color = "darkred", linewidth = 0.8) +
  coord_flip() +
  scale_fill_manual(values = c("#1DB954", "#E74C3C"),
                    labels = c("VIF ≤ 5 (Aman)", "VIF > 5 (Perlu Perhatian)")) +
  labs(title = "Variance Inflation Factor (VIF) — Deteksi Multikolinearitas",
       x = "Variabel", y = "Nilai VIF",
       fill = "Status",
       caption = "Garis merah putus-putus: VIF = 5 (moderate) dan VIF = 10 (severe)") +
  theme_minimal(base_size = 11) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5))

Variance Inflation Factor (VIF) digunakan untuk mendeteksi multikolinearitas, yaitu kondisi di mana dua atau lebih variabel prediktor saling berkorelasi tinggi sehingga dapat mengganggu stabilitas model regresi. Secara umum, nilai VIF > 5 menunjukkan adanya multikolinearitas moderat, sedangkan VIF > 10 menunjukkan multikolinearitas yang lebih serius.

Berdasarkan hasil di atas, seluruh variabel memiliki nilai VIF di bawah 5, sehingga tidak terindikasi adanya multikolinearitas yang kuat. Namun, variabel energy (4.146), loudness (3.146), dan acousticness (2.377) menunjukkan nilai VIF yang relatif lebih tinggi dibandingkan variabel lainnya, konsisten dengan temuan pada matriks korelasi. Hal ini sejalan dengan matriks korelasi sebelumnya yang menunjukkan adanya hubungan cukup kuat antarvariabel tersebut. Oleh karena itu, variabel-variabel tersebut tetap dapat dipertimbangkan pada tahap feature selection untuk mengurangi kemungkinan adanya informasi yang saling tumpang tindih.

3. Feature Engineering

Feature Engineering merupakan tahap dalam proses analisis data yang bertujuan untuk membentuk fitur-fitur baru dari variabel yang sudah tersedia agar dapat merepresentasikan informasi dalam data secara lebih informatif dan bermakna. Pada tahap ini, dilakukan transformasi serta kombinasi variabel-variabel yang ada untuk menghasilkan fitur tambahan yang diharapkan mampu meningkatkan kualitas analisis dan performa model.

Fitur Baru

# 3. FEATURE ENGINEERING

# Fitur 1: Mood Score
mood_score <- (spotify_num$valence + spotify_num$energy) / 2

# Fitur 2: Acoustic Energy Ratio
acoustic_energy_ratio <- spotify_num$acousticness / (spotify_num$energy + 0.001)

# Fitur 3: Dance Popularity Score
dance_popularity <- spotify_num$danceability * spotify_num$popularity

# Fitur 4: Energy Intensity
energy_intensity <- spotify_num$energy * abs(spotify_num$loudness)

# Masukkan ke dataset
spotify_num$mood_score <- mood_score
spotify_num$acoustic_energy_ratio <- acoustic_energy_ratio
spotify_num$dance_popularity <- dance_popularity
spotify_num$energy_intensity <- energy_intensity


# Mengecek hasil feature baru

head(spotify_num)
## # A tibble: 6 × 15
##   popularity duration_ms danceability energy loudness speechiness acousticness
##        <dbl>       <dbl>        <dbl>  <dbl>    <dbl>       <dbl>        <dbl>
## 1         59      244831        0.746  0.988    -4.57      0.0637     0.29    
## 2         42      250601        0.628  0.79     -7.16      0.0655     0.27    
## 3         44      170866        0.65   0.915    -5.76      0.0368     0.446   
## 4         39      178285        0.593  0.937    -6.70      0.0566     0.000397
## 5         34      299613        0.546  0.628   -10.3       0.0441     0.625   
## 6          4      168828        0.676  0.338    -7.96      0.0284     0.372   
## # ℹ 8 more variables: instrumentalness <dbl>, liveness <dbl>, valence <dbl>,
## #   tempo <dbl>, mood_score <dbl>, acoustic_energy_ratio <dbl>,
## #   dance_popularity <dbl>, energy_intensity <dbl>
summary(spotify_num[, c("mood_score",
                         "acoustic_energy_ratio",
                         "dance_popularity",
                         "energy_intensity")])
##    mood_score      acoustic_energy_ratio dance_popularity energy_intensity  
##  Min.   :0.00095   Min.   :  0.0000      Min.   : 0.000   Min.   : 0.04763  
##  1st Qu.:0.41550   1st Qu.:  0.0298      1st Qu.: 7.081   1st Qu.: 3.13219  
##  Median :0.57550   Median :  0.2522      Median :17.188   Median : 4.01230  
##  Mean   :0.55801   Mean   :  1.9689      Mean   :19.162   Mean   : 4.21422  
##  3rd Qu.:0.71662   3rd Qu.:  1.1382      3rd Qu.:29.009   3rd Qu.: 5.10010  
##  Max.   :0.96100   Max.   :339.7924      Max.   :81.052   Max.   :12.69496

Alasan Pembuatan Fitur dan Interpretasi Fitur

Fitur 1: Mood Score

Alasan Pembuatan Fitur

Fitur mood_score dibuat untuk menggambarkan suasana atau karakter emosional suatu lagu dengan menggabungkan variabel energy dan valence. Variabel energy menunjukkan tingkat intensitas dan aktivitas lagu, sedangkan valence menggambarkan tingkat kepositifan atau keceriaan lagu. Penggabungan kedua variabel ini diharapkan dapat memberikan representasi yang lebih sederhana mengenai suasana keseluruhan lagu.

Interpretasi Fitur

Nilai mood_score yang tinggi menunjukkan lagu cenderung memiliki energi dan suasana yang positif atau ceria. Sebaliknya, nilai yang rendah menunjukkan lagu cenderung memiliki energi rendah dan suasana yang lebih tenang atau melankolis. Berdasarkan hasil ringkasan statistik, fitur ini memiliki nilai rata-rata sebesar 0,558 dengan rentang nilai antara 0,00095 hingga 0,961, yang menunjukkan adanya variasi karakter suasana antar lagu dalam dataset.

Fitur 2: Acoustic Energy Ratio

Alasan Pembuatan Fitur

Fitur acoustic_energy_ratio dibuat untuk melihat hubungan antara tingkat keakustikan lagu (acousticness) dan tingkat energinya (energy). Fitur ini dapat membantu membedakan lagu yang dominan akustik namun memiliki energi rendah maupun lagu yang lebih energik dengan unsur akustik yang kecil.

Interpretasi Fitur

Nilai acoustic_energy_ratio yang tinggi menunjukkan bahwa karakter akustik lagu lebih dominan dibandingkan tingkat energinya. Sebaliknya, nilai yang rendah menunjukkan lagu memiliki energi yang relatif lebih tinggi dibandingkan unsur akustiknya. Berdasarkan hasil yang diperoleh, fitur ini memiliki rata-rata sebesar 1,969 dengan nilai maksimum mencapai 339,792, yang menunjukkan adanya beberapa lagu dengan tingkat akustik yang sangat tinggi dan energi yang sangat rendah.

Fitur 3: Dance Popularity Score

Alasan Pembuatan Fitur

Fitur dance_popularity dibuat dengan menggabungkan variabel danceability dan popularity. Tujuannya adalah untuk mengidentifikasi lagu yang tidak hanya mudah digunakan untuk menari, tetapi juga memiliki tingkat popularitas yang tinggi. Fitur ini dapat memberikan informasi tambahan mengenai potensi daya tarik suatu lagu di kalangan pendengar.

Interpretasi Fitur

Nilai dance_popularity yang tinggi menunjukkan lagu memiliki tingkat kemudahan untuk menari yang tinggi serta populer di kalangan pengguna Spotify. Sebaliknya, nilai yang rendah menunjukkan lagu kurang populer, kurang danceable, atau keduanya. Hasil ringkasan statistik menunjukkan nilai rata-rata sebesar 19,162 dengan nilai maksimum sebesar 81,052, yang mengindikasikan adanya beberapa lagu yang sangat populer dan memiliki karakteristik yang mendukung aktivitas menari.

Fitur 4: Energy Intensity

Alasan Pembuatan Fitur

Fitur energy_intensity dibuat untuk menggambarkan intensitas keseluruhan lagu melalui kombinasi antara variabel energy dan loudness. Variabel energy merepresentasikan tingkat aktivitas lagu, sedangkan loudness menunjukkan tingkat kekuatan suara. Penggabungan kedua variabel tersebut diharapkan dapat memberikan gambaran yang lebih jelas mengenai tingkat intensitas audio pada setiap lagu.

Interpretasi Fitur

Nilai energy_intensity yang tinggi menunjukkan lagu memiliki tingkat energi dan kekuatan suara yang tinggi, sehingga cenderung terdengar lebih dinamis dan kuat. Sebaliknya, nilai yang rendah menunjukkan lagu dengan energi dan intensitas suara yang lebih rendah. Berdasarkan hasil ringkasan statistik, fitur ini memiliki rata-rata sebesar 4,214 dengan rentang nilai antara 0,048 hingga 12,695, yang menunjukkan adanya variasi intensitas lagu dalam dataset.

4. Feature Selection

Feature selection adalah proses memilih sebagian variabel yang paling relevan dari keseluruhan variabel yang tersedia, tanpa membentuk variabel baru. Tujuannya untuk mengurangi kompleksitas model, menghindari overfitting, serta mengurangi potensi multikolinearitas. Pada tahap ini digunakan dua metode feature selection, yaitu Filter Method berbasis korelasi dan Wrapper Method menggunakan Stepwise Regression.

Metode 1: Filter Method (Korelasi)

Filter Method merupakan pendekatan feature selection yang memilih variabel berdasarkan ukuran statistik, tanpa melibatkan proses pembentukan model. Pada metode ini, variabel dipilih berdasarkan nilai korelasi absolut terhadap variabel target, yaitu popularity. Variabel dengan korelasi absolut ≥ 0.1 dianggap memiliki hubungan yang cukup relevan dengan popularity sehingga dipertahankan dalam analisis, sedangkan variabel dengan korelasi yang sangat rendah tidak digunakan lebih lanjut.

library(ggplot2)
library(dplyr)

# Menghitung korelasi semua variabel terhadap popularity
cor_with_popularity <- cor(spotify_num, use = "complete.obs")[, "popularity"]
cor_df <- data.frame(
  Variabel = names(cor_with_popularity),
  Korelasi = round(cor_with_popularity, 4)
) %>%
  filter(Variabel != "popularity") %>%
  arrange(desc(abs(Korelasi)))

cor_df
##                                    Variabel Korelasi
## dance_popularity           dance_popularity   0.9015
## speechiness                     speechiness  -0.0903
## energy_intensity           energy_intensity  -0.0770
## valence                             valence  -0.0683
## instrumentalness           instrumentalness  -0.0657
## liveness                           liveness  -0.0612
## mood_score                       mood_score  -0.0565
## acoustic_energy_ratio acoustic_energy_ratio   0.0541
## danceability                   danceability   0.0306
## energy                               energy  -0.0215
## loudness                           loudness   0.0148
## duration_ms                     duration_ms   0.0069
## tempo                                 tempo  -0.0006
## acousticness                   acousticness  -0.0003
# Visualisasi korelasi terhadap popularity
ggplot(cor_df, aes(x = reorder(Variabel, abs(Korelasi)),
                   y = Korelasi,
                   fill = Korelasi > 0)) +
  geom_col(width = 0.6) +
  geom_hline(yintercept = 0.1, linetype = "dashed", color = "darkgreen", linewidth = 0.8) +
  geom_hline(yintercept = -0.1, linetype = "dashed", color = "darkgreen", linewidth = 0.8) +
  coord_flip() +
  scale_fill_manual(values = c("#E74C3C", "#1DB954"),
                    labels = c("Korelasi Negatif", "Korelasi Positif")) +
  labs(title = "Korelasi Variabel terhadap Popularity (Filter Method)",
       x = "Variabel", y = "Nilai Korelasi",
       fill = "Arah Korelasi",
       caption = "Garis hijau putus-putus: batas |korelasi| = 0.1") +
  theme_minimal(base_size = 11) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5))

# Variabel terpilih (|korelasi| >= 0.1)
selected_filter <- cor_df %>%
  filter(abs(Korelasi) >= 0.1) %>%
  pull(Variabel)

cat("Variabel terpilih (Filter Method):\n")
## Variabel terpilih (Filter Method):
print(selected_filter)
## [1] "dance_popularity"
# Variabel tereliminasi
eliminated_filter <- cor_df %>%
  filter(abs(Korelasi) < 0.1) %>%
  pull(Variabel)

cat("\nVariabel tereliminasi (Filter Method):\n")
## 
## Variabel tereliminasi (Filter Method):
print(eliminated_filter)
##  [1] "speechiness"           "energy_intensity"      "valence"              
##  [4] "instrumentalness"      "liveness"              "mood_score"           
##  [7] "acoustic_energy_ratio" "danceability"          "energy"               
## [10] "loudness"              "duration_ms"           "tempo"                
## [13] "acousticness"

Berdasarkan hasil Filter Method, variabel yang terpilih adalah variabel dengan nilai korelasi absolut terhadap popularity lebih dari atau sama dengan 0.1. Variabel dance_popularity memiliki korelasi tertinggi terhadap popularity, yang wajar karena variabel ini memang dibentuk dari kombinasi danceability dan popularity.

Seluruh variabel selain dance_popularity memiliki nilai korelasi absolut di bawah 0,1, sehingga dieliminasi karena hubungannya terhadap popularity dianggap kurang signifikan.

Metode 2: Wrapper Method (Stepwise Regression)

Wrapper Method merupakan pendekatan feature selection yang menggunakan performa model untuk menentukan variabel terbaik. Pada metode ini digunakan Stepwise Regression dengan tiga pendekatan: Forward Selection, Backward Elimination, dan Bidirectional. Evaluasi pemilihan variabel dilakukan berdasarkan kriteria AIC (Akaike Information Criterion), di mana model dengan nilai AIC lebih kecil dianggap lebih baik.

# Dataset dengan semua variabel (termasuk fitur baru dari feature engineering)
spotify_full <- spotify_num

# Model lengkap dan model null
full_model  <- lm(popularity ~ ., data = spotify_full)
null_model  <- lm(popularity ~ 1, data = spotify_full)

# Forward Selection
forward_model <- step(null_model,
                      scope = list(lower = null_model, upper = full_model),
                      direction = "forward",
                      trace = 0)

# Backward Elimination
backward_model <- step(full_model,
                       direction = "backward",
                       trace = 0)

# Stepwise Bidirectional
stepwise_model <- step(null_model,
                       scope = list(lower = null_model, upper = full_model),
                       direction = "both",
                       trace = 0)


# variabel terpilih tiap metode
cat("Forward Selection:\n");     print(formula(forward_model))
## Forward Selection:
## popularity ~ dance_popularity + danceability + acoustic_energy_ratio + 
##     duration_ms + instrumentalness + speechiness + acousticness
cat("\nBackward Elimination:\n");  print(formula(backward_model))
## 
## Backward Elimination:
## popularity ~ duration_ms + danceability + speechiness + acousticness + 
##     instrumentalness + acoustic_energy_ratio + dance_popularity
cat("\nStepwise Bidirectional:\n"); print(formula(stepwise_model))
## 
## Stepwise Bidirectional:
## popularity ~ dance_popularity + danceability + acoustic_energy_ratio + 
##     duration_ms + instrumentalness + speechiness + acousticness
# Perbandingan AIC
aic_comparison <- AIC(forward_model, backward_model, stepwise_model)
rownames(aic_comparison) <- c("Forward Selection", "Backward Elimination", "Stepwise Bidirectional")
aic_comparison
##                        df      AIC
## Forward Selection       9 6499.314
## Backward Elimination    9 6499.314
## Stepwise Bidirectional  9 6499.314
# Model terbaik (AIC terkecil)
summary(stepwise_model)
## 
## Call:
## lm(formula = popularity ~ dance_popularity + danceability + acoustic_energy_ratio + 
##     duration_ms + instrumentalness + speechiness + acousticness, 
##     data = spotify_full)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -23.9512  -2.4046  -0.0698   2.9526  31.4630 
## 
## Coefficients:
##                         Estimate Std. Error t value Pr(>|t|)    
## (Intercept)            2.961e+01  9.927e-01  29.830  < 2e-16 ***
## dance_popularity       1.560e+00  1.406e-02 111.005  < 2e-16 ***
## danceability          -4.679e+01  1.297e+00 -36.069  < 2e-16 ***
## acoustic_energy_ratio  1.006e-01  1.612e-02   6.240 6.46e-10 ***
## duration_ms            5.600e-06  2.302e-06   2.432   0.0152 *  
## instrumentalness      -1.496e+00  6.800e-01  -2.200   0.0280 *  
## speechiness           -3.375e+00  1.758e+00  -1.920   0.0551 .  
## acousticness          -1.064e+00  6.225e-01  -1.710   0.0876 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 6.207 on 992 degrees of freedom
## Multiple R-squared:  0.9269, Adjusted R-squared:  0.9264 
## F-statistic:  1797 on 7 and 992 DF,  p-value: < 2.2e-16
# Visualisasi koefisien model stepwise terpilih
coef_df <- as.data.frame(summary(stepwise_model)$coefficients)
coef_df$Variabel <- rownames(coef_df)
coef_df <- coef_df %>%
  filter(Variabel != "(Intercept)") %>%
  rename(Estimate = Estimate, p_value = `Pr(>|t|)`) %>%
  mutate(Signifikan = ifelse(p_value < 0.05, "Signifikan (p < 0.05)", "Tidak Signifikan"))

ggplot(coef_df, aes(x = reorder(Variabel, abs(Estimate)),
                    y = Estimate,
                    fill = Signifikan)) +
  geom_col(width = 0.6) +
  coord_flip() +
  scale_fill_manual(values = c("#1DB954", "#B0B0B0")) +
  labs(title = "Koefisien Variabel Terpilih — Stepwise Regression",
       x = "Variabel", y = "Koefisien",
       fill = "Status") +
  theme_minimal(base_size = 11) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5))

Berdasarkan hasil Stepwise Regression dengan pendekatan Forward Selection, Backward Elimination, dan Bidirectional, diperoleh model akhir dengan variabel-variabel yang dipilih berdasarkan kriteria AIC dalam menjelaskan variabel popularity. Ketiga metode menghasilkan variabel terpilih yang relatif sama, yaitu dance_popularity, danceability, acoustic_energy_ratio, duration_ms, instrumentalness, speechiness, dan acousticness.

Perbandingan nilai AIC menunjukkan bahwa ketiga pendekatan menghasilkan nilai AIC yang sama, yaitu 6499,314. Hal ini menunjukkan bahwa ketiga metode memiliki performa model yang setara dalam proses pemilihan variabel.

Berdasarkan hasil model stepwise, variabel dance_popularity memiliki pengaruh positif paling besar terhadap popularity. Sementara itu, variabel instrumentalness, speechiness, dan acousticness memiliki koefisien negatif terhadap popularity. Meskipun speechiness dan acousticness memiliki nilai p yang relatif lebih besar dibanding variabel lainnya, kedua variabel tersebut tetap dipertahankan dalam model karena masih memberikan kontribusi terhadap performa model berdasarkan kriteria AIC.

Perbandingan dan Kesimpulan Feature Selection

# Variabel terpilih dari kedua metode
vars_forward  <- all.vars(formula(forward_model))[-1]
vars_backward <- all.vars(formula(backward_model))[-1]
vars_stepwise <- all.vars(formula(stepwise_model))[-1]

# Variabel terpilih (Filter Method)
print(selected_filter)
## [1] "dance_popularity"
# Variabel terpilih (Forward Selection)
print(vars_forward)
## [1] "dance_popularity"      "danceability"          "acoustic_energy_ratio"
## [4] "duration_ms"           "instrumentalness"      "speechiness"          
## [7] "acousticness"
# Variabel terpilih —(Backward Elimination)
print(vars_backward)
## [1] "duration_ms"           "danceability"          "speechiness"          
## [4] "acousticness"          "instrumentalness"      "acoustic_energy_ratio"
## [7] "dance_popularity"
# Variabel terpilih — Stepwise Bidirectional
print(vars_stepwise)
## [1] "dance_popularity"      "danceability"          "acoustic_energy_ratio"
## [4] "duration_ms"           "instrumentalness"      "speechiness"          
## [7] "acousticness"
# Variabel yang konsisten terpilih di semua metode wrapper
vars_konsisten <- Reduce(intersect, list(vars_forward, vars_backward, vars_stepwise))
cat("\nVariabel konsisten terpilih di semua metode Stepwise:\n")
## 
## Variabel konsisten terpilih di semua metode Stepwise:
print(vars_konsisten)
## [1] "dance_popularity"      "danceability"          "acoustic_energy_ratio"
## [4] "duration_ms"           "instrumentalness"      "speechiness"          
## [7] "acousticness"
# Dataset final untuk PCA (variabel dari filter + stepwise yang konsisten)
vars_final <- union(selected_filter, vars_konsisten)
# Hanya ambil variabel numerik asli (tanpa fitur baru jika ingin PCA murni)
vars_final_numerik <- vars_final[vars_final %in% c("popularity","duration_ms","danceability",
                                                    "energy","loudness","speechiness",
                                                    "acousticness","instrumentalness",
                                                    "liveness","valence","tempo",
                                                    "mood_score","acoustic_energy_ratio",
                                                    "dance_popularity","energy_intensity")]

cat("\nVariabel final untuk tahap selanjutnya (PCA):\n")
## 
## Variabel final untuk tahap selanjutnya (PCA):
print(vars_final_numerik)
## [1] "dance_popularity"      "danceability"          "acoustic_energy_ratio"
## [4] "duration_ms"           "instrumentalness"      "speechiness"          
## [7] "acousticness"
# Simpan dataset final
spotify_selected <- spotify_num[, vars_final_numerik]

Berdasarkan hasil kedua metode feature selection yang digunakan, dapat disimpulkan bahwa terdapat variabel-variabel yang secara konsisten terpilih, baik melalui Filter Method maupun Wrapper Method (Stepwise Regression). Variabel yang terpilih pada Filter Method menunjukkan adanya hubungan statistik yang relevan dengan popularity, sedangkan variabel yang terpilih pada Stepwise Regression merupakan variabel yang secara signifikan berkontribusi dalam model prediktif.

Variabel yang tereliminasi pada kedua metode dianggap tidak memberikan informasi yang cukup relevan terhadap popularity, sehingga tidak diikutkan pada tahap Principal Component Analysis (PCA).

5. Feature Extraction

Ekstraksi fitur adalah teknik yang mengurangi dimensi atau kompleksitas data untuk meningkatkan kinerja dan efisiensi algoritma machine learning (ML). Proses ini memfasilitasi tugas ML dan meningkatkan analisis data dengan menyederhanakan kumpulan data untuk menyertakan hanya variabel atau atribut yang signifikan.

Analisis PCA

Principal Component Analysis (PCA) adalah teknik reduksi dimensi (feature extraction) yang menyederhanakan banyak variabel menjadi beberapa komponen utama.

Tujuan PCA antara lain:

• Mengurangi jumlah variabel

• Mengurangi multikolinearitas

• Mempermudah visualisasi

• Mempertahankan informasi utama data

# 1. Load Library 
library(ggplot2)
library(dplyr)
library(factoextra) 
library(ggcorrplot)
library(tidyr)

# 2. Data Preprocessing 
spotify_num <- spotify[, c("popularity", "duration_ms", "danceability",
                            "energy", "loudness", "speechiness",
                            "acousticness", "instrumentalness",
                            "liveness", "valence", "tempo")]

# Tambahkan kembali fitur hasil feature engineering
spotify_num$mood_score            <- (spotify_num$valence + spotify_num$energy) / 2
spotify_num$acoustic_energy_ratio <- spotify_num$acousticness / (spotify_num$energy + 0.001)
spotify_num$dance_popularity      <- spotify_num$danceability * spotify_num$popularity
spotify_num$energy_intensity      <- spotify_num$energy * abs(spotify_num$loudness)

vars_final_numerik <- c("dance_popularity", "danceability", "acoustic_energy_ratio",
                         "duration_ms", "instrumentalness", "speechiness", "acousticness")

spotify_selected <- spotify_num[, vars_final_numerik]

pca_data <- spotify_selected %>%
  select(-any_of("popularity"))

dim(pca_data)
## [1] 1000    7
# Cek missing value
colSums(is.na(pca_data))
##      dance_popularity          danceability acoustic_energy_ratio 
##                     0                     0                     0 
##           duration_ms      instrumentalness           speechiness 
##                     0                     0                     0 
##          acousticness 
##                     0
# 3. PCA 
pca_result <- prcomp(pca_data, center = TRUE, scale. = TRUE)

# Ringkasan PCA
cat("\n--- Ringkasan PCA ---\n")
## 
## --- Ringkasan PCA ---
summary(pca_result)
## Importance of components:
##                           PC1    PC2    PC3    PC4    PC5    PC6     PC7
## Standard deviation     1.3166 1.0526 1.0259 1.0069 0.9095 0.8398 0.74823
## Proportion of Variance 0.2476 0.1583 0.1504 0.1448 0.1182 0.1007 0.07998
## Cumulative Proportion  0.2476 0.4059 0.5563 0.7011 0.8193 0.9200 1.00000

Analisis Principal Component Analysis (PCA) dilakukan terhadap 7 variabel dari 1000 observasi hasil feature selection. Sebelum PCA dijalankan, seluruh variabel telah distandarisasi (scaling) untuk memastikan tidak ada variabel yang mendominasi. Hasil pengecekan missing value menunjukkan bahwa tidak terdapat nilai yang hilang pada seluruh variabel, sehingga PCA dapat dijalankan langsung. Secara keseluruhan, hasil PCA memperoleh 7 komponen utama yang menjelaskan 100% variansi data.

1. Variansi Tiap Komponen

Variansi tiap komponen dalam PCA ditunjukkan oleh nilai eigenvalue dan proporsi variansi masing-masing PC.

# Eigenvalue & proporsi variansi
eigenvalues <- pca_result$sdev^2
prop_var    <- eigenvalues / sum(eigenvalues)
cumul_var   <- cumsum(prop_var)

variance_df <- data.frame(
  PC         = paste0("PC", seq_along(eigenvalues)),
  Eigenvalue = round(eigenvalues, 4),
  Proporsi   = round(prop_var * 100, 2),
  Kumulatif  = round(cumul_var * 100, 2)
)

print(variance_df)
##    PC Eigenvalue Proporsi Kumulatif
## 1 PC1     1.7333    24.76     24.76
## 2 PC2     1.1081    15.83     40.59
## 3 PC3     1.0525    15.04     55.63
## 4 PC4     1.0138    14.48     70.11
## 5 PC5     0.8272    11.82     81.93
## 6 PC6     0.7052    10.07     92.00
## 7 PC7     0.5598     8.00    100.00

Berdasarkan kriteria Kaiser, komponen yang memiliki nilai eigenvalue ≥ 1, dipertahankan. Hasil tersebut menunjukkan terdapat 4 komponen utama yang lebih besar dari 1, yaitu PC1 hingga PC4 dengan total kumulatif sebesar 70.11%. Hal ini menunjukkan bahwa keempat komponen tersebut mampu merepresentasikan sebagian besar informasi dari 7 variabel asli, sehingga dimensi data berhasi direduksi atau berkurang sebesar 42.86%.

Berdasarkan proporsi variansinya, PC1 merupakan komponen paling informatif dengan nilai proporsi 24.76%, kemudian nilai dibawahnya diikuti oleh PC2 - PC4. Proporsi variansi ini mengindikasikan bahwa setiap komponen menangkap dimensi informasi yang berbeda-beda, sehingga tidak tidak ada satu variabel yang mendominasi dalam data ini. Sementara itu, PC5 hingga PC7 memiliki eigenvalue dibawah 1 sehingga dianggap belum cukup informatif dan komponen ini tidak diikut sertakan untuk analisis selanjutnya.

Plot 1: Scree Plot

fviz_eig(pca_result, addlabels = TRUE, ylim = c(0, 100),
         barfill = "#1DB954", barcolor = "#1DB954") +
  labs(title = "Scree Plot — Variansi Tiap PC")

Hasil Scree-plot tersebut merepresentasikan nilai proporsi variansi yang dihasilkan. Terlihat bahwa dimensi 1 (PC1) merupakan dimensi dengan persentase tertinggi sebesar 24.8%. Sementara itu, dimensi 7 (PC7) memiliki persentase terendah yang berarti komponen tersebut hanya menjelaskan sebagian kecil dari total variansi data dan dianggap tidak cukup informatif. Garis pada scree plot tersebut terlihat mulai melandai setelah PC4, ini menegaskan bahwa penambahan komponen setelah PC4 tidak memberikan kontribusi variansi yang signifikan.

2. Loading Factor

Loading factor menunjukkan besarnya kontribusi setiap variabel terhadap masing-masing PC. Semakin dekat dengan nilai ±1, maka kontribusi dianggap kuat, sedangkan semakin mendekati 0, kontribusi dianggap lemah.

# Loading Factor
loadings_df <- as.data.frame(round(pca_result$rotation, 4))
loadings_df$Variabel <- rownames(loadings_df)
print(loadings_df)
##                           PC1     PC2     PC3     PC4     PC5     PC6     PC7
## dance_popularity      -0.4381  0.2793  0.1892  0.5652 -0.0862 -0.2267  0.5621
## danceability          -0.5744  0.0706  0.2241  0.1839 -0.1832  0.0917 -0.7342
## acoustic_energy_ratio  0.3955  0.2782  0.4243  0.3453  0.1718  0.6608 -0.0270
## duration_ms            0.1563 -0.6490  0.3095  0.3985  0.4084 -0.3389 -0.1347
## instrumentalness       0.3831 -0.2085 -0.1262  0.3702 -0.8060 -0.0473 -0.0703
## speechiness           -0.0287 -0.1473  0.7702 -0.4813 -0.3303 -0.0699  0.1966
## acousticness           0.3869  0.5944  0.1704 -0.0033  0.0632 -0.6176 -0.2873
##                                    Variabel
## dance_popularity           dance_popularity
## danceability                   danceability
## acoustic_energy_ratio acoustic_energy_ratio
## duration_ms                     duration_ms
## instrumentalness           instrumentalness
## speechiness                     speechiness
## acousticness                   acousticness

A. PC1 (Dimensi Danceability vs Acoustic): Berdasarkan hasil PC1 tersebut, danceability (-0.5744) dan dance_popularity (0.4381) memiliki nilai loading negatif yang kuat, sementara itu acoustic_energy_ratio (0.3955), acousticness (0.3869), dan instrumentalness (0.3831) memiliki loading positif yang kuat. Hal ini menunjukkan bahwa PC1 dengan skor rendah merepresentasikan lagu yang bersifat danceable dan populer, sementara di sisi lain lagu dengan skor PC1 tinggi cenderung akustik dan intrumental.

B. PC2 (Dimensi Duration vs Acoustic): PC2 merepresentasikan antara duration_ms (-0.6490) dan acousticness (0.5944), keduanya hampir sama besar tapi berlawanan tanda, sehingga apabila salah satu nilai naik, yang lain cenderung turun. PC2 dengan skor rendah merepresentasikan lagu yang berdurasi panjang dengan karakteristik akustik yang lemah. Sebaliknya, skor PC2 tinggi cenderung memiliki karakteristik akustik yang kuat namun berdurasi pendek.

C. PC3 (Dimensi Speechiness): PC3 didominasi oleh speechiness (0.7702), dengan loading positif yang kuat. Komponen ini lebih dominan pada karakteristik lagu yang mengandung banyak elemen vokal atau lirik. Semakin tinggi skor PC3, maka semakin banyak elemen vokal pada lagu tersebut.

D. PC4 (Dimensi Dance Popularity vs Speechiness): PC4 merepresentasikan antara dance_popularity (0.5652) dan speechiness (-0.4813). Lagu dengan skor PC4 yang tinggi cenderung danceable dan populer namun elemen vokalnya sedikit. Sebaliknya, lagu dengan skor PC4 yang rendah memiliki elemen vokal yang tinggi namun tidak danceable dan populer.

E. PC5 (Dimensi Instrumentalness): PC5 didominasi oleh instrumentalness (-0.8060) dengan loading negatif yang kuat. Komponen ini hampir sepenuhnya menangkap karakteristik instrumental lagu. PC5 cenderung merupakan lagu instrumental murni tanpa vokal. Sementara itu, Variabel lainnya memiliki loading yang mendekati 0 sehingga kontribusinya dapat diabaikan.

F. PC6 (Dimensi Acoustic Energy Ratio vs Acousticness): PC6 didominasi oleh acoustic_energy_ratio (0.6608) dan acousticness (-0.6176). Kedua variabel ini berkaitan dengan akustik namun berlawanan arah. PC6 menunjukkan adanya perbedaan antara rasio energi akustik terhadap tingkat akustik murni suatu lagu.

G. PC7 (Dimensi Danceability): PC7 didominasi oleh danceability (-0.7342) dengan loading negatif kuat. Lagu dengan skor PC7 rendah cenderung memiliki tingkat danceability yang tinggi.

Plot 2: Heatmap

loadings_df %>%
  pivot_longer(-Variabel, names_to = "PC", values_to = "Loading") %>%
  ggplot(aes(x = PC, y = Variabel, fill = Loading)) +
  geom_tile(color = "white") +
  geom_text(aes(label = round(Loading, 2)), size = 3.5) +
  scale_fill_gradient2(low = "#E74C3C", mid = "white", high = "#1DB954", midpoint = 0) +
  labs(title = "Heatmap Loading Factor") +
  theme_minimal()

Heatmap loading factor menampilkan kontribusi setiap variabel terhadap masing-masing komponen utama (PCA). Warna hijau menunjukkan loading positif, warna merah menunjukkan loading negatif, dan warna putih menunjukkan loading mendekati 0 atau tidak berkontribusi secara signifikan terhadap komponen. Semakin pekat warnanya, maka semakin besar kontribusi variabel tersebut.

Secara keseluruhan, heatmap ini menunjukkan dimensi informasi yang berbeda-beda setiap komponennya, dengan tidak ada satu variabel yang mendominasi komponen secara bersamaan. Hal ini mengindikasikan bahwa variabel dalam dataset memiliki karakteristik yang beragam dan saling melengkapi.

3. PCA Keseluruhan

# Jumlah PC optimal (Kaiser: eigenvalue >= 1)
n_pc <- sum(pca_result$sdev^2 >= 1)
cat("Jumlah PC terpilih (Kaiser):", n_pc, "\n")
## Jumlah PC terpilih (Kaiser): 4
# Skor PC
pc_scores <- as.data.frame(pca_result$x[, 1:n_pc])
print(head(pc_scores))
##          PC1          PC2         PC3        PC4
## 1 -1.5659467  0.444010452  0.44299401  1.0629889
## 2 -0.6756565 -0.008328797  0.09393802  0.2974240
## 3 -0.7424257  0.990516007 -0.23465933  0.1621183
## 4 -0.1108059 -0.454683810 -0.72378936  0.6156896
## 5  0.3612012  0.113413439  0.11599623  0.2612173
## 6 -0.1486593  0.431940475 -0.60620251 -0.7322983

Berdasarkan kriteria Kaiser, diperoleh 4 komponen utama yang dipertahankan (PC1 - PC4) dengan total variansi kumulatif sebesar 70.11%.

Skor PC merupakan hasil transformasi data asli ke dalam ruang komponen utama (PC), di mana setiap baris merepresentasikan posisi masing-masing lagu pada keempat komponen.

Observasi 1: Pada lagu pertama, skor tertinggi adalah pada PC4 (1.0630), yang menunjukkan lagu ini cenderung populer dengan elemen vokal yang sedikit.

Observasi 2: Pada lagu kedua, skor tertinggi adalah pada PC1 (-0.6757), yang menunjukkan lagu ini cenderung danceable dan populer.

Observasi 3: Pada lagu ketiga, skor tertinggi adalah pada PC2 (0.9905), yang menunjukkan lagu ini cenderung memiliki karakteristik akustik yang kuat dengan durasi yang pendek.

Observasi 4: Pada lagu keempat, skor tertinggi adalah pada PC3 (-0.7238), yang menunjukkan lagu ini memiliki elemen vokal yang sedikit.

Observasi 5: Pada lagu kelima, skor tertinggi adalah pada PC1 (0.3612), yang menunjukkan lagu ini cenderung bersifat akustik dan instrumental.

Observasi 6: Pada lagu keenam, skor tertinggi adalah pada PC4 (-0.7323), yang menunjukkan lagu ini cenderung memiliki elemen vokal yang banyak namun kurang populer.

Plot 3: Variable Plot

# Variable Plot
fviz_pca_var(pca_result, repel = TRUE, col.var = "contrib",
             gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07")) +
  labs(title = "Variable Plot PCA")

Variable plot menampilkan posisi setiap variabel dalam ruang dua dimensi yang dibentuk oleh PC1 (Dim1, 24.8%) dan PC2 (Dim2, 15.8%). Warna panah mencerminkan tingkat kontribusi variabel terhadap kedua komponen, di mana warna merah - oranye menunjukkan kontribusi tinggi dan warna biru-hijau menunjukkan kontribusi rendah.

Kuadran 1: Variabel acousticness memiliki panah terpanjang yang mengarah ke kuadran kanan atas, menunjukkan kontribusi positif yang kuat pada PC2 sekaligus kontribusi positif pada PC2. Acousticness dan acoustic_energy_ratio juga memiliki arah panah yang relatif berdekatan, mengindikasikan korelasi positif antara keduanya.

Kuadran 2: Variabel danceability dan dance_popularity memiliki panah yang mengarah ke kiri, menunjukkan kontribusi negatif pada PC1. Variabel dance_popularity dan danceability memiliki arah panah yang berdekatan, menunjukkan bahwa kedua variabel tersebut berkorelasi positif satu sama lain.

Kuadran 3: Speechiness memiliki panah yang sangat pendek dan hampir sejajar sumbu horizontal, mengindikasikan kontribusinya pada PC1 dan PC2 sangat kecil.

Kuadran 4: Variabel duration_ms memiliki panah yang mengarah ke kuadran kanan bawah, mencerminkan kontribusi positif pada PC1 namun negatif pada PC2.

Kesimpulan

Analisis eksplorasi dan reduksi data pada dataset Spotify telah dilakukan melalui tiga tahapan utama, yaitu Feature Engineering, Feature Selection, dan Feature Extraction menggunakan Principal Component Analysis (PCA). Berikut adalah kesimpulan dari keseluruhan analisis yang telah dilakukan.

Feature Engineering

Pada tahap Feature Engineering, berhasil dibentuk empat fitur baru dari variabel-variabel yang telah ada, yaitu mood_score, acoustic_energy_ratio, dance_popularity, dan energy_intensity. Keempat fitur tersebut dibuat untuk menangkap informasi yang lebih kaya mengenai karakteristik audio lagu, seperti suasana emosional, rasio akustik terhadap energi, potensi daya tarik lagu, serta intensitas audio secara keseluruhan.

Feature Selection

Pada tahap Feature Selection, digunakan dua metode yaitu Filter Method berbasis korelasi dan Wrapper Method menggunakan Stepwise Regression. Filter Method menghasilkan satu variabel terpilih, yaitu dance_popularity, karena memiliki korelasi absolut tertinggi terhadap popularity. Sementara itu, Wrapper Method dengan ketiga pendekatannya, yaitu Forward Selection, Backward Elimination, dan Stepwise Bidirectional, secara konsisten menghasilkan tujuh variabel terpilih yang sama dengan nilai AIC sebesar 6499,314. Variabel final yang digunakan untuk tahap PCA adalah dance_popularity, danceability, acoustic_energy_ratio, duration_ms, instrumentalness, speechiness, dan acousticness.

Berdasarkan hasil feature selection, variabel yang paling penting dalam menjelaskan popularity lagu adalah dance_popularity, yang memiliki korelasi absolut tertinggi terhadap popularity sebesar 0,9015. Hal ini menunjukkan bahwa lagu yang memiliki karakteristik danceable sekaligus populer cenderung mendominasi dataset. Selain itu, variabel danceability, acoustic_energy_ratio, duration_ms, instrumentalness, speechiness, dan acousticness juga terpilih secara konsisten oleh Wrapper Method sebagai variabel yang berkontribusi signifikan dalam model prediktif.

Feature Extraction

Pada tahap Feature Extraction menggunakan PCA, analisis dilakukan terhadap 7 variabel hasil feature selection dengan 1.000 observasi. Berdasarkan kriteria Kaiser, diperoleh 4 komponen utama yang dipertahankan, yaitu PC1 hingga PC4, dengan total variansi kumulatif sebesar 70,11%.

Keempat komponen tersebut masing-masing merepresentasikan dimensi yang berbeda:

  • PC1 menangkap kontras antara karakteristik Danceability dengan Acoustic, di mana lagu dengan skor rendah cenderung danceable dan populer, sementara lagu dengan skor tinggi cenderung akustik dan instrumental.

  • PC2 menangkap kontras antara Duration dengan Acoustic, yang menangkap kontras antara lagu berdurasi panjang dengan lagu berkarakter akustik kuat.

  • PC3 menangkap Speechiness, merepresentasikan dimensi speechiness, yang menangkap seberapa banyak kandungan vokal atau lirik dalam suatu lagu.

  • PC4 menangkap kontras antara Dance Popularity dengan Speechiness, merepresentasikan dimensi popularitas vs kandungan vokal, di mana lagu populer cenderung memiliki kandungan vokal yang rendah.

Beberapa insight penting yang diperoleh dari analisis ini adalah sebagai berikut:

  1. Popularitas lagu di Spotify sangat erat kaitannya dengan karakteristik danceability, yang tercermin dari tingginya korelasi dance_popularity terhadap popularity.

  2. Terdapat kontras yang jelas antara lagu berkarakter danceable dengan lagu berkarakter akustik, di mana keduanya membentuk dimensi utama dalam data.

  3. Durasi lagu dan tingkat akustik merupakan dua dimensi yang saling berlawanan, artinya lagu akustik pada dataset ini cenderung berdurasi lebih pendek dibandingkan lagu non-akustik.

  4. Elemen vokal (speechiness) membentuk dimensi tersendiri yang independen dari variabel lainnya, menunjukkan bahwa karakteristik ini unik dan tidak tumpang tindih dengan dimensi lain dalam data.