1 Pendahuluan

1.1 Latar Belakang

Human Development Index (HDI) atau Indeks Pembangunan Manusia (IPM) merupakan salah satu indikator yang digunakan untuk melihat tingkat pembangunan manusia di suatu negara. HDI menggambarkan pencapaian pembangunan manusia melalui aspek kesehatan, pendidikan, dan standar hidup. Perbedaan nilai HDI antarnegara dapat dipengaruhi oleh berbagai kondisi sosial dan ekonomi.

Dalam praktikum ini, digunakan enam variabel prediktor, yaitu GDP per capita, social support, healthy life expectancy, freedom to make life choices, generosity, dan perceptions of corruption. Keenam variabel tersebut digunakan untuk menganalisis pola hubungan dengan HDI melalui pendekatan machine learning.

Metode yang digunakan adalah Support Vector Machine (SVM), Support Vector Regression (SVR), dan Neural Network (NN). SVM digunakan untuk mengklasifikasikan HDI ke dalam dua kategori, yaitu tinggi dan rendah, dengan batas nilai 0,700. Sementara itu, SVR digunakan untuk memprediksi nilai HDI dalam bentuk numerik. NN diterapkan untuk kedua permasalahan tersebut, baik klasifikasi maupun regresi. Setiap metode diuji menggunakan beberapa konfigurasi model. Pada SVM dan SVR dilakukan pengujian kernel serta kombinasi hyperparameter, sedangkan pada NN dilakukan perbandingan jumlah neuron dan struktur hidden layer, termasuk NN biasa dan Deep Neural Network. Selain itu, digunakan 5-fold dan 10-fold cross-validation untuk membandingkan hasil evaluasi dan menentukan konfigurasi model yang paling sesuai.

Melalui praktikum ini, dilakukan perbandingan performa SVM, SVR, dan NN dalam mengklasifikasikan serta memprediksi HDI. Hasil pengujian diharapkan dapat menunjukkan kombinasi parameter dan konfigurasi model yang memberikan performa terbaik berdasarkan metrik evaluasi klasifikasi dan regresi.

1.2 Tujuan

  1. Memahami penerapan metode Support Vector Machine (SVM), Support Vector Regression (SVR), dan Neural Network (NN) dalam menyelesaikan permasalahan klasifikasi dan regresi.

  2. Membangun model klasifikasi untuk mengelompokkan negara berdasarkan kategori Human Development Index (HDI), yaitu tinggi dan rendah, menggunakan metode SVM dan Neural Network.

  3. Membangun model regresi untuk memprediksi nilai Human Development Index (HDI) menggunakan metode SVR dan Neural Network.

  4. Melakukan optimasi hyperparameter pada setiap metode untuk memperoleh kombinasi parameter yang memberikan performa model terbaik, serta mengevaluasi model menggunakan cross-validation.

  5. Membandingkan performa model klasifikasi dan regresi berdasarkan metrik evaluasi yang sesuai untuk menentukan model yang paling baik dalam menyelesaikan masing-masing permasalahan.

2 Metodologi Penelitian

2.1 Data dan Sumber Data

Data yang digunakan dalam praktikum ini merupakan data sekunder yang terdiri dari 153 observasi. Data mencakup informasi mengenai kondisi sosial, ekonomi, dan pembangunan manusia dari sejumlah negara. Data tersebut digunakan untuk membangun model klasifikasi dan regresi dengan HDI sebagai variabel yang dianalisis.

Sumber data berasal dari World Happiness Report dan Human Development Data Center yang dikelola oleh United Nations Development Programme (UNDP). Kedua sumber tersebut menyediakan informasi terkait kondisi kesejahteraan dan pembangunan manusia antarnegara.

2.2 Variabel Penelitian

2.2.1 Variabel Respons

tabel_y <- data.frame(
  Kode = "Y",
  Variabel = "Human Development Index (HDI)",
  Keterangan = "Indeks yang menggambarkan tingkat pembangunan manusia berdasarkan dimensi kesehatan, pendidikan, dan standar hidup layak."
)

knitr::kable(
  tabel_y,
  caption = "Deskripsi Variabel Respons",
  align = "c"
)
Deskripsi Variabel Respons
Kode Variabel Keterangan
Y Human Development Index (HDI) Indeks yang menggambarkan tingkat pembangunan manusia berdasarkan dimensi kesehatan, pendidikan, dan standar hidup layak.

2.2.2 Variabel Prediktor

# Tabel variabel prediktor
tabel_x <- data.frame(
  Kode = paste0("X", 1:6),
  Variabel = c(
    "GDP per Capita",
    "Social Support",
    "Healthy Life Expectancy",
    "Freedom to Make Life Choices",
    "Generosity",
    "Perceptions of Corruption"
  ),
  Keterangan = c(
    "Menggambarkan tingkat pendapatan atau aktivitas ekonomi per kapita.",
    "Menggambarkan dukungan sosial yang dirasakan individu.",
    "Menggambarkan perkiraan harapan hidup sehat masyarakat.",
    "Menggambarkan kebebasan individu dalam menentukan pilihan hidup.",
    "Menggambarkan kecenderungan perilaku kedermawanan atau kepedulian terhadap orang lain.",
    "Menggambarkan persepsi masyarakat terhadap tingkat korupsi dalam pemerintahan dan institusi."
  )
)

knitr::kable(
  tabel_x,
  caption = "Deskripsi Variabel Prediktor",
  align = c("c", "l", "l")
)
Deskripsi Variabel Prediktor
Kode Variabel Keterangan
X1 GDP per Capita Menggambarkan tingkat pendapatan atau aktivitas ekonomi per kapita.
X2 Social Support Menggambarkan dukungan sosial yang dirasakan individu.
X3 Healthy Life Expectancy Menggambarkan perkiraan harapan hidup sehat masyarakat.
X4 Freedom to Make Life Choices Menggambarkan kebebasan individu dalam menentukan pilihan hidup.
X5 Generosity Menggambarkan kecenderungan perilaku kedermawanan atau kepedulian terhadap orang lain.
X6 Perceptions of Corruption Menggambarkan persepsi masyarakat terhadap tingkat korupsi dalam pemerintahan dan institusi.

3 Persiapan dan Eksplorasi Data

3.1 Persiapan Analisis

Sebelum memasuki tahap pengolahan data dan pemodelan, dilakukan persiapan lingkungan analisis menggunakan perangkat lunak R. Tahap ini mencakup pengaturan konfigurasi code chunk, penetapan nilai random seed, serta instalasi dan pemanggilan package yang dibutuhkan selama proses analisis.

Pengaturan awal dilakukan untuk memastikan proses analisis berjalan secara konsisten, meminimalkan gangguan pada tampilan output, serta mempermudah penyajian hasil dalam dokumen R Markdown.

3.1.1 Konfigurasi Analisis

Konfigurasi awal dilakukan melalui fungsi knitr::opts_chunk$set() untuk mengatur perilaku setiap code chunk dalam dokumen. Pada analisis ini, kode secara default ditampilkan, sedangkan pesan dan peringatan dari package disembunyikan agar hasil analisis terlihat lebih ringkas.

Selain itu, digunakan set.seed(2026) untuk menjaga konsistensi hasil dari proses yang melibatkan bilangan acak, seperti pembagian data menjadi data latih dan data uji.

3.1.2 Instalasi dan Pemanggilan Package

Analisis ini memanfaatkan beberapa package R yang memiliki fungsi berbeda, mulai dari pengolahan data, visualisasi, hingga pembentukan model Support Vector Regression (SVR).

Berikut merupakan package yang digunakan beserta fungsinya.

Package Fungsi
e1071 Membentuk model Support Vector Regression (SVR).
ggplot2 Membuat visualisasi data dan hasil analisis.
dplyr Melakukan manipulasi dan pengolahan data.
knitr Mendukung penyajian tabel dan output dalam dokumen R Markdown.
readr Membaca dan mengimpor data.
janitor Membersihkan dan merapikan nama kolom serta struktur data.
corrplot Memvisualisasikan matriks korelasi antarvariabel.
tidyr Merapikan data dan menangani missing value.
rsample Membantu proses pembagian data dan cross-validation.
kernlab Membentuk model SVM dan SVR dengan berbagai kernel.
yardstick Menghitung metrik evaluasi performa model.
purrr Membantu melakukan iterasi dan pengolahan data secara berulang.
tibble Membuat dan mengelola tabel data dalam bentuk tibble.

Sebelum digunakan, dilakukan pemeriksaan ketersediaan package pada sistem. Apabila terdapat package yang belum terpasang, R akan menginstalnya secara otomatis. Selanjutnya, seluruh package dipanggil menggunakan fungsi library() agar dapat digunakan dalam tahapan analisis berikutnya.

# Package yang digunakan
paket <- c(
  "e1071",
  "ggplot2",
  "dplyr",
  "knitr",
  "readr",
  "janitor",
  "corrplot",
  "tidyr",
  "rsample",
  "kernlab",
  "yardstick",
  "purrr",
  "tibble"
)

# Instalasi package jika belum tersedia
belum <- paket[
  !vapply(paket, requireNamespace, logical(1), quietly = TRUE)
]

if (length(belum) > 0) {
  install.packages(belum, repos = "https://cloud.r-project.org")
}

# Memanggil package
library(e1071)
library(ggplot2)
library(dplyr)
library(knitr)
library(janitor)
library(corrplot)
library(tidyr)
library(rsample)
library(kernlab)
library(yardstick)
library(purrr)
library(tibble)

3.2 Import Data

Sebelum masuk ke tahap pengolahan dan analisis data, dilakukan proses import dataset yang akan digunakan dalam pemodelan. Dataset yang digunakan merupakan data yang telah melalui tahap preprocessing dan disimpan dalam format CSV dengan nama data_preprocessed.csv. Data tersebut memuat informasi mengenai sejumlah negara, termasuk indikator sosial ekonomi dan nilai Human Development Index (HDI).

Proses import dilakukan menggunakan fungsi read.csv() untuk membaca dataset ke dalam lingkungan kerja R. Setelah data berhasil diimpor, dilakukan pemeriksaan awal menggunakan fungsi head(), tail(), summary(), dan str() untuk memperoleh gambaran mengenai isi, karakteristik, serta struktur dataset sebelum memasuki tahapan analisis lebih lanjut.

calon <- unique(c(params$file_hdi, "data_preprocessed.csv"))
ada <- calon[file.exists(calon)]

if (!length(ada)) stop("CSV HDI tidak ditemukan. Simpan di output/data_preprocessed.csv atau ubah params$file_hdi. Untuk contoh saja, set jalankan_hdi: false.")
raw_hdi <- readr::read_csv(ada[1], show_col_types=FALSE)
asal <- c("GDP per capita", "Social support", "Healthy life expectancy",
          "Freedom to make life choices", "Generosity", "Perceptions of corruption")
aman <- c("gdp", "support", "health", "freedom", "generosity", "corruption")
wajib <- c("hdi", asal)
hilang <- setdiff(wajib, names(raw_hdi))

if (length(hilang)) stop("Kolom tidak ditemukan: ", paste(hilang, collapse=", "))
hdi <- as.data.frame(raw_hdi[, wajib])
names(hdi) <- c("hdi", aman)

if (!all(vapply(hdi, is.numeric, logical(1))))
  stop("Seluruh variabel harus numerik. Periksa CSV/preprocessing.")
valid <- Reduce(`&`, lapply(hdi, is.finite))
cat("Baris awal:", nrow(hdi), "| Tidak lengkap/tidak finite:", sum(!valid), "\n")
## Baris awal: 153 | Tidak lengkap/tidak finite: 0
baris_asal <- which(valid)
hdi <- hdi[valid, , drop=FALSE]

if (nrow(hdi)<30) stop("Minimal 30 baris lengkap untuk rancangan praktikum ini.")
if (any(hdi$hdi<0 | hdi$hdi>1)) stop("HDI harus pada rentang 0–1; periksa sumber.")
knitr::kable(summary(hdi), digits=3)
hdi gdp support health freedom generosity corruption
Min. :0.3880 Min. :0.0000 Min. :0.000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.000
1st Qu.:0.6170 1st Qu.:0.5780 1st Qu.:1.055 1st Qu.:0.5380 1st Qu.:0.3050 1st Qu.:0.1080 1st Qu.:0.047
Median :0.7760 Median :0.9600 Median :1.274 Median :0.7890 Median :0.4170 Median :0.1770 Median :0.085
Mean :0.7455 Mean :0.8999 Mean :1.207 Mean :0.7217 Mean :0.3921 Mean :0.1839 Mean :0.111
3rd Qu.:0.8890 3rd Qu.:1.2210 3rd Qu.:1.454 3rd Qu.:0.8810 3rd Qu.:0.5080 3rd Qu.:0.2470 3rd Qu.:0.141
Max. :0.9720 Max. :1.6840 Max. :1.624 Max. :1.1410 Max. :0.6310 Max. :0.5660 Max. :0.453
# Membaca data
data <- read.csv(ada[1], header = TRUE)

head(data)
##   Overall.rank Country.or.region Score GDP.per.capita Social.support
## 1            1           Finland 7.769          1.340          1.587
## 2            2           Denmark 7.600          1.383          1.573
## 3            3            Norway 7.554          1.488          1.582
## 4            4           Iceland 7.494          1.380          1.624
## 5            5       Netherlands 7.488          1.396          1.522
## 6            6       Switzerland 7.480          1.452          1.526
##   Healthy.life.expectancy Freedom.to.make.life.choices Generosity
## 1                   0.986                        0.596      0.153
## 2                   0.996                        0.592      0.252
## 3                   1.028                        0.603      0.271
## 4                   1.026                        0.591      0.354
## 5                   0.999                        0.557      0.322
## 6                   1.052                        0.572      0.263
##   Perceptions.of.corruption hdi_name    join_key   hdi
## 1                     0.393     <NA>     Finland 0.948
## 2                     0.410     <NA>     Denmark 0.962
## 3                     0.341     <NA>      Norway 0.970
## 4                     0.118     <NA>     Iceland 0.972
## 5                     0.298     <NA> Netherlands 0.955
## 6                     0.343     <NA> Switzerland 0.970
tail(data)
##     Overall.rank        Country.or.region Score GDP.per.capita Social.support
## 148          151                    Yemen 3.380          0.287          1.163
## 149          152                   Rwanda 3.334          0.359          0.711
## 150          153                 Tanzania 3.231          0.476          0.885
## 151          154              Afghanistan 3.203          0.350          0.517
## 152          155 Central African Republic 3.083          0.026          0.000
## 153          156              South Sudan 2.853          0.306          0.575
##     Healthy.life.expectancy Freedom.to.make.life.choices Generosity
## 148                   0.463                        0.143      0.108
## 149                   0.614                        0.555      0.217
## 150                   0.499                        0.417      0.276
## 151                   0.361                        0.000      0.158
## 152                   0.105                        0.225      0.235
## 153                   0.295                        0.010      0.202
##     Perceptions.of.corruption hdi_name                 join_key   hdi
## 148                     0.077     <NA>                    Yemen 0.470
## 149                     0.411     <NA>                   Rwanda 0.578
## 150                     0.147     <NA>                 Tanzania 0.555
## 151                     0.025     <NA>              Afghanistan 0.496
## 152                     0.035     <NA> Central African Republic 0.414
## 153                     0.091     <NA>              South Sudan 0.388
summary(data)
##   Overall.rank    Country.or.region      Score       GDP.per.capita  
##  Min.   :  1.00   Length:153         Min.   :2.853   Min.   :0.0000  
##  1st Qu.: 40.00   Class :character   1st Qu.:4.534   1st Qu.:0.5780  
##  Median : 80.00   Mode  :character   Median :5.339   Median :0.9600  
##  Mean   : 79.16                      Mean   :5.394   Mean   :0.8999  
##  3rd Qu.:118.00                      3rd Qu.:6.182   3rd Qu.:1.2210  
##  Max.   :156.00                      Max.   :7.769   Max.   :1.6840  
##  Social.support  Healthy.life.expectancy Freedom.to.make.life.choices
##  Min.   :0.000   Min.   :0.0000          Min.   :0.0000              
##  1st Qu.:1.055   1st Qu.:0.5380          1st Qu.:0.3050              
##  Median :1.274   Median :0.7890          Median :0.4170              
##  Mean   :1.207   Mean   :0.7217          Mean   :0.3921              
##  3rd Qu.:1.454   3rd Qu.:0.8810          3rd Qu.:0.5080              
##  Max.   :1.624   Max.   :1.1410          Max.   :0.6310              
##    Generosity     Perceptions.of.corruption   hdi_name        
##  Min.   :0.0000   Min.   :0.000             Length:153        
##  1st Qu.:0.1080   1st Qu.:0.047             Class :character  
##  Median :0.1770   Median :0.085             Mode  :character  
##  Mean   :0.1839   Mean   :0.111                               
##  3rd Qu.:0.2470   3rd Qu.:0.141                               
##  Max.   :0.5660   Max.   :0.453                               
##    join_key              hdi        
##  Length:153         Min.   :0.3880  
##  Class :character   1st Qu.:0.6170  
##  Mode  :character   Median :0.7760  
##                     Mean   :0.7455  
##                     3rd Qu.:0.8890  
##                     Max.   :0.9720
str(data)
## 'data.frame':    153 obs. of  12 variables:
##  $ Overall.rank                : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Country.or.region           : chr  "Finland" "Denmark" "Norway" "Iceland" ...
##  $ Score                       : num  7.77 7.6 7.55 7.49 7.49 ...
##  $ GDP.per.capita              : num  1.34 1.38 1.49 1.38 1.4 ...
##  $ Social.support              : num  1.59 1.57 1.58 1.62 1.52 ...
##  $ Healthy.life.expectancy     : num  0.986 0.996 1.028 1.026 0.999 ...
##  $ Freedom.to.make.life.choices: num  0.596 0.592 0.603 0.591 0.557 0.572 0.574 0.585 0.584 0.532 ...
##  $ Generosity                  : num  0.153 0.252 0.271 0.354 0.322 0.263 0.267 0.33 0.285 0.244 ...
##  $ Perceptions.of.corruption   : num  0.393 0.41 0.341 0.118 0.298 0.343 0.373 0.38 0.308 0.226 ...
##  $ hdi_name                    : chr  NA NA NA NA ...
##  $ join_key                    : chr  "Finland" "Denmark" "Norway" "Iceland" ...
##  $ hdi                         : num  0.948 0.962 0.97 0.972 0.955 0.97 0.959 0.938 0.939 0.93 ...

Berdasarkan hasil pemeriksaan awal, dataset yang digunakan terdiri atas 153 observasi dan 12 variabel. Variabel tersebut mencakup informasi peringkat negara, skor kebahagiaan, enam indikator prediktor, serta nilai HDI.

Hasil summary() menunjukkan bahwa nilai HDI memiliki rata-rata sebesar 0,7455, median sebesar 0,776, nilai minimum sebesar 0,388, dan maksimum sebesar 0,972. Hal ini menunjukkan bahwa nilai HDI pada observasi yang digunakan memiliki rentang yang cukup beragam.

Sementara itu, hasil str() menunjukkan bahwa keenam variabel prediktor dan variabel HDI memiliki tipe data numerik. Dengan demikian, variabel-variabel tersebut dapat digunakan dalam pemodelan SVR yang merupakan metode regresi untuk memprediksi respons numerik.

3.3 Pemeriksaan Data

Setelah dataset berhasil diimpor, tahap selanjutnya adalah melakukan pemeriksaan data untuk mengetahui kondisi dan kelengkapan dataset yang akan digunakan dalam analisis. Pemeriksaan ini dilakukan untuk mengidentifikasi keberadaan missing value (data yang hilang), data duplikat, serta memastikan dimensi dan struktur dataset telah sesuai dengan kebutuhan analisis. Selain itu, dilakukan pemeriksaan terhadap nama dan tipe variabel untuk memperoleh gambaran awal mengenai karakteristik data.

Tahap ini penting dilakukan agar permasalahan pada data dapat diketahui sejak awal sehingga tidak mengganggu proses pengolahan maupun pemodelan menggunakan metode Support Vector Machine (SVM), Support Vector Regression (SVR), dan Neural Network (NN).

# Mengecek Missing Value
colSums(is.na(data))
##                 Overall.rank            Country.or.region 
##                            0                            0 
##                        Score               GDP.per.capita 
##                            0                            0 
##               Social.support      Healthy.life.expectancy 
##                            0                            0 
## Freedom.to.make.life.choices                   Generosity 
##                            0                            0 
##    Perceptions.of.corruption                     hdi_name 
##                            0                          147 
##                     join_key                          hdi 
##                            0                            0
# Mengecek Duplikasi
sum(duplicated(data))
## [1] 0
# Dimensi data
dim(data)
## [1] 153  12
# Struktur data
str(data)
## 'data.frame':    153 obs. of  12 variables:
##  $ Overall.rank                : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Country.or.region           : chr  "Finland" "Denmark" "Norway" "Iceland" ...
##  $ Score                       : num  7.77 7.6 7.55 7.49 7.49 ...
##  $ GDP.per.capita              : num  1.34 1.38 1.49 1.38 1.4 ...
##  $ Social.support              : num  1.59 1.57 1.58 1.62 1.52 ...
##  $ Healthy.life.expectancy     : num  0.986 0.996 1.028 1.026 0.999 ...
##  $ Freedom.to.make.life.choices: num  0.596 0.592 0.603 0.591 0.557 0.572 0.574 0.585 0.584 0.532 ...
##  $ Generosity                  : num  0.153 0.252 0.271 0.354 0.322 0.263 0.267 0.33 0.285 0.244 ...
##  $ Perceptions.of.corruption   : num  0.393 0.41 0.341 0.118 0.298 0.343 0.373 0.38 0.308 0.226 ...
##  $ hdi_name                    : chr  NA NA NA NA ...
##  $ join_key                    : chr  "Finland" "Denmark" "Norway" "Iceland" ...
##  $ hdi                         : num  0.948 0.962 0.97 0.972 0.955 0.97 0.959 0.938 0.939 0.93 ...
# Variabel Data
names(data)
##  [1] "Overall.rank"                 "Country.or.region"           
##  [3] "Score"                        "GDP.per.capita"              
##  [5] "Social.support"               "Healthy.life.expectancy"     
##  [7] "Freedom.to.make.life.choices" "Generosity"                  
##  [9] "Perceptions.of.corruption"    "hdi_name"                    
## [11] "join_key"                     "hdi"
# Inspeksi Awal Data
glimpse(data)
## Rows: 153
## Columns: 12
## $ Overall.rank                 <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13…
## $ Country.or.region            <chr> "Finland", "Denmark", "Norway", "Iceland"…
## $ Score                        <dbl> 7.769, 7.600, 7.554, 7.494, 7.488, 7.480,…
## $ GDP.per.capita               <dbl> 1.340, 1.383, 1.488, 1.380, 1.396, 1.452,…
## $ Social.support               <dbl> 1.587, 1.573, 1.582, 1.624, 1.522, 1.526,…
## $ Healthy.life.expectancy      <dbl> 0.986, 0.996, 1.028, 1.026, 0.999, 1.052,…
## $ Freedom.to.make.life.choices <dbl> 0.596, 0.592, 0.603, 0.591, 0.557, 0.572,…
## $ Generosity                   <dbl> 0.153, 0.252, 0.271, 0.354, 0.322, 0.263,…
## $ Perceptions.of.corruption    <dbl> 0.393, 0.410, 0.341, 0.118, 0.298, 0.343,…
## $ hdi_name                     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ join_key                     <chr> "Finland", "Denmark", "Norway", "Iceland"…
## $ hdi                          <dbl> 0.948, 0.962, 0.970, 0.972, 0.955, 0.970,…

Berdasarkan pemeriksaan missing value, ditemukan bahwa variabel hdi_name memiliki 147 nilai yang hilang. Namun, variabel tersebut tidak digunakan dalam analisis SVR sehingga tidak memengaruhi proses pemodelan. Seluruh variabel yang digunakan, yaitu enam variabel prediktor dan satu variabel respons HDI, tidak memiliki missing value.

Selain itu, hasil pemeriksaan duplikasi menunjukkan bahwa tidak terdapat observasi yang terduplikasi. Dataset juga memiliki dimensi 153 baris dan 12 kolom. Setelah dilakukan pemilihan variabel untuk pemodelan, diperoleh 153 observasi dengan tujuh variabel analisis.

Dengan demikian, data yang digunakan dalam pemodelan tidak memerlukan penanganan missing value maupun penghapusan data duplikat.

3.4 Membersihkan Nama Variabel

Setelah dilakukan pemeriksaan data, tahap selanjutnya adalah membersihkan nama variabel agar lebih sederhana dan konsisten. Proses ini dilakukan menggunakan fungsi clean_names() dari package janitor untuk mengubah nama variabel ke dalam format snake_case, yaitu menggunakan huruf kecil dan tanda garis bawah (_) sebagai pemisah antar kata. Dengan demikian, nama variabel menjadi lebih mudah dipanggil dalam kode R tanpa mengubah isi maupun nilai data.

data <- data %>%
  clean_names()

names(data)
##  [1] "overall_rank"                 "country_or_region"           
##  [3] "score"                        "gdp_per_capita"              
##  [5] "social_support"               "healthy_life_expectancy"     
##  [7] "freedom_to_make_life_choices" "generosity"                  
##  [9] "perceptions_of_corruption"    "hdi_name"                    
## [11] "join_key"                     "hdi"

Berdasarkan hasil pembersihan, nama-nama variabel berhasil diseragamkan ke dalam format snake_case. Sebagai contoh, variabel GDP.per.capita diubah menjadi gdp_per_capita, sedangkan Social.support menjadi social_support. Dengan demikian, seluruh nama variabel menjadi lebih sederhana dan konsisten sehingga memudahkan proses pemanggilan variabel dalam analisis selanjutnya.

Perlu diperhatikan bahwa proses ini hanya mengubah format penamaan variabel dan tidak mengubah nilai maupun isi data di dalamnya.

3.5 Menentukan Variabel Penelitian

Pada penelitian ini, variabel yang digunakan terdiri atas satu variabel respons dan enam variabel prediktor. Variabel respons merupakan variabel yang akan diprediksi oleh model Support Vector Regression (SVR), sedangkan variabel prediktor merupakan variabel yang digunakan sebagai masukan untuk membantu menjelaskan dan memprediksi nilai variabel respons.

3.5.1 Variabel Respons

Variabel respons yang digunakan adalah Human Development Index (HDI). Variabel ini dipilih sebagai variabel yang akan diprediksi karena menggambarkan tingkat pembangunan manusia suatu negara. Dalam pemodelan SVR, nilai HDI berperan sebagai target yang ingin diestimasi berdasarkan enam indikator prediktor.

tabel_y <- data.frame(
  Kode = "Y",
  Variabel = "Human Development Index (HDI)",
  Keterangan = "Indeks yang menggambarkan tingkat pembangunan manusia berdasarkan dimensi kesehatan, pendidikan, dan standar hidup layak."
)

knitr::kable(
  tabel_y,
  caption = "Deskripsi Variabel Respons",
  align = "c"
)
Deskripsi Variabel Respons
Kode Variabel Keterangan
Y Human Development Index (HDI) Indeks yang menggambarkan tingkat pembangunan manusia berdasarkan dimensi kesehatan, pendidikan, dan standar hidup layak.
Y <- data[, 
          c("hdi"
            )]

# Memeriksa dimensi
length(Y)
## [1] 153
# Melihat data
head(Y)
## [1] 0.948 0.962 0.970 0.972 0.955 0.970

Berdasarkan hasil pemeriksaan, variabel respons HDI memiliki 153 observasi, yang menunjukkan bahwa seluruh observasi dalam dataset digunakan sebagai variabel respons. Nilai HDI memiliki rentang antara 0,388 hingga 0,972. Sementara itu, enam nilai yang ditampilkan merupakan sebagian hasil pengamatan HDI, yang menunjukkan bahwa variabel respons telah tersimpan dalam bentuk numerik dan dapat digunakan dalam pemodelan regresi. Pada tahap selanjutnya, nilai HDI juga akan dikategorikan menjadi kelas tinggi dan rendah untuk kebutuhan pemodelan klasifikasi.

3.5.2 Variabel Prediktor

Setelah menentukan variabel respons, tahap selanjutnya adalah menentukan variabel prediktor (X) yang digunakan untuk menjelaskan dan memprediksi nilai Human Development Index (HDI). Dalam penelitian ini, terdapat enam variabel prediktor yang dipilih, yaitu GDP per Capita, Social Support, Healthy Life Expectancy, Freedom to Make Life Choices, Generosity, dan Perceptions of Corruption. Keenam variabel tersebut merepresentasikan berbagai aspek sosial dan ekonomi yang diduga berkaitan dengan tingkat pembangunan manusia. Variabel-variabel ini kemudian digunakan sebagai input dalam proses pemodelan SVM, SVR, dan Neural Network.

# Tabel variabel prediktor
tabel_x <- data.frame(
  Kode = paste0("X", 1:6),
  Variabel = c(
    "GDP per Capita",
    "Social Support",
    "Healthy Life Expectancy",
    "Freedom to Make Life Choices",
    "Generosity",
    "Perceptions of Corruption"
  ),
  Keterangan = c(
    "Menggambarkan tingkat pendapatan atau aktivitas ekonomi per kapita.",
    "Menggambarkan dukungan sosial yang dirasakan individu.",
    "Menggambarkan perkiraan harapan hidup sehat masyarakat.",
    "Menggambarkan kebebasan individu dalam menentukan pilihan hidup.",
    "Menggambarkan kecenderungan perilaku kedermawanan atau kepedulian terhadap orang lain.",
    "Menggambarkan persepsi masyarakat terhadap tingkat korupsi dalam pemerintahan dan institusi."
  )
)

knitr::kable(
  tabel_x,
  caption = "Deskripsi Variabel Prediktor",
  align = c("c", "l", "l")
)
Deskripsi Variabel Prediktor
Kode Variabel Keterangan
X1 GDP per Capita Menggambarkan tingkat pendapatan atau aktivitas ekonomi per kapita.
X2 Social Support Menggambarkan dukungan sosial yang dirasakan individu.
X3 Healthy Life Expectancy Menggambarkan perkiraan harapan hidup sehat masyarakat.
X4 Freedom to Make Life Choices Menggambarkan kebebasan individu dalam menentukan pilihan hidup.
X5 Generosity Menggambarkan kecenderungan perilaku kedermawanan atau kepedulian terhadap orang lain.
X6 Perceptions of Corruption Menggambarkan persepsi masyarakat terhadap tingkat korupsi dalam pemerintahan dan institusi.

Keenam variabel prediktor tersebut digunakan untuk menggambarkan berbagai aspek ekonomi, sosial, kesehatan, dan tata kelola yang berpotensi berkaitan dengan tingkat pembangunan manusia. Seluruh variabel prediktor digunakan secara bersamaan sebagai masukan dalam model SVR untuk memprediksi nilai HDI.

X <- data[, c(
  "gdp_per_capita",
  "social_support",
  "healthy_life_expectancy",
  "freedom_to_make_life_choices",
  "generosity",
  "perceptions_of_corruption"
)]

dim(X)
## [1] 153   6
head(X)
##   gdp_per_capita social_support healthy_life_expectancy
## 1          1.340          1.587                   0.986
## 2          1.383          1.573                   0.996
## 3          1.488          1.582                   1.028
## 4          1.380          1.624                   1.026
## 5          1.396          1.522                   0.999
## 6          1.452          1.526                   1.052
##   freedom_to_make_life_choices generosity perceptions_of_corruption
## 1                        0.596      0.153                     0.393
## 2                        0.592      0.252                     0.410
## 3                        0.603      0.271                     0.341
## 4                        0.591      0.354                     0.118
## 5                        0.557      0.322                     0.298
## 6                        0.572      0.263                     0.343

3.5.3 Membentuk Dataset Variabel

data_svr <- data.frame(X,
                       hdi=Y)

# Melihat dataset analisis
head(data_svr)
##   gdp_per_capita social_support healthy_life_expectancy
## 1          1.340          1.587                   0.986
## 2          1.383          1.573                   0.996
## 3          1.488          1.582                   1.028
## 4          1.380          1.624                   1.026
## 5          1.396          1.522                   0.999
## 6          1.452          1.526                   1.052
##   freedom_to_make_life_choices generosity perceptions_of_corruption   hdi
## 1                        0.596      0.153                     0.393 0.948
## 2                        0.592      0.252                     0.410 0.962
## 3                        0.603      0.271                     0.341 0.970
## 4                        0.591      0.354                     0.118 0.972
## 5                        0.557      0.322                     0.298 0.955
## 6                        0.572      0.263                     0.343 0.970
# Melihat dimensi data
dim(data_svr)
## [1] 153   7
# Memeriksa struktur data
str(data_svr)
## 'data.frame':    153 obs. of  7 variables:
##  $ gdp_per_capita              : num  1.34 1.38 1.49 1.38 1.4 ...
##  $ social_support              : num  1.59 1.57 1.58 1.62 1.52 ...
##  $ healthy_life_expectancy     : num  0.986 0.996 1.028 1.026 0.999 ...
##  $ freedom_to_make_life_choices: num  0.596 0.592 0.603 0.591 0.557 0.572 0.574 0.585 0.584 0.532 ...
##  $ generosity                  : num  0.153 0.252 0.271 0.354 0.322 0.263 0.267 0.33 0.285 0.244 ...
##  $ perceptions_of_corruption   : num  0.393 0.41 0.341 0.118 0.298 0.343 0.373 0.38 0.308 0.226 ...
##  $ hdi                         : num  0.948 0.962 0.97 0.972 0.955 0.97 0.959 0.938 0.939 0.93 ...

Pada penelitian ini, variabel respons yang digunakan adalah HDI (hdi), sedangkan variabel prediktor terdiri atas enam indikator dari World Happiness, yaitu GDP per capita, Social support, Healthy life expectancy, Freedom to make life choices, Generosity, dan Perceptions of corruption.

Pemilihan HDI sebagai variabel respons didasarkan pada tujuan analisis, yaitu membangun model yang dapat memprediksi nilai HDI berdasarkan indikator-indikator tersebut. Keenam prediktor digunakan secara bersamaan untuk menangkap informasi yang berpotensi berkaitan dengan variasi nilai HDI.

Selanjutnya, seluruh variabel digabungkan ke dalam satu dataset analisis yang terdiri atas 153 observasi dan tujuh variabel.

3.6 Pemeriksaan Tipe Data dan Missing Value

# Memastikan seluruh variabel numerik
sapply(data_svr, is.numeric)
##               gdp_per_capita               social_support 
##                         TRUE                         TRUE 
##      healthy_life_expectancy freedom_to_make_life_choices 
##                         TRUE                         TRUE 
##                   generosity    perceptions_of_corruption 
##                         TRUE                         TRUE 
##                          hdi 
##                         TRUE
# Menghitung missing value
colSums(is.na(data_svr))
##               gdp_per_capita               social_support 
##                            0                            0 
##      healthy_life_expectancy freedom_to_make_life_choices 
##                            0                            0 
##                   generosity    perceptions_of_corruption 
##                            0                            0 
##                          hdi 
##                            0
# Menghitung persentase missing value
round(
  colMeans(is.na(data_svr)) * 100,
  2
)
##               gdp_per_capita               social_support 
##                            0                            0 
##      healthy_life_expectancy freedom_to_make_life_choices 
##                            0                            0 
##                   generosity    perceptions_of_corruption 
##                            0                            0 
##                          hdi 
##                            0
# Menghitung total missing value
sum(is.na(data_svr))
## [1] 0
# Menghapus observasi yang memiliki missing value
data_svr <- data_svr[
  complete.cases(data_svr),
]

# Memeriksa kembali missing value
colSums(is.na(data_svr))
##               gdp_per_capita               social_support 
##                            0                            0 
##      healthy_life_expectancy freedom_to_make_life_choices 
##                            0                            0 
##                   generosity    perceptions_of_corruption 
##                            0                            0 
##                          hdi 
##                            0
# Memeriksa dimensi setelah pembersihan
dim(data_svr)
## [1] 153   7

Berdasarkan hasil pemeriksaan tipe data, seluruh variabel yang digunakan dalam analisis memiliki tipe numerik. Hal ini sesuai dengan kebutuhan metode SVR karena variabel respons yang diprediksi berupa data kontinu.

Hasil pemeriksaan lanjutan juga menunjukkan bahwa tidak terdapat missing value pada ketujuh variabel analisis. Oleh karena itu, seluruh 153 observasi dapat dipertahankan dan digunakan dalam tahapan analisis selanjutnya tanpa perlu melakukan imputasi atau penghapusan observasi.

3.7 Statistik Deskriptif

# Ringkasan statistik
summary(data_svr)
##  gdp_per_capita   social_support  healthy_life_expectancy
##  Min.   :0.0000   Min.   :0.000   Min.   :0.0000         
##  1st Qu.:0.5780   1st Qu.:1.055   1st Qu.:0.5380         
##  Median :0.9600   Median :1.274   Median :0.7890         
##  Mean   :0.8999   Mean   :1.207   Mean   :0.7217         
##  3rd Qu.:1.2210   3rd Qu.:1.454   3rd Qu.:0.8810         
##  Max.   :1.6840   Max.   :1.624   Max.   :1.1410         
##  freedom_to_make_life_choices   generosity     perceptions_of_corruption
##  Min.   :0.0000               Min.   :0.0000   Min.   :0.000            
##  1st Qu.:0.3050               1st Qu.:0.1080   1st Qu.:0.047            
##  Median :0.4170               Median :0.1770   Median :0.085            
##  Mean   :0.3921               Mean   :0.1839   Mean   :0.111            
##  3rd Qu.:0.5080               3rd Qu.:0.2470   3rd Qu.:0.141            
##  Max.   :0.6310               Max.   :0.5660   Max.   :0.453            
##       hdi        
##  Min.   :0.3880  
##  1st Qu.:0.6170  
##  Median :0.7760  
##  Mean   :0.7455  
##  3rd Qu.:0.8890  
##  Max.   :0.9720
# Statistik deskriptif
stat_deskriptif <- data.frame(
  Variabel = names(data_svr),

  Mean = sapply(data_svr, mean),

  Median = sapply(data_svr, median),

  SD = sapply(data_svr, sd),

  Minimum = sapply(data_svr, min),

  Maksimum = sapply(data_svr, max)
)

# Membulatkan hasil
stat_deskriptif[, -1] <- round(
  stat_deskriptif[, -1],
  4
)

# Menampilkan tabel
kable(
  stat_deskriptif,
  caption = "Statistik Deskriptif Variabel Penelitian"
)
Statistik Deskriptif Variabel Penelitian
Variabel Mean Median SD Minimum Maksimum
gdp_per_capita gdp_per_capita 0.8999 0.960 0.3995 0.000 1.684
social_support social_support 1.2069 1.274 0.3016 0.000 1.624
healthy_life_expectancy healthy_life_expectancy 0.7217 0.789 0.2426 0.000 1.141
freedom_to_make_life_choices freedom_to_make_life_choices 0.3921 0.417 0.1444 0.000 0.631
generosity generosity 0.1839 0.177 0.0959 0.000 0.566
perceptions_of_corruption perceptions_of_corruption 0.1110 0.085 0.0950 0.000 0.453
hdi hdi 0.7455 0.776 0.1596 0.388 0.972

Secara keseluruhan, nilai rata-rata dan median pada beberapa variabel memiliki perbedaan yang relatif kecil. Namun, pada variabel tertentu, seperti GDP per capita, terdapat perbedaan antara rata-rata sebesar 0,8999 dan median sebesar 0,9600. Hal ini memberikan gambaran bahwa distribusi data tidak selalu simetris.

Nilai standar deviasi juga menunjukkan tingkat keragaman masing-masing variabel. Semakin besar standar deviasi, semakin besar variasi data terhadap nilai rata-ratanya. Pada data ini, GDP per capita memiliki standar deviasi sebesar 0,3995, sedangkan Generosity memiliki standar deviasi sebesar 0,0959.

3.8 Eksplorasi Data

3.8.1 Visualisasi Distribusi Data

par(mfrow = c(2, 4))

for (i in 1:ncol(data_svr)) {
  hist(
    data_svr[[i]],
    main = names(data_svr)[i],
    xlab = names(data_svr)[i],
    col = "skyblue",
    border = "white"
  )
}

par(mfrow = c(1, 1))

  1. GDP per Capita Sebagian besar data terkonsentrasi pada nilai 0,8–1,4. Distribusi cenderung mengarah ke nilai yang lebih tinggi.

  2. Social Support Data didominasi oleh nilai 1,0–1,5, dengan frekuensi tertinggi pada rentang 1,3–1,5. Hal ini menunjukkan bahwa sebagian besar negara memiliki tingkat dukungan sosial yang relatif tinggi.

  3. Healthy Life Expectancy Sebagian besar data terkonsentrasi pada rentang 0,7–1,0, dengan puncak frekuensi sekitar 0,8–0,9. Artinya, nilai harapan hidup sehat relatif banyak berada pada rentang tersebut.

  4. Freedom to Make Life Choices Distribusi data terkonsentrasi pada rentang 0,3–0,6, dengan frekuensi tertinggi sekitar 0,4–0,5. Hal ini menunjukkan bahwa sebagian besar negara memiliki nilai kebebasan memilih pada rentang tersebut.

  5. Generosity Sebagian besar data berada pada rentang 0,1–0,3, sedangkan nilai di atas 0,4 relatif jarang ditemukan. Distribusi cenderung menceng ke kanan (right-skewed).

  6. Perceptions of Corruption Data didominasi oleh nilai rendah, terutama pada rentang 0,0–0,2. Distribusi menceng ke kanan karena hanya sedikit pengamatan yang memiliki nilai relatif tinggi.

  7. HDI Sebagian besar nilai HDI terkonsentrasi pada rentang 0,7–1,0, dengan frekuensi tertinggi sekitar 0,9–1,0. Hal ini menunjukkan bahwa sebagian besar negara dalam dataset memiliki nilai HDI yang relatif tinggi.

3.8.2 Identifikasi Outlier

par(mfrow = c(2, 4))

for (i in 1:ncol(data_svr)) {
  boxplot(
    data_svr[[i]],
    main = names(data_svr)[i],
    ylab = "Nilai",
    col = "lightgreen"
  )
}

par(mfrow = c(1, 1))

  1. GDP per capita Tidak terdapat outlier karena seluruh data masih berada dalam batas whisker boxplot.

  2. Social support Terdapat beberapa outlier pada bagian bawah, yang menunjukkan adanya data dengan nilai social support lebih rendah dibandingkan sebagian besar pengamatan lainnya.

  3. Healthy life expectancy Terdapat satu outlier pada bagian bawah, yang menunjukkan adanya data dengan nilai harapan hidup sehat lebih rendah dibandingkan pengamatan lainnya.

  4. Freedom to make life choices Terdapat satu outlier pada bagian bawah, yang menunjukkan adanya data dengan tingkat kebebasan dalam menentukan pilihan hidup lebih rendah dibandingkan sebagian besar data.

  5. Generosity Terdapat dua outlier pada bagian atas, yang menunjukkan adanya data dengan tingkat generosity lebih tinggi dibandingkan pengamatan lainnya.

  6. Perceptions of corruption Terdapat beberapa outlier pada bagian atas, yang menunjukkan adanya data dengan nilai perceptions of corruption lebih tinggi dibandingkan sebagian besar pengamatan lainnya.

  7. HDI Tidak terdapat outlier karena seluruh data masih berada dalam batas whisker boxplot.

3.8.3 Analisis Korelasi

cor_matrix <- cor(
  data_svr,
  method = "pearson",
  use = "complete.obs"
)

# Menampilkan matriks korelasi
round(cor_matrix, 3)
##                              gdp_per_capita social_support
## gdp_per_capita                        1.000          0.755
## social_support                        0.755          1.000
## healthy_life_expectancy               0.834          0.721
## freedom_to_make_life_choices          0.384          0.450
## generosity                           -0.085         -0.052
## perceptions_of_corruption             0.300          0.184
## hdi                                   0.948          0.771
##                              healthy_life_expectancy
## gdp_per_capita                                 0.834
## social_support                                 0.721
## healthy_life_expectancy                        1.000
## freedom_to_make_life_choices                   0.394
## generosity                                    -0.034
## perceptions_of_corruption                      0.296
## hdi                                            0.903
##                              freedom_to_make_life_choices generosity
## gdp_per_capita                                      0.384     -0.085
## social_support                                      0.450     -0.052
## healthy_life_expectancy                             0.394     -0.034
## freedom_to_make_life_choices                        1.000      0.269
## generosity                                          0.269      1.000
## perceptions_of_corruption                           0.446      0.335
## hdi                                                 0.396     -0.083
##                              perceptions_of_corruption    hdi
## gdp_per_capita                                   0.300  0.948
## social_support                                   0.184  0.771
## healthy_life_expectancy                          0.296  0.903
## freedom_to_make_life_choices                     0.446  0.396
## generosity                                       0.335 -0.083
## perceptions_of_corruption                        1.000  0.287
## hdi                                              0.287  1.000
# Visualisasi
library(corrplot)

corrplot(
  cor_matrix,
  method = "color",
  type = "upper",
  addCoef.col = "black",
  tl.col = "black",
  tl.srt = 45,
  number.cex = 0.7
)

  1. GDP per capita Memiliki korelasi positif kuat dengan social support (0,76), healthy life expectancy (0,83), dan HDI (0,95). Artinya, semakin tinggi GDP per capita, cenderung semakin tinggi pula nilai ketiga variabel tersebut.

  2. Social support Memiliki korelasi positif kuat dengan healthy life expectancy (0,72) dan HDI (0,77). Artinya, semakin tinggi social support, cenderung semakin tinggi pula healthy life expectancy dan HDI.

  3. Healthy life expectancy Memiliki korelasi positif kuat dengan HDI (0,90). Artinya, semakin tinggi healthy life expectancy, cenderung semakin tinggi pula HDI.

  4. Freedom to make life choices Memiliki korelasi positif sedang dengan perceptions of corruption (0,45), HDI (0,40), dan healthy life expectancy (0,39).

  5. Generosity Memiliki korelasi yang cenderung lemah dengan variabel lainnya. Korelasi tertinggi terdapat pada perceptions of corruption, yaitu sebesar 0,34.

  6. Perceptions of corruption Memiliki korelasi positif sedang dengan freedom to make life choices (0,45), serta korelasi positif lemah dengan variabel lainnya.

  7. HDI Memiliki korelasi positif kuat dengan GDP per capita (0,95), healthy life expectancy (0,90), dan social support (0,77).

Sebagian besar variabel memiliki hubungan positif, artinya peningkatan suatu variabel cenderung diikuti peningkatan variabel lainnya. Korelasi paling kuat terdapat antara GDP per capita dan HDI sebesar 0,95. Sementara itu, korelasi negatif terlihat pada generosity dengan beberapa variabel, tetapi nilainya relatif lemah.

3.8.4 Scatterplot Prediktor terhadap HDI

par(mfrow = c(2, 3))

for (i in 1:ncol(X)) {
  plot(
    X[[i]],
    Y,
    main = paste(names(X)[i], "vs HDI"),
    xlab = names(X)[i],
    ylab = "HDI",
    pch = 19,
    col = "steelblue"
  )

  # Menambahkan garis regresi linear
  abline(
    lm(Y ~ X[[i]]),
    col = "red",
    lwd = 2
  )
}

par(mfrow = c(1, 1))
  1. GDP per capita vs HDI Terlihat hubungan positif yang kuat. Semakin tinggi GDP per capita, cenderung semakin tinggi pula HDI. Hal ini terlihat dari pola titik yang mengikuti arah garis regresi ke atas.

  2. Social support vs HDI Terlihat hubungan positif yang cukup kuat. Semakin tinggi social support, cenderung semakin tinggi pula HDI, meskipun titik data masih cukup menyebar.

  3. Healthy life expectancy vs HDI Terlihat hubungan positif yang kuat. Semakin tinggi healthy life expectancy, cenderung semakin tinggi pula HDI. Pola titik data cukup mengikuti garis regresi.

  4. Freedom to make life choices vs HDI Terlihat hubungan positif, tetapi cenderung lemah hingga sedang. Titik data cukup menyebar, sehingga hubungan antara kedua variabel tidak terlalu kuat.

  5. Generosity vs HDI Terlihat hubungan negatif yang sangat lemah. Hal ini ditunjukkan oleh garis regresi yang sedikit menurun, sehingga peningkatan generosity tidak menunjukkan hubungan yang kuat dengan HDI.

  6. Perceptions of corruption vs HDI Terlihat hubungan positif yang lemah. Meskipun garis regresi menunjukkan arah positif, titik data cukup menyebar dan tidak membentuk pola hubungan yang kuat.

Hubungan positif paling kuat terlihat pada GDP per capita dan healthy life expectancy terhadap HDI. Sementara itu, generosity menunjukkan hubungan negatif yang sangat lemah dengan HDI.

4 Support Vector Machine (SVM)

4.1 Persiapan Data Klasifikasi

Pada tahap ini, data dipersiapkan untuk keperluan klasifikasi menggunakan metode Support Vector Machine (SVM). Variabel HDI yang awalnya berbentuk numerik akan diubah menjadi dua kategori, yaitu HDI tinggi dan HDI rendah. Selain itu, dipilih enam variabel prediktor yang akan digunakan untuk membantu proses klasifikasi.

4.1.1 Membentuk Kategori HDI

Variabel HDI dikelompokkan menjadi dua kategori berdasarkan nilai 0,700. Negara dengan nilai HDI lebih besar atau sama dengan 0,700 dikategorikan sebagai HDI tinggi, sedangkan negara dengan nilai di bawah 0,700 dikategorikan sebagai HDI rendah. Pengelompokan ini dilakukan karena SVM yang digunakan pada tahap ini bertujuan untuk mengklasifikasikan negara ke dalam dua kelompok tersebut.

# Membentuk dataset khusus SVM Classification
data_svm <- data_svr %>%
  dplyr::select(
    hdi,
    GDP = gdp_per_capita,
    SocialSupport = social_support,
    HealthyLife = healthy_life_expectancy,
    Freedom = freedom_to_make_life_choices,
    Generosity = generosity,
    Corruption = perceptions_of_corruption
  ) %>%
  tidyr::drop_na() %>%
  dplyr::mutate(
    hdi_class = factor(
      dplyr::if_else(hdi >= 0.700, "Tinggi", "Rendah"),
      levels = c("Tinggi", "Rendah")
    )
  )

# Melihat struktur data
str(data_svm)
## 'data.frame':    153 obs. of  8 variables:
##  $ hdi          : num  0.948 0.962 0.97 0.972 0.955 0.97 0.959 0.938 0.939 0.93 ...
##  $ GDP          : num  1.34 1.38 1.49 1.38 1.4 ...
##  $ SocialSupport: num  1.59 1.57 1.58 1.62 1.52 ...
##  $ HealthyLife  : num  0.986 0.996 1.028 1.026 0.999 ...
##  $ Freedom      : num  0.596 0.592 0.603 0.591 0.557 0.572 0.574 0.585 0.584 0.532 ...
##  $ Generosity   : num  0.153 0.252 0.271 0.354 0.322 0.263 0.267 0.33 0.285 0.244 ...
##  $ Corruption   : num  0.393 0.41 0.341 0.118 0.298 0.343 0.373 0.38 0.308 0.226 ...
##  $ hdi_class    : Factor w/ 2 levels "Tinggi","Rendah": 1 1 1 1 1 1 1 1 1 1 ...
# Melihat beberapa observasi
head(data_svm)
##     hdi   GDP SocialSupport HealthyLife Freedom Generosity Corruption hdi_class
## 1 0.948 1.340         1.587       0.986   0.596      0.153      0.393    Tinggi
## 2 0.962 1.383         1.573       0.996   0.592      0.252      0.410    Tinggi
## 3 0.970 1.488         1.582       1.028   0.603      0.271      0.341    Tinggi
## 4 0.972 1.380         1.624       1.026   0.591      0.354      0.118    Tinggi
## 5 0.955 1.396         1.522       0.999   0.557      0.322      0.298    Tinggi
## 6 0.970 1.452         1.526       1.052   0.572      0.263      0.343    Tinggi
# Ukuran dataset
dim(data_svm)
## [1] 153   8

4.2 Exploratory Data Analysis

4.2.1 Visualisasi Distribusi Kategori HDI

# Distribusi kelas HDI
distribusi_hdi <- data_svm %>%
  dplyr::count(hdi_class) %>%
  dplyr::mutate(
    Persentase = round(n / sum(n) * 100, 2)
  )

knitr::kable(
  distribusi_hdi,
  caption = "Distribusi Kategori Human Development Index",
  col.names = c("Kategori HDI", "Jumlah", "Persentase (%)"),
  digits = 2
)
Distribusi Kategori Human Development Index
Kategori HDI Jumlah Persentase (%)
Tinggi 99 64.71
Rendah 54 35.29
# Visualisasi distribusi kelas HDI
ggplot(data_svm, aes(x = hdi_class, fill = hdi_class)) +
  geom_bar(width = 0.6, alpha = 0.85) +
  geom_text(
    stat = "count",
    aes(label = after_stat(count)),
    vjust = -0.5,
    size = 5
  ) +
  scale_fill_manual(
    values = c(
      "Tinggi" = "#E8B94F",
      "Rendah" = "#B9A6D3"
    )
  ) +
  labs(
    title = "Distribusi Kategori HDI",
    subtitle = "Jumlah negara berdasarkan kategori HDI",
    x = "Kategori HDI",
    y = "Jumlah Negara",
    fill = "Kategori"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold"),
    legend.position = "none"
  )

Visualisasi distribusi dilakukan untuk mengetahui jumlah negara yang termasuk dalam kategori HDI tinggi dan HDI rendah. Hasilnya menunjukkan bahwa terdapat 99 negara (64,71%) dengan kategori HDI tinggi dan 54 negara (35,29%) dengan kategori HDI rendah. Dengan demikian, jumlah negara pada kategori HDI tinggi lebih banyak dibandingkan kategori HDI rendah.

4.2.2 Hubungan GDP per capita dan Healthy Life Expectancy Berdasarkan kategori HDI

ggplot(
  data_svm,
  aes(
    x = GDP,
    y = HealthyLife,
    color = hdi_class
  )
) +
  geom_point(size = 3, alpha = 0.8) +
  scale_color_manual(
    values = c(
      "Tinggi" = "#D9A441",
      "Rendah" = "#9277B5"
    )
  ) +
  labs(
    title = "Hubungan GDP per Capita dan Healthy Life Expectancy",
    subtitle = "Berdasarkan kategori Human Development Index",
    x = "GDP per Capita",
    y = "Healthy Life Expectancy",
    color = "Kategori HDI"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold"),
    legend.position = "bottom"
  )

Berdasarkan scatter plot, terlihat bahwa terdapat hubungan positif antara GDP per Capita dan Healthy Life Expectancy. Artinya, semakin tinggi GDP per Capita suatu negara, cenderung semakin tinggi pula angka harapan hidup sehatnya. Selain itu, negara dengan kategori HDI tinggi (warna oranye) cenderung mengelompok pada nilai GDP per Capita dan Healthy Life Expectancy yang lebih tinggi. Sementara itu, negara dengan kategori HDI rendah (warna ungu) lebih banyak berada pada nilai yang lebih rendah. Hal ini menunjukkan bahwa GDP per Capita dan Healthy Life Expectancy memiliki pola hubungan yang searah dengan kategori HDI.

4.3 Pembagian Data Training dan Testing

Data dibagi menjadi dua bagian, yaitu 80% data training dan 20% data testing. Data training digunakan untuk membentuk model SVM, sedangkan data testing digunakan untuk menguji kemampuan model dalam mengklasifikasikan data yang belum pernah dipelajari sebelumnya.

Pembagian dilakukan menggunakan metode stratified sampling agar proporsi kategori HDI tinggi dan rendah tetap terjaga pada kedua kelompok data. Berdasarkan hasil pembagian, diperoleh 122 observasi untuk data training dan 31 observasi untuk data testing.

set.seed(123)

# Pembagian data training dan testing
split_svm <- rsample::initial_split(
  data_svm,
  prop = 0.80,
  strata = hdi_class
)

# Data training
train_raw <- rsample::training(split_svm)

# Data testing
test_raw <- rsample::testing(split_svm)

# Melihat ukuran data
cat("Jumlah data training:", nrow(train_raw), "\n")
## Jumlah data training: 122
cat("Jumlah data testing:", nrow(test_raw), "\n")
## Jumlah data testing: 31
# Distribusi kelas pada data training
table(train_raw$hdi_class)
## 
## Tinggi Rendah 
##     79     43
# Distribusi kelas pada data testing
table(test_raw$hdi_class)
## 
## Tinggi Rendah 
##     20     11

4.4 Standardisasi Data

Standardisasi dilakukan agar semua variabel prediktor memiliki skala yang sama sehingga tidak ada variabel yang lebih dominan karena perbedaan skala. Proses ini menggunakan metode Z-score berdasarkan rata-rata dan standar deviasi data training. Nilai tersebut juga digunakan pada data testing agar tidak terjadi data leakage.

# Daftar prediktor
predictors <- c(
  "GDP",
  "SocialSupport",
  "HealthyLife",
  "Freedom",
  "Generosity",
  "Corruption"
)

# Menghitung mean dan standar deviasi dari training
mean_train <- sapply(
  train_raw[, predictors],
  mean,
  na.rm = TRUE
)

sd_train <- sapply(
  train_raw[, predictors],
  sd,
  na.rm = TRUE
)

# Menghindari standar deviasi nol
sd_train[sd_train == 0 | is.na(sd_train)] <- 1

# Fungsi standardisasi
standardize_data <- function(data, mean_values, sd_values) {

  data_scaled <- data

  data_scaled[, predictors] <- scale(
    data[, predictors],
    center = mean_values,
    scale = sd_values
  )

  return(data_scaled)
}

# Standardisasi training dan testing
train_scaled <- standardize_data(
  train_raw,
  mean_train,
  sd_train
)

test_scaled <- standardize_data(
  test_raw,
  mean_train,
  sd_train
)

# Melihat hasil standardisasi
head(train_scaled)
##     hdi        GDP SocialSupport HealthyLife     Freedom Generosity  Corruption
## 1 0.645 -0.6707165     0.1026268  0.43385439  0.85379282 0.72873368 -0.30285599
## 2 0.544 -0.5816215    -1.0244735 -0.75609106 -0.43585990 0.44395565 -0.07435611
## 3 0.691 -1.0500067    -0.3417727 -0.01288281  0.06936488 0.55348566  0.45119362
## 4 0.698 -0.2354237     0.3763512 -0.47586500  0.52140810 2.08690579  0.71396848
## 5 0.588 -0.9074547    -0.9471867 -1.58458550  0.01618332 0.08250662 -0.77128075
## 6 0.628 -0.7496292    -1.0824387 -0.95509217  0.01618332 0.71778067 -0.73700576
##   hdi_class
## 1    Rendah
## 2    Rendah
## 3    Rendah
## 4    Rendah
## 5    Rendah
## 6    Rendah

4.5 Membangun Model SVM

Pada tahap ini, model SVM dibentuk menggunakan tiga jenis kernel, yaitu Linear, Polynomial, dan Radial Basis Function (RBF). Penggunaan beberapa kernel bertujuan untuk melihat jenis kernel yang paling sesuai dalam memisahkan data ke dalam kategori HDI tinggi dan HDI rendah.

# Fungsi untuk membangun model SVM
fit_svm <- function(
  train_data,
  predictors_used,
  kernel_type,
  cost_value = 1,
  degree_value = 2,
  gamma_value = 1
) {

  x_train <- as.matrix(
    train_data[, predictors_used]
  )

  y_train <- train_data$hdi_class

  # Menentukan kernel
  if (kernel_type == "Linear") {

    kernel_function <- kernlab::vanilladot()

  } else if (kernel_type == "Polynomial") {

    kernel_function <- kernlab::polydot(
      degree = degree_value,
      scale = 1,
      offset = 1
    )

  } else if (kernel_type == "RBF") {

    kernel_function <- kernlab::rbfdot(
      sigma = gamma_value
    )

  }

  # Membentuk model SVM
  model <- kernlab::ksvm(
    x = x_train,
    y = y_train,
    type = "C-svc",
    kernel = kernel_function,
    C = cost_value,
    prob.model = TRUE,
    scaled = FALSE
  )

  return(model)
}

4.5.1 Model SVM Linear

Kernel Linear digunakan untuk membentuk batas pemisah antarkelas dalam bentuk garis lurus atau bidang linear. Kernel ini sesuai digunakan ketika hubungan antarvariabel dan kelas dapat dipisahkan secara linear.

svm_linear <- fit_svm(
  train_scaled,
  predictors,
  kernel_type = "Linear",
  cost_value = 1
)

svm_linear
## Support Vector Machine object of class "ksvm" 
## 
## SV type: C-svc  (classification) 
##  parameter : cost C = 1 
## 
## Linear (vanilla) kernel function. 
## 
## Number of Support Vectors : 16 
## 
## Objective Function Value : -10.2229 
## Training error : 0.032787 
## Probability model included.

4.5.2 Model SVM Polinomial

Kernel Polynomial digunakan untuk membentuk batas pemisah yang lebih fleksibel dengan mempertimbangkan hubungan nonlinier antarvariabel. Pada pemodelan ini digunakan nilai degree sebesar 2, yang menunjukkan tingkat pangkat dalam fungsi kernel polynomial.

svm_poly <- fit_svm(
  train_scaled,
  predictors,
  kernel_type = "Polynomial",
  cost_value = 1,
  degree_value = 2
)

svm_poly
## Support Vector Machine object of class "ksvm" 
## 
## SV type: C-svc  (classification) 
##  parameter : cost C = 1 
## 
## Polynomial kernel function. 
##  Hyperparameters : degree =  2  scale =  1  offset =  1 
## 
## Number of Support Vectors : 17 
## 
## Objective Function Value : -4.8116 
## Training error : 0.008197 
## Probability model included.

4.5.3 Model SVM RBF

Kernel RBF (Radial Basis Function) digunakan untuk menangani hubungan nonlinier dengan membentuk batas pemisah yang lebih fleksibel. Kernel ini memanfaatkan parameter gamma untuk mengatur seberapa jauh pengaruh suatu observasi terhadap observasi lainnya.

svm_rbf <- fit_svm(
  train_scaled,
  predictors,
  kernel_type = "RBF",
  cost_value = 1,
  gamma_value = 1
)

svm_rbf
## Support Vector Machine object of class "ksvm" 
## 
## SV type: C-svc  (classification) 
##  parameter : cost C = 1 
## 
## Gaussian Radial Basis kernel function. 
##  Hyperparameter : sigma =  1 
## 
## Number of Support Vectors : 96 
## 
## Objective Function Value : -26.9863 
## Training error : 0.008197 
## Probability model included.

4.6 Evaluasi Awal Model

# Fungsi mengambil probabilitas kelas Tinggi
get_probability <- function(model, new_data) {

  probability <- predict(
    model,
    as.matrix(new_data),
    type = "probabilities"
  )

  probability <- as.matrix(probability)

  return(probability[, "Tinggi"])
}

# Fungsi evaluasi model
evaluate_svm <- function(model, test_data, predictors_used) {

  # Prediksi kelas
  prediction <- predict(
    model,
    as.matrix(test_data[, predictors_used]),
    type = "response"
  )

  prediction <- factor(
    prediction,
    levels = levels(test_data$hdi_class)
  )

  # Prediksi probabilitas kelas Tinggi
  probability <- get_probability(
    model,
    test_data[, predictors_used]
  )

  # Menghitung metrik evaluasi
  hasil <- tibble::tibble(
    Accuracy = yardstick::accuracy_vec(
      truth = test_data$hdi_class,
      estimate = prediction
    ),

    AUC = yardstick::roc_auc_vec(
      truth = test_data$hdi_class,
      estimate = probability,
      event_level = "first"
    ),

    Sensitivity = yardstick::sens_vec(
      truth = test_data$hdi_class,
      estimate = prediction,
      event_level = "first"
    ),

    Specificity = yardstick::spec_vec(
      truth = test_data$hdi_class,
      estimate = prediction,
      event_level = "first"
    ),

    Precision = yardstick::precision_vec(
      truth = test_data$hdi_class,
      estimate = prediction,
      event_level = "first"
    ),

    F1_Score = yardstick::f_meas_vec(
      truth = test_data$hdi_class,
      estimate = prediction,
      event_level = "first"
    )
  )

  return(hasil)
}

# Evaluasi ketiga model
hasil_linear <- evaluate_svm(
  svm_linear,
  test_scaled,
  predictors
)

hasil_poly <- evaluate_svm(
  svm_poly,
  test_scaled,
  predictors
)

hasil_rbf <- evaluate_svm(
  svm_rbf,
  test_scaled,
  predictors
)

# Menggabungkan hasil evaluasi
hasil_baseline <- dplyr::bind_rows(
  Linear = hasil_linear,
  Polynomial = hasil_poly,
  RBF = hasil_rbf,
  .id = "Kernel"
)

knitr::kable(
  hasil_baseline,
  digits = 4,
  caption = "Perbandingan Performa Awal SVM Berdasarkan Kernel"
)
Perbandingan Performa Awal SVM Berdasarkan Kernel
Kernel Accuracy AUC Sensitivity Specificity Precision F1_Score
Linear 0.8710 0.9591 0.90 0.8182 0.9000 0.9000
Polynomial 0.8065 0.8682 0.85 0.7273 0.8500 0.8500
RBF 0.8065 0.8727 0.95 0.5455 0.7917 0.8636

Hasil evaluasi awal menunjukkan bahwa ketiga model memiliki nilai Accuracy yang cukup baik, dengan model Linear memiliki nilai accuracy tertinggi yaitu 0.8710, disusul oleh model polynomial dan RBF yang memiliki accuracy sama yaitu 0.8065.

4.7 Kernel Battle

Pada tahap ini dilakukan pencarian kombinasi parameter terbaik dari masing-masing kernel menggunakan metode 5-Fold dan 10-Fold Cross Validation. Proses ini bertujuan untuk memperoleh kombinasi parameter yang memberikan performa model paling baik dan stabil.

Parameter yang diuji meliputi:

  1. Cost (C): Mengatur seberapa besar penalti terhadap kesalahan klasifikasi. Nilai C yang lebih besar memberikan penalti lebih tinggi terhadap kesalahan.

  2. Degree: Menentukan tingkat pangkat pada kernel Polynomial.

  3. Gamma: Mengatur jangkauan pengaruh setiap observasi pada kernel RBF.

Pengujian dilakukan menggunakan beberapa kombinasi parameter yang telah ditentukan. Setiap kombinasi dievaluasi melalui lima pembagian data, kemudian hasil Accuracy dan AUC dirata-ratakan untuk menentukan kombinasi terbaik.

# Fungsi Cross-Validation SVM
cv_svm <- function(
  data,
  predictors_used,
  kernel_type,
  cost_value,
  degree_value = 2,
  gamma_value = 0.1,
  k = 5,
  seed = 2026
) {

  set.seed(seed)

  # Membuat fold secara stratified
  folds <- caret::createFolds(
    y = data$hdi_class,
    k = k,
    list = TRUE
  )

  # Menyiapkan penyimpanan hasil
  hasil_fold <- data.frame(
    Accuracy = numeric(length(folds)),
    AUC = numeric(length(folds))
  )

  # Proses setiap fold
  for (i in seq_along(folds)) {

    # Membagi data training dan validasi
    index_valid <- folds[[i]]

    fold_train <- data[-index_valid, ]
    fold_valid <- data[index_valid, ]

    # Standardisasi berdasarkan training fold
    scaled <- scale_fold(
      train_data = fold_train,
      valid_data = fold_valid,
      predictors_used = predictors_used
    )

    # Membentuk kernel
    kernel_function <- make_kernel(
      kernel_type = kernel_type,
      degree_value = degree_value,
      gamma_value = gamma_value
    )

    # Membentuk model SVM
    model <- kernlab::ksvm(
      x = as.matrix(
        scaled$train[, predictors_used]
      ),
      y = scaled$train$hdi_class,
      type = "C-svc",
      kernel = kernel_function,
      C = cost_value,
      prob.model = TRUE,
      scaled = FALSE
    )

    # Prediksi kelas
    pred <- predict(
      model,
      as.matrix(
        scaled$valid[, predictors_used]
      ),
      type = "response"
    )

    pred <- factor(
      pred,
      levels = levels(scaled$valid$hdi_class)
    )

    # Prediksi probabilitas kelas Tinggi
    prob <- predict(
      model,
      as.matrix(
        scaled$valid[, predictors_used]
      ),
      type = "probabilities"
    )

    prob <- as.matrix(prob)

    prob_tinggi <- prob[, "Tinggi"]

    # Menghitung Accuracy
    hasil_fold$Accuracy[i] <- yardstick::accuracy_vec(
      truth = scaled$valid$hdi_class,
      estimate = pred
    )

    # Menghitung AUC
    hasil_fold$AUC[i] <- yardstick::roc_auc_vec(
      truth = scaled$valid$hdi_class,
      estimate = prob_tinggi,
      event_level = "first"
    )
  }

  # Menghitung rata-rata dan standar deviasi
  hasil <- data.frame(
    Mean_Accuracy = mean(hasil_fold$Accuracy),
    SD_Accuracy = sd(hasil_fold$Accuracy),
    Mean_AUC = mean(hasil_fold$AUC),
    SD_AUC = sd(hasil_fold$AUC)
  )

  return(hasil)
}

# ============================================================
# MENENTUKAN GRID HYPERPARAMETER SVM
# ============================================================

# Kernel Linear
grid_linear <- expand.grid(
  Kernel = "Linear",
  C = c(0.01, 0.1, 1, 10, 100),
  Degree = NA_real_,
  Gamma = NA_real_
)

# Kernel Polynomial
grid_poly <- expand.grid(
  Kernel = "Polynomial",
  C = c(0.1, 1, 10),
  Degree = c(2, 3),
  Gamma = NA_real_
)

# Kernel RBF
grid_rbf <- expand.grid(
  Kernel = "RBF",
  C = c(0.1, 1, 10),
  Degree = NA_real_,
  Gamma = c(0.01, 0.1, 1)
)


# Fungsi untuk menentukan jenis kernel SVM
make_kernel <- function(
  kernel_type,
  degree_value = 2,
  gamma_value = 0.1
) {

  if (kernel_type == "Linear") {

    kernel <- kernlab::vanilladot()

  } else if (kernel_type == "Polynomial") {

    kernel <- kernlab::polydot(
      degree = degree_value,
      scale = 1,
      offset = 1
    )

  } else if (kernel_type == "RBF") {

    kernel <- kernlab::rbfdot(
      sigma = gamma_value
    )

  } else {

    stop("Jenis kernel tidak dikenali.")

  }

  return(kernel)
}


# Fungsi Standardisasi Data pada Setiap Fold
scale_fold <- function(train_data, valid_data, predictors_used) {

  # Menghitung mean dan standar deviasi dari data training
  mean_train <- sapply(
    train_data[, predictors_used, drop = FALSE],
    mean,
    na.rm = TRUE
  )

  sd_train <- sapply(
    train_data[, predictors_used, drop = FALSE],
    sd,
    na.rm = TRUE
  )

  # Menghindari pembagian dengan nol
  sd_train[sd_train == 0] <- 1

  # Standardisasi data training
  train_scaled <- train_data
  train_scaled[, predictors_used] <- scale(
    train_data[, predictors_used, drop = FALSE],
    center = mean_train,
    scale = sd_train
  )

  # Standardisasi data validasi menggunakan mean dan SD training
  valid_scaled <- valid_data
  valid_scaled[, predictors_used] <- scale(
    valid_data[, predictors_used, drop = FALSE],
    center = mean_train,
    scale = sd_train
  )

  # Mengembalikan kedua data yang sudah distandardisasi
  return(list(
    train = train_scaled,
    valid = valid_scaled
  ))
}


# Menggabungkan seluruh kombinasi
grid_svm <- dplyr::bind_rows(
  grid_linear,
  grid_poly,
  grid_rbf
)

# Menampilkan jumlah kombinasi
cat(
  "Jumlah kombinasi hyperparameter:",
  nrow(grid_svm)
)
## Jumlah kombinasi hyperparameter: 20
# Menampilkan grid hyperparameter
knitr::kable(
  grid_svm,
  caption = "Grid Hyperparameter SVM",
  digits = 3
)
Grid Hyperparameter SVM
Kernel C Degree Gamma
Linear 1e-02 NA NA
Linear 1e-01 NA NA
Linear 1e+00 NA NA
Linear 1e+01 NA NA
Linear 1e+02 NA NA
Polynomial 1e-01 2 NA
Polynomial 1e+00 2 NA
Polynomial 1e+01 2 NA
Polynomial 1e-01 3 NA
Polynomial 1e+00 3 NA
Polynomial 1e+01 3 NA
RBF 1e-01 NA 0.01
RBF 1e+00 NA 0.01
RBF 1e+01 NA 0.01
RBF 1e-01 NA 0.10
RBF 1e+00 NA 0.10
RBF 1e+01 NA 0.10
RBF 1e-01 NA 1.00
RBF 1e+00 NA 1.00
RBF 1e+01 NA 1.00
# Menyiapkan hasil 5-fold
hasil_kernel_5 <- grid_svm

hasil_kernel_5$Mean_Accuracy <- NA_real_
hasil_kernel_5$SD_Accuracy <- NA_real_
hasil_kernel_5$Mean_AUC <- NA_real_
hasil_kernel_5$SD_AUC <- NA_real_

# Menguji seluruh kombinasi parameter
for (i in seq_len(nrow(hasil_kernel_5))) {

  hasil_cv <- cv_svm(
    data = train_raw,
    predictors_used = predictors,
    kernel_type = hasil_kernel_5$Kernel[i],
    cost_value = hasil_kernel_5$C[i],
    degree_value = ifelse(
      is.na(hasil_kernel_5$Degree[i]),
      2,
      hasil_kernel_5$Degree[i]
    ),
    gamma_value = ifelse(
      is.na(hasil_kernel_5$Gamma[i]),
      0.1,
      hasil_kernel_5$Gamma[i]
    ),
    k = 5,
    seed = 2026
  )

  hasil_kernel_5$Mean_Accuracy[i] <-
    hasil_cv$Mean_Accuracy

  hasil_kernel_5$SD_Accuracy[i] <-
    hasil_cv$SD_Accuracy

  hasil_kernel_5$Mean_AUC[i] <-
    hasil_cv$Mean_AUC

  hasil_kernel_5$SD_AUC[i] <-
    hasil_cv$SD_AUC
}

# Mengurutkan hasil berdasarkan AUC
hasil_kernel_5 <- hasil_kernel_5 %>%
  dplyr::arrange(
    dplyr::desc(Mean_AUC),
    dplyr::desc(Mean_Accuracy)
  )

# Menampilkan hasil
knitr::kable(
  hasil_kernel_5,
  digits = 4,
  caption = "Hasil Optimasi Hyperparameter SVM dengan 5-Fold CV"
)
Hasil Optimasi Hyperparameter SVM dengan 5-Fold CV
Kernel C Degree Gamma Mean_Accuracy SD_Accuracy Mean_AUC SD_AUC
RBF 1e+00 NA 0.01 0.9663 0.0544 0.9967 0.0075
RBF 1e+00 NA 0.10 0.9663 0.0544 0.9967 0.0075
Linear 1e-01 NA NA 0.9583 0.0510 0.9967 0.0075
RBF 1e+01 NA 0.01 0.9583 0.0510 0.9967 0.0075
Linear 1e+02 NA NA 0.9499 0.0360 0.9967 0.0075
Linear 1e-02 NA NA 0.9416 0.0483 0.9967 0.0075
Linear 1e+00 NA NA 0.9416 0.0483 0.9967 0.0075
RBF 1e-01 NA 0.10 0.9416 0.0483 0.9967 0.0075
Linear 1e+01 NA NA 0.9249 0.0558 0.9967 0.0075
RBF 1e-01 NA 0.01 0.6478 0.0118 0.9967 0.0075
Polynomial 1e+00 3 NA 0.9423 0.0377 0.9940 0.0060
Polynomial 1e+01 3 NA 0.9423 0.0377 0.9940 0.0060
Polynomial 1e-01 2 NA 0.9583 0.0510 0.9939 0.0084
RBF 1e+01 NA 0.10 0.9423 0.0471 0.9925 0.0077
Polynomial 1e-01 3 NA 0.9343 0.0372 0.9897 0.0097
RBF 1e-01 NA 1.00 0.6478 0.0118 0.9815 0.0192
Polynomial 1e+00 2 NA 0.9023 0.0453 0.9770 0.0240
RBF 1e+00 NA 1.00 0.9176 0.0501 0.9769 0.0220
RBF 1e+01 NA 1.00 0.9016 0.0458 0.9684 0.0276
Polynomial 1e+01 2 NA 0.9106 0.0427 0.9624 0.0385
# Menyiapkan hasil 10-fold
hasil_kernel_10 <- grid_svm

hasil_kernel_10$Mean_Accuracy <- NA_real_
hasil_kernel_10$SD_Accuracy <- NA_real_
hasil_kernel_10$Mean_AUC <- NA_real_
hasil_kernel_10$SD_AUC <- NA_real_

# Menguji seluruh kombinasi parameter
for (i in seq_len(nrow(hasil_kernel_10))) {

  hasil_cv <- cv_svm(
    data = train_raw,
    predictors_used = predictors,
    kernel_type = hasil_kernel_10$Kernel[i],
    cost_value = hasil_kernel_10$C[i],
    degree_value = ifelse(
      is.na(hasil_kernel_10$Degree[i]),
      2,
      hasil_kernel_10$Degree[i]
    ),
    gamma_value = ifelse(
      is.na(hasil_kernel_10$Gamma[i]),
      0.1,
      hasil_kernel_10$Gamma[i]
    ),
    k = 10,
    seed = 2026
  )

  hasil_kernel_10$Mean_Accuracy[i] <-
    hasil_cv$Mean_Accuracy

  hasil_kernel_10$SD_Accuracy[i] <-
    hasil_cv$SD_Accuracy

  hasil_kernel_10$Mean_AUC[i] <-
    hasil_cv$Mean_AUC

  hasil_kernel_10$SD_AUC[i] <-
    hasil_cv$SD_AUC
}

# Mengurutkan hasil berdasarkan AUC
hasil_kernel_10 <- hasil_kernel_10 %>%
  dplyr::arrange(
    dplyr::desc(Mean_AUC),
    dplyr::desc(Mean_Accuracy)
  )

# Menampilkan hasil
knitr::kable(
  hasil_kernel_10,
  digits = 4,
  caption = "Hasil Optimasi Hyperparameter SVM dengan 10-Fold CV"
)
Hasil Optimasi Hyperparameter SVM dengan 10-Fold CV
Kernel C Degree Gamma Mean_Accuracy SD_Accuracy Mean_AUC SD_AUC
RBF 1e+00 NA 0.01 0.9658 0.0617 0.9964 0.0113
RBF 1e-01 NA 0.10 0.9581 0.0617 0.9964 0.0113
Linear 1e+00 NA NA 0.9491 0.0614 0.9950 0.0158
RBF 1e-01 NA 0.01 0.6483 0.0245 0.9939 0.0130
Linear 1e-02 NA NA 0.9498 0.0611 0.9929 0.0226
Linear 1e+02 NA NA 0.9422 0.0540 0.9912 0.0205
Linear 1e+01 NA NA 0.9331 0.0665 0.9912 0.0142
RBF 1e+00 NA 0.10 0.9567 0.0872 0.9904 0.0231
Linear 1e-01 NA NA 0.9575 0.0622 0.9879 0.0261
RBF 1e+01 NA 0.01 0.9575 0.0622 0.9879 0.0261
Polynomial 1e-01 2 NA 0.9498 0.0611 0.9879 0.0261
RBF 1e+01 NA 0.10 0.9247 0.0869 0.9879 0.0261
Polynomial 1e+00 2 NA 0.9009 0.0889 0.9816 0.0262
Polynomial 1e-01 3 NA 0.9408 0.0593 0.9789 0.0318
Polynomial 1e+00 3 NA 0.9241 0.0752 0.9785 0.0256
Polynomial 1e+01 3 NA 0.9241 0.0752 0.9785 0.0256
RBF 1e+00 NA 1.00 0.9100 0.0912 0.9754 0.0574
RBF 1e-01 NA 1.00 0.6483 0.0245 0.9754 0.0574
Polynomial 1e+01 2 NA 0.9017 0.0761 0.9685 0.0343
RBF 1e+01 NA 1.00 0.8934 0.1041 0.9629 0.0593
# Mengambil parameter terbaik dari 5-fold
best_5 <- hasil_kernel_5 %>%
  dplyr::slice_max(
    order_by = Mean_AUC,
    n = 1,
    with_ties = FALSE
  ) %>%
  dplyr::mutate(
    CV = "5-Fold"
  )

# Mengambil parameter terbaik dari 10-fold
best_10 <- hasil_kernel_10 %>%
  dplyr::slice_max(
    order_by = Mean_AUC,
    n = 1,
    with_ties = FALSE
  ) %>%
  dplyr::mutate(
    CV = "10-Fold"
  )

# Menggabungkan hasil terbaik
perbandingan_best <- dplyr::bind_rows(
  best_5,
  best_10
) %>%
  dplyr::select(
    CV,
    Kernel,
    C,
    Degree,
    Gamma,
    Mean_Accuracy,
    SD_Accuracy,
    Mean_AUC,
    SD_AUC
  )

knitr::kable(
  perbandingan_best,
  digits = 4,
  caption = "Perbandingan Kombinasi Parameter Terbaik SVM"
)
Perbandingan Kombinasi Parameter Terbaik SVM
CV Kernel C Degree Gamma Mean_Accuracy SD_Accuracy Mean_AUC SD_AUC
5-Fold RBF 1 NA 0.01 0.9663 0.0544 0.9967 0.0075
10-Fold RBF 1 NA 0.01 0.9658 0.0617 0.9964 0.0113

Berdasarkan hasil optimasi hyperparameter menggunakan 5-Fold Cross-Validation, kombinasi terbaik diperoleh pada kernel Radial Basis Function (RBF) dengan nilai Cost (C) sebesar 1 dan Gamma sebesar 0,01. Kombinasi tersebut menghasilkan Mean Accuracy sebesar 96,63% dan Mean AUC sebesar 99,67%. Hasil ini menunjukkan bahwa model memiliki kemampuan klasifikasi yang sangat baik dalam mengklasifikasikan negara ke dalam kategori HDI Tinggi dan HDI Rendah, serta kemampuan yang sangat tinggi dalam membedakan kedua kelas tersebut.

Hasil yang relatif konsisten diperoleh pada 10-Fold Cross-Validation dengan kombinasi hyperparameter terbaik yang sama, yaitu kernel RBF, C sebesar 1, dan Gamma sebesar 0,01. Model menghasilkan Mean Accuracy sebesar 96,58% dan Mean AUC sebesar 99,64%. Perbedaan performa antara kedua skema Cross-Validation relatif kecil, sehingga kombinasi hyperparameter tersebut dipilih sebagai model terbaik untuk tahap pemodelan SVM selanjutnya.

4.8 Membentuk Model SVM Terbaik

# Menggabungkan seluruh hasil 5-fold dan 10-fold
hasil_kernel_battle <- dplyr::bind_rows(
  hasil_kernel_5 %>%
    dplyr::mutate(CV = "5-Fold"),

  hasil_kernel_10 %>%
    dplyr::mutate(CV = "10-Fold")
)

# Mengambil kombinasi parameter terbaik
best_parameter <- hasil_kernel_battle %>%
  dplyr::slice_max(
    order_by = Mean_AUC,
    n = 1,
    with_ties = FALSE
  )

# Menampilkan parameter terbaik
knitr::kable(
  best_parameter,
  digits = 4,
  caption = "Kombinasi Hyperparameter SVM Terbaik"
)
Kombinasi Hyperparameter SVM Terbaik
Kernel C Degree Gamma Mean_Accuracy SD_Accuracy Mean_AUC SD_AUC CV
RBF 1 NA 0.01 0.9663 0.0544 0.9967 0.0075 5-Fold

4.9 Membentuk Model SVM Final

# Membentuk model SVM final berdasarkan parameter terbaik
svm_best <- fit_svm(
  train_data = train_scaled,
  predictors_used = predictors,
  kernel_type = as.character(best_parameter$Kernel[1]),
  cost_value = best_parameter$C[1],
  degree_value = ifelse(
    is.na(best_parameter$Degree[1]),
    2,
    best_parameter$Degree[1]
  ),
  gamma_value = ifelse(
    is.na(best_parameter$Gamma[1]),
    0.1,
    best_parameter$Gamma[1]
  )
)

# Ringkasan model
svm_best
## Support Vector Machine object of class "ksvm" 
## 
## SV type: C-svc  (classification) 
##  parameter : cost C = 1 
## 
## Gaussian Radial Basis kernel function. 
##  Hyperparameter : sigma =  0.01 
## 
## Number of Support Vectors : 54 
## 
## Objective Function Value : -38.4921 
## Training error : 0.032787 
## Probability model included.

4.10 Evaluasi Model Final pada Data Testing

# Evaluasi model SVM terbaik
hasil_final_svm <- evaluate_svm(
  model = svm_best,
  test_data = test_scaled,
  predictors_used = predictors
)

# Menampilkan hasil evaluasi
knitr::kable(
  hasil_final_svm,
  digits = 4,
  caption = "Evaluasi Model SVM Terbaik pada Data Testing"
)
Evaluasi Model SVM Terbaik pada Data Testing
Accuracy AUC Sensitivity Specificity Precision F1_Score
0.9032 0.9545 0.95 0.8182 0.9048 0.9268

4.11 Kesimpulan

Berdasarkan hasil analisis menggunakan metode Support Vector Machine (SVM), data HDI dari 153 negara dikelompokkan menjadi dua kategori, yaitu HDI tinggi dan HDI rendah. Hasil eksplorasi menunjukkan bahwa terdapat 99 negara (64,71%) dalam kategori HDI tinggi dan 54 negara (35,29%) dalam kategori HDI rendah. Pemodelan dilakukan menggunakan enam variabel prediktor, yaitu GDP, Social Support, Healthy Life Expectancy, Freedom, Generosity, dan Corruption, dengan pembagian data sebesar 80% untuk training dan 20% untuk testing.

Pada evaluasi awal, kernel Linear menunjukkan performa terbaik dengan Accuracy sebesar 87,10%. Namun, setelah dilakukan optimasi hyperparameter menggunakan 5-Fold dan 10-Fold Cross-Validation, kernel Radial Basis Function (RBF) dengan nilai Cost sebesar 1 dan Gamma sebesar 0,01 memberikan hasil terbaik pada kedua skema. Kombinasi tersebut menghasilkan Mean Accuracy sebesar 96,63% pada 5-Fold dan 96,58% pada 10-Fold, serta Mean AUC masing-masing sebesar 99,67% dan 99,64%. Konsistensi hasil tersebut menunjukkan bahwa kombinasi hyperparameter yang diperoleh relatif stabil pada kedua skema validasi.

4.12 Setelah model terbaik diterapkan pada data testing, diperoleh Accuracy sebesar 90,32%, AUC sebesar 95,45%, Sensitivity sebesar 95%, Specificity sebesar 81,82%, Precision sebesar 90,48%, dan F1-Score sebesar 92,68%. Hasil ini menunjukkan bahwa model SVM RBF memiliki kemampuan klasifikasi yang baik, terutama dalam mengenali negara dengan kategori HDI tinggi. Dengan demikian, SVM menggunakan kernel RBF dengan Cost = 1 dan Gamma = 0,01 dipilih sebagai model final karena mampu memberikan performa klasifikasi yang baik dalam membedakan negara berdasarkan kategori HDI.

5 Support Vector Regression (SVR)

5.1 Pemodelan

5.1.1 Menentukan Formula Model

# Formula enam prediktor
vars_x <- c(
  "gdp_per_capita",
  "social_support",
  "healthy_life_expectancy",
  "freedom_to_make_life_choices",
  "generosity",
  "perceptions_of_corruption"
)

form6 <- reformulate(
  vars_x,
  response = "hdi"
)

form6
## hdi ~ gdp_per_capita + social_support + healthy_life_expectancy + 
##     freedom_to_make_life_choices + generosity + perceptions_of_corruption

Formula model digunakan untuk mendefinisikan HDI sebagai variabel respons yang diprediksi berdasarkan enam variabel prediktor. Dengan demikian, seluruh indikator Happiness dimasukkan secara bersamaan ke dalam model SVR.

5.1.2 Membagi Data Training dan Testing

set.seed(123)

# Menentukan jumlah data training
n <- nrow(data_svr)

# Membagi data dengan promosi 80 : 20
index_train <- sample(
  seq_len(n),
  size = floor(0.8 * n)
)

# Membentuk data training dan testing
train_hdi <- data_svr[index_train, ]
test_hdi <- data_svr[-index_train, ]

# Memeriksa jumlah observasi
cat("Jumlah data training:", nrow(train_hdi), "\n")
## Jumlah data training: 122
cat("Jumlah data testing:", nrow(test_hdi), "\n")
## Jumlah data testing: 31

Dataset dibagi menjadi dua bagian dengan proporsi 80:20. Sebanyak 122 observasi digunakan sebagai data training untuk membentuk model, sedangkan 31 observasi digunakan sebagai data testing untuk mengevaluasi kemampuan model dalam memprediksi data yang belum digunakan pada proses pelatihan.

Pembagian ini dilakukan agar evaluasi model dapat memberikan gambaran mengenai kemampuan generalisasi model terhadap data baru.

5.1.3 Membentuk Model SVR Linear

fit_linear6 <- svm(
  hdi ~ gdp_per_capita + social_support +
    healthy_life_expectancy + freedom_to_make_life_choices +
    generosity + perceptions_of_corruption,
  data = train_hdi,
  type = "eps-regression",
  kernel = "linear",
  cost = 1,
  epsilon = 0.05,
  scale = TRUE
)

# Melihat hasil model
summary(fit_linear6)
## 
## Call:
## svm(formula = hdi ~ gdp_per_capita + social_support + healthy_life_expectancy + 
##     freedom_to_make_life_choices + generosity + perceptions_of_corruption, 
##     data = train_hdi, type = "eps-regression", kernel = "linear", 
##     cost = 1, epsilon = 0.05, scale = TRUE)
## 
## 
## Parameters:
##    SVM-Type:  eps-regression 
##  SVM-Kernel:  linear 
##        cost:  1 
##       gamma:  0.1666667 
##     epsilon:  0.05 
## 
## 
## Number of Support Vectors:  102

Model SVR Linear dibangun menggunakan kernel linear dengan parameter cost = 1 dan epsilon = 0.05. Kernel linear digunakan untuk membentuk model berdasarkan pola hubungan yang bersifat linear dalam ruang fitur.

Berdasarkan hasil ringkasan model, diperoleh jumlah support vectors sebanyak 102. Support vectors merupakan observasi yang berperan dalam menentukan fungsi regresi SVR. Dengan kata lain, observasi tersebut menjadi bagian penting dalam pembentukan model.

5.1.4 Membangun SVR Radial

fit_radial6 <- svm(
  hdi ~ gdp_per_capita + social_support +
    healthy_life_expectancy + freedom_to_make_life_choices +
    generosity + perceptions_of_corruption,
  data = train_hdi,
  type = "eps-regression",
  kernel = "radial",
  cost = 1,
  epsilon = 0.05,
  scale = TRUE
)

# Melihat hasil model
summary(fit_radial6)
## 
## Call:
## svm(formula = hdi ~ gdp_per_capita + social_support + healthy_life_expectancy + 
##     freedom_to_make_life_choices + generosity + perceptions_of_corruption, 
##     data = train_hdi, type = "eps-regression", kernel = "radial", 
##     cost = 1, epsilon = 0.05, scale = TRUE)
## 
## 
## Parameters:
##    SVM-Type:  eps-regression 
##  SVM-Kernel:  radial 
##        cost:  1 
##       gamma:  0.1666667 
##     epsilon:  0.05 
## 
## 
## Number of Support Vectors:  100

Model SVR Radial dibangun menggunakan kernel radial atau Radial Basis Function (RBF), dengan nilai parameter cost = 1 dan epsilon = 0.05.

Berbeda dengan kernel linear, kernel radial memungkinkan model menangkap pola hubungan nonlinier antara variabel prediktor dan respons. Berdasarkan hasil ringkasan model, diperoleh jumlah support vectors sebanyak 100 observasi.

Jumlah support vectors pada kedua model tidak secara langsung menunjukkan model mana yang lebih baik. Kinerja keduanya perlu dibandingkan melalui hasil prediksi dan metrik evaluasi pada data testing.

5.2 Evaluasi Model Baseline

5.2.1 Membuat Fungsi Evaluasi

Sebelum melakukan prediksi, terlebih dahulu dibuat fungsi evaluasi untuk mengukur kinerja model SVR. Evaluasi dilakukan menggunakan tiga metrik, yaitu Mean Absolute Error (MAE), Root Mean Squared Error (RMSE), dan Coefficient of Determination (\(R^2\)). Ketiga metrik tersebut digunakan untuk mengetahui besarnya kesalahan prediksi serta kemampuan model dalam menjelaskan variasi nilai HDI.

metrik_svr <- function(actual, predicted) {

  MAE <- mean(abs(actual - predicted))

  RMSE <- sqrt(mean((actual - predicted)^2))

  R2 <- if (sum((actual - mean(actual))^2) > 0) {
    1 - sum((actual - predicted)^2) /
      sum((actual - mean(actual))^2)
  } else {
    NA_real_
  }

  return(c(
    MAE = MAE,
    RMSE = RMSE,
    R2 = R2
  ))
}

5.2.2 Melakukan Prediksi

Setelah fungsi evaluasi dibuat, tahap selanjutnya adalah melakukan prediksi nilai HDI pada data testing menggunakan model SVR Linear dan SVR Radial yang telah dibangun sebelumnya. Prediksi ini dilakukan untuk memperoleh estimasi nilai HDI berdasarkan masing-masing model. Hasil prediksi kemudian disajikan bersama nilai HDI aktual agar dapat dibandingkan dan digunakan dalam proses evaluasi kinerja model.

# Prediksi SVR Linear
pred_linear <- predict(
  fit_linear6,
  newdata = test_hdi
)

# Prediksi SVR Radial
pred_radial <- predict(
  fit_radial6,
  newdata = test_hdi
)

# Membandingkan nilai aktual dan prediksi
hasil_prediksi <- data.frame(
  Aktual = test_hdi$hdi,
  Prediksi_Linear = as.numeric(pred_linear),
  Prediksi_Radial = as.numeric(pred_radial)
)

head(hasil_prediksi)
##   Aktual Prediksi_Linear Prediksi_Radial
## 1  0.948       0.9304751       0.8822071
## 2  0.962       0.9439617       0.9230815
## 3  0.970       0.9777317       0.9457098
## 4  0.958       0.9496924       0.9522328
## 5  0.951       0.9331535       0.9252848
## 6  0.938       0.9248696       0.9253091

5.2.3 Evaluasi Model Baseline

Setelah memperoleh hasil prediksi dari kedua model, selanjutnya dilakukan evaluasi model baseline untuk mengetahui kemampuan awal SVR Linear dan SVR Radial dalam memprediksi nilai HDI. Evaluasi dilakukan menggunakan metrik MAE, RMSE, dan \(R^2\) yang telah didefinisikan sebelumnya. Nilai MAE dan RMSE yang lebih kecil menunjukkan tingkat kesalahan prediksi yang lebih rendah, sedangkan nilai \(R^2\) yang lebih besar menunjukkan kemampuan model yang lebih baik dalam menjelaskan variasi nilai HDI.

# Evaluasi model linear
eval_linear <- metrik_svr(
  test_hdi$hdi,
  pred_linear
)

# Evaluasi model radial
eval_radial <- metrik_svr(
  test_hdi$hdi,
  pred_radial
)

# Membuat tabel perbandingan
hasil_evaluasi <- data.frame(
  Model = c("SVR Linear", "SVR Radial"),

  MAE = c(
    eval_linear["MAE"],
    eval_radial["MAE"]
  ),

  RMSE = c(
    eval_linear["RMSE"],
    eval_radial["RMSE"]
  ),

  R2 = c(
    eval_linear["R2"],
    eval_radial["R2"]
  )
)

kable(
  hasil_evaluasi,
  digits = 4,
  caption = "Perbandingan Kinerja Model SVR Baseline"
)
Perbandingan Kinerja Model SVR Baseline
Model MAE RMSE R2
SVR Linear 0.0286 0.0372 0.9213
SVR Radial 0.0312 0.0384 0.9163

Berdasarkan hasil evaluasi, SVR Linear menunjukkan kinerja yang sedikit lebih baik dibandingkan SVR Radial. Hal ini ditunjukkan oleh nilai MAE dan RMSE yang lebih rendah, masing-masing sebesar 0,0286 dan 0,0372. Selain itu, SVR Linear memiliki nilai \(R^2\) lebih tinggi, yaitu 0,9213, yang menunjukkan bahwa model mampu menjelaskan sekitar 92,13% variasi nilai HDI.

Dengan demikian, SVR Linear dipilih sebagai model baseline terbaik karena menghasilkan kesalahan prediksi yang lebih kecil dan kemampuan penjelasan variasi data yang lebih tinggi dibandingkan SVR Radial.

5.3 Tuning Hyperparameter

Setelah mengevaluasi model baseline, tahap selanjutnya adalah melakukan tuning hyperparameter untuk memperoleh kombinasi parameter terbaik yang dapat meningkatkan kinerja model SVR. Proses tuning dilakukan dengan menguji beberapa kombinasi nilai cost, epsilon, dan gamma pada kernel yang digunakan. Pengujian dilakukan menggunakan metode 5-Fold Cross-Validation dan 10-Fold Cross-Validation untuk memperoleh nilai RMSE pada setiap kombinasi parameter. Kombinasi dengan rata-rata RMSE terkecil akan dipertimbangkan sebagai kombinasi parameter terbaik.

5.3.1 Menentukan Fold Cross-Validation

Data training dibagi menjadi 5 fold dan 10 fold. Proses ini dilakukan untuk mengevaluasi kombinasi hyperparameter secara berulang sehingga penilaian model tidak hanya bergantung pada satu pembagian data training dan validasi.

set.seed(123)

# Membagi training set menjadi 5 fold
fold_id <- sample(
  rep(1:5, length.out = nrow(train_hdi))
)

# Melihat jumlah observasi setiap fold
table(fold_id)
## fold_id
##  1  2  3  4  5 
## 25 25 24 24 24

5.3.2 Menentukan Kombinasi Hyperparameter

Tuning dilakukan dengan menguji beberapa kombinasi nilai cost, epsilon, dan gamma untuk kernel yang sesuai. - cost: mengatur besarnya penalti terhadap kesalahan prediksi yang berada di luar epsilon tube. - epsilon: menentukan lebar toleransi kesalahan prediksi yang tidak dikenai penalti dalam fungsi loss SVR. - gamma: mengatur jangkauan pengaruh observasi pada kernel radial. Parameter ini tidak digunakan untuk kernel linear. Dalam penelitian ini, terdapat sembilan kombinasi parameter untuk kernel linear dan 27 kombinasi untuk kernel radial. Dengan demikian, total terdapat 36 kombinasi hyperparameter yang dievaluasi.

# Grid untuk kernel linear
grid_linear <- expand.grid(
  kernel = "linear",
  cost = c(0.1, 1, 10),
  epsilon = c(0.02, 0.05, 0.1),
  gamma = NA_real_
)

# Grid untuk kernel radial
grid_radial <- expand.grid(
  kernel = "radial",
  cost = c(0.1, 1, 10),
  epsilon = c(0.02, 0.05, 0.1),
  gamma = c(0.05, 0.2, 1)
)

# Menggabungkan seluruh kombinasi
grid <- rbind(
  grid_linear,
  grid_radial
)

# Melihat jumlah kombinasi
nrow(grid)
## [1] 36
# Melihat kombinasi parameter
head(grid)
##   kernel cost epsilon gamma
## 1 linear  0.1    0.02    NA
## 2 linear  1.0    0.02    NA
## 3 linear 10.0    0.02    NA
## 4 linear  0.1    0.05    NA
## 5 linear  1.0    0.05    NA
## 6 linear 10.0    0.05    NA

5.3.3 Membuat Fungsi Cross-Validation

# Fungsi Cross-Validation SVR
cv_rmse <- function(
  kernel,
  cost,
  epsilon,
  gamma,
  formula,
  dataset,
  k = 5,
  seed = 123
) {

  set.seed(seed)

  # Membagi data training menjadi k fold
  fold_id <- sample(
    rep(1:k, length.out = nrow(dataset))
  )

  # Menyimpan RMSE setiap fold
  hasil_fold <- vapply(1:k, function(i) {

    # Membagi data training dan validasi
    d_train <- dataset[fold_id != i, ]
    d_valid <- dataset[fold_id == i, ]

    # Menentukan parameter model
    args <- list(
      formula = formula,
      data = d_train,
      type = "eps-regression",
      kernel = kernel,
      cost = cost,
      epsilon = epsilon,
      scale = TRUE
    )

    # Gamma hanya digunakan untuk kernel radial
    if (kernel == "radial") {
      args$gamma <- gamma
    }

    # Membentuk model
    fit <- do.call(
      e1071::svm,
      args
    )

    # Prediksi data validasi
    pred <- predict(
      fit,
      newdata = d_valid
    )

    # Menghitung RMSE
    sqrt(mean(
      (d_valid$hdi - pred)^2
    ))

  }, numeric(1))

  return(hasil_fold)
}

5.3.4 Melakukan Tuning Hyperparameter

# Fungsi untuk melakukan tuning SVR
tune_svr <- function(grid, k, formula, dataset, seed = 123) {

  hasil <- grid

  hasil$Mean_RMSE <- NA_real_
  hasil$SD_RMSE <- NA_real_

  for (i in seq_len(nrow(hasil))) {

    nilai_rmse <- cv_rmse(
      kernel = hasil$kernel[i],
      cost = hasil$cost[i],
      epsilon = hasil$epsilon[i],
      gamma = hasil$gamma[i],
      formula = formula,
      dataset = dataset,
      k = k,
      seed = seed
    )

    hasil$Mean_RMSE[i] <- mean(nilai_rmse)
    hasil$SD_RMSE[i] <- sd(nilai_rmse)
  }

  # Mengurutkan berdasarkan RMSE terkecil
  hasil <- hasil[
    order(hasil$Mean_RMSE),
  ]

  rownames(hasil) <- NULL

  return(hasil)
}


# Tuning menggunakan 5-Fold CV
hasil_svr_5 <- tune_svr(
  grid = grid,
  k = 5,
  formula = form6,
  dataset = train_hdi,
  seed = 123
)

# Menampilkan 10 kombinasi terbaik
knitr::kable(
  head(hasil_svr_5, 10),
  digits = 4,
  caption = "10 Kombinasi Hyperparameter Terbaik SVR Berdasarkan 5-Fold CV"
)
10 Kombinasi Hyperparameter Terbaik SVR Berdasarkan 5-Fold CV
kernel cost epsilon gamma Mean_RMSE SD_RMSE
linear 0.1 0.10 NA 0.0407 0.0086
linear 1.0 0.10 NA 0.0409 0.0080
linear 1.0 0.05 NA 0.0409 0.0083
linear 10.0 0.10 NA 0.0409 0.0079
linear 10.0 0.05 NA 0.0410 0.0083
linear 1.0 0.02 NA 0.0411 0.0087
linear 10.0 0.02 NA 0.0412 0.0087
linear 0.1 0.02 NA 0.0412 0.0087
linear 0.1 0.05 NA 0.0412 0.0086
radial 10.0 0.10 0.05 0.0429 0.0048
# Tuning menggunakan 10-Fold CV
hasil_svr_10 <- tune_svr(
  grid = grid,
  k = 10,
  formula = form6,
  dataset = train_hdi,
  seed = 123
)

# Menampilkan 10 kombinasi terbaik
knitr::kable(
  head(hasil_svr_10, 10),
  digits = 4,
  caption = "10 Kombinasi Hyperparameter Terbaik SVR Berdasarkan 10-Fold CV"
)
10 Kombinasi Hyperparameter Terbaik SVR Berdasarkan 10-Fold CV
kernel cost epsilon gamma Mean_RMSE SD_RMSE
linear 10.0 0.10 NA 0.0410 0.0095
linear 1.0 0.10 NA 0.0410 0.0095
linear 1.0 0.05 NA 0.0410 0.0103
linear 10.0 0.05 NA 0.0411 0.0103
linear 10.0 0.02 NA 0.0412 0.0106
linear 1.0 0.02 NA 0.0413 0.0107
linear 0.1 0.02 NA 0.0413 0.0108
linear 0.1 0.05 NA 0.0413 0.0107
linear 0.1 0.10 NA 0.0413 0.0106
radial 10.0 0.10 0.05 0.0422 0.0071
# Mengambil kombinasi terbaik 5-Fold
best_5 <- hasil_svr_5[1, ]

# Mengambil kombinasi terbaik 10-Fold
best_10 <- hasil_svr_10[1, ]

# Membuat tabel perbandingan
perbandingan_best <- data.frame(
  CV = c("5-Fold", "10-Fold"),
  Kernel = c(best_5$kernel, best_10$kernel),
  Cost = c(best_5$cost, best_10$cost),
  Epsilon = c(best_5$epsilon, best_10$epsilon),
  Gamma = c(best_5$gamma, best_10$gamma),
  Mean_RMSE = c(
    best_5$Mean_RMSE,
    best_10$Mean_RMSE
  ),
  SD_RMSE = c(
    best_5$SD_RMSE,
    best_10$SD_RMSE
  )
)

knitr::kable(
  perbandingan_best,
  digits = 4,
  caption = "Perbandingan Kombinasi Parameter Terbaik SVR"
)
Perbandingan Kombinasi Parameter Terbaik SVR
CV Kernel Cost Epsilon Gamma Mean_RMSE SD_RMSE
5-Fold linear 0.1 0.1 NA 0.0407 0.0086
10-Fold linear 10.0 0.1 NA 0.0410 0.0095

Berdasarkan hasil optimasi parameter, 5-Fold Cross-Validation menghasilkan kombinasi terbaik berupa kernel linear, cost sebesar 0,1, dan epsilon sebesar 0,1, dengan Mean RMSE sebesar 0,0407. Sementara itu, pada 10-Fold Cross-Validation, kombinasi terbaik diperoleh menggunakan kernel linear, cost sebesar 10, dan epsilon sebesar 0,1, dengan Mean RMSE sebesar 0,0410.

Kedua skema cross-validation sama-sama memilih kernel linear dengan nilai epsilon yang sama. Nilai Mean RMSE pada 5-Fold sedikit lebih rendah dibandingkan 10-Fold, sehingga kombinasi parameter 5-Fold memberikan hasil validasi yang sedikit lebih baik. Nilai SD_RMSE menunjukkan variasi RMSE antar-fold, dengan variasi pada 5-Fold (0,0086) yang juga sedikit lebih rendah dibandingkan 10-Fold (0,0095).

5.3.5 Menentukan Model Terbaik

# Menggabungkan hasil 5-Fold dan 10-Fold
hasil_svr_battle <- merge(
  hasil_svr_5,
  hasil_svr_10,
  by = c("kernel", "cost", "epsilon", "gamma"),
  suffixes = c("_5Fold", "_10Fold")
)

# Menghitung rata-rata RMSE kedua skema
hasil_svr_battle$Mean_RMSE_Gabungan <- rowMeans(
  hasil_svr_battle[, c(
    "Mean_RMSE_5Fold",
    "Mean_RMSE_10Fold"
  )]
)

# Mengurutkan berdasarkan RMSE gabungan terkecil
hasil_svr_battle <- hasil_svr_battle[
  order(hasil_svr_battle$Mean_RMSE_Gabungan),
]

# Menampilkan 10 kombinasi terbaik
knitr::kable(
  head(hasil_svr_battle, 10),
  digits = 4,
  caption = "Perbandingan Kombinasi Hyperparameter SVR Berdasarkan 5-Fold dan 10-Fold CV"
)
Perbandingan Kombinasi Hyperparameter SVR Berdasarkan 5-Fold dan 10-Fold CV
kernel cost epsilon gamma Mean_RMSE_5Fold SD_RMSE_5Fold Mean_RMSE_10Fold SD_RMSE_10Fold Mean_RMSE_Gabungan
6 linear 1.0 0.10 NA 0.0409 0.0080 0.0410 0.0095 0.0409
9 linear 10.0 0.10 NA 0.0409 0.0079 0.0410 0.0095 0.0409
5 linear 1.0 0.05 NA 0.0409 0.0083 0.0410 0.0103 0.0410
3 linear 0.1 0.10 NA 0.0407 0.0086 0.0413 0.0106 0.0410
8 linear 10.0 0.05 NA 0.0410 0.0083 0.0411 0.0103 0.0410
4 linear 1.0 0.02 NA 0.0411 0.0087 0.0413 0.0107 0.0412
7 linear 10.0 0.02 NA 0.0412 0.0087 0.0412 0.0106 0.0412
1 linear 0.1 0.02 NA 0.0412 0.0087 0.0413 0.0108 0.0413
2 linear 0.1 0.05 NA 0.0412 0.0086 0.0413 0.0107 0.0413
34 radial 10.0 0.10 0.05 0.0429 0.0048 0.0422 0.0071 0.0425
# Mengambil parameter terbaik berdasarkan rata-rata
best <- hasil_svr_battle[1, ]

# Menampilkan parameter terbaik
best[, c(
  "kernel",
  "cost",
  "epsilon",
  "gamma",
  "Mean_RMSE_5Fold",
  "Mean_RMSE_10Fold",
  "Mean_RMSE_Gabungan"
)]
##   kernel cost epsilon gamma Mean_RMSE_5Fold Mean_RMSE_10Fold Mean_RMSE_Gabungan
## 6 linear    1     0.1    NA      0.04086577       0.04100417         0.04093497

5.3.6 Membentuk Model Terbaik

# Menyiapkan parameter model terbaik
args_best <- list(
  formula = form6,
  data = train_hdi,
  type = "eps-regression",
  kernel = best$kernel,
  cost = best$cost,
  epsilon = best$epsilon,
  scale = TRUE
)

# Gamma hanya digunakan untuk kernel radial
if (best$kernel == "radial") {
  args_best$gamma <- best$gamma
}

# Membentuk model terbaik
best_fit <- do.call(
  e1071::svm,
  args_best
)

# Melihat ringkasan model
summary(best_fit)
## 
## Call:
## svm(formula = hdi ~ gdp_per_capita + social_support + healthy_life_expectancy + 
##     freedom_to_make_life_choices + generosity + perceptions_of_corruption, 
##     data = structure(list(gdp_per_capita = c(1.609, 1.05, 0.512, 
##     1.258, 0.306, 0.35, 1.029, 0.741, 0.619, 0.813, 0.755, 0.393, 
##     1.155, 0.8, 1.387, 0.366, 1.067, 0.983, 0.476, 0.96, 0.308, 1.183, 
##     1.124, 0, 0.274, 1.365, 1.238, 1.015, 1.07, 1.403, 1.201, 1.237, 
##     0.35, 0.562, 0.094, 0.569, 1.231, 0.931, 0.794, 0.549, 1.044, 
##     0.489, 0.811, 1.221, 1.276, 0.696, 0.446, 1.041, 1.159, 0.332, 
##     1.503, 1.181, 0.949, 1.263, 0.191, 1.043, 0.611, 1.499, 1.092, 
##     1.452, 0.331, 0.801, 0.323, 1.206, 1.004, 0.275, 0.026, 0.38, 
##     1.38, 0.82, 0.85, 0.96, 1.301, 1.3, 0.336, 0.45, 1.294, 0.745, 
##     1.149, 1.034, 0.886, 0.945, 0.677, 0.359, 1.004, 0.385, 1.246, 
##     1.303, 1.187, 1.376, 1.043, 0.985, 0.694, 0.574, 0.493, 0.948, 
##     0.268, 1.269, 1.162, 1.373, 1.12, 1.221, 0.138, 0.204, 1.1, 1.333, 
##     1.324, 0.685, 0.578, 0.921, 1.286, 0.912, 0.879, 0.274, 0.947, 
##     0.764, 0.855, 0.57, 1.396, 0.657, 0.287, 0.673), social_support = c(1.479, 
##     1.409, 0.983, 1.523, 0.575, 0.517, 1.125, 1.346, 0.378, 1.321, 
##     0.765, 0.437, 1.266, 1.269, 1.487, 1.114, 1.465, 1.294, 0.885, 
##     1.351, 0.939, 1.36, 1.465, 0.698, 0.757, 1.505, 1.515, 1.401, 
##     1.323, 1.357, 1.41, 1.528, 0.766, 0.928, 1.125, 0.808, 1.477, 
##     1.203, 1.242, 0.91, 1.303, 1.169, 1.149, 1.431, 1.455, 1.111, 
##     1.226, 1.145, 1.369, 1.069, 1.31, 1.156, 1.265, 1.223, 0.56, 
##     0.98, 0.868, 1.553, 1.513, 1.526, 1.056, 0.782, 0.688, 1.438, 
##     1.439, 0.572, 0, 0.829, 1.624, 1.39, 1.055, 1.427, 1.219, 1.52, 
##     1.033, 1.134, 1.488, 1.529, 1.442, 1.441, 0.666, 1.212, 0.886, 
##     0.711, 1.383, 1.105, 1.504, 1.557, 1.465, 1.475, 1.147, 1.41, 
##     1.325, 1.122, 1.098, 1.531, 0.841, 1.487, 1.232, 1.454, 1.402, 
##     1.171, 0.774, 0.986, 0.842, 1.538, 1.472, 1.328, 1.058, 1, 1.484, 
##     1.312, 1.313, 0.916, 0.848, 1.03, 1.475, 1.167, 1.522, 1.247, 
##     1.163, 0.799), healthy_life_expectancy = c(1.012, 0.828, 0.581, 
##     0.953, 0.295, 0.361, 0.893, 0.851, 0.44, 0.604, 0.588, 0.397, 
##     0.914, 0.746, 1.009, 0.433, 0.789, 0.838, 0.499, 0.469, 0.428, 
##     0.808, 0.891, 0.268, 0.505, 1.039, 0.818, 0.779, 0.861, 0.795, 
##     0.828, 0.874, 0.192, 0.723, 0.357, 0.232, 0.713, 0.66, 0.789, 
##     0.331, 0.673, 0.168, 0, 0.999, 1.029, 0.245, 0.677, 0.538, 0.92, 
##     0.443, 0.825, 0.999, 0.831, 1.042, 0.495, 0.574, 0.486, 0.999, 
##     0.815, 1.052, 0.38, 0.782, 0.449, 0.884, 0.802, 0.41, 0.105, 
##     0.375, 1.026, 0.739, 0.815, 0.805, 1.036, 0.999, 0.532, 0.571, 
##     1.039, 0.756, 0.91, 0.963, 0.752, 0.845, 0.535, 0.614, 0.854, 
##     0.308, 0.881, 1.026, 0.812, 1.016, 0.769, 0.841, 0.835, 0.637, 
##     0.718, 0.667, 0.242, 0.92, 0.825, 0.987, 0.798, 0.828, 0.366, 
##     0.39, 0.785, 0.996, 1.045, 0.739, 0.426, 0.815, 1.062, 0.868, 
##     0.477, 0.555, 0.874, 0.551, 0.777, 0.489, 0.999, 0.672, 0.463, 
##     0.508), freedom_to_make_life_choices = c(0.526, 0.557, 0.431, 
##     0.564, 0.01, 0, 0.521, 0.543, 0.013, 0.457, 0.498, 0.349, 0.296, 
##     0.535, 0.574, 0.361, 0.235, 0.345, 0.417, 0.389, 0.382, 0.195, 
##     0.523, 0.559, 0.142, 0.584, 0.291, 0.497, 0.433, 0.439, 0.199, 
##     0.495, 0.174, 0.527, 0.269, 0.352, 0.489, 0.491, 0.43, 0.381, 
##     0.416, 0.359, 0.313, 0.508, 0.371, 0.426, 0.439, 0.455, 0.357, 
##     0.356, 0.598, 0.067, 0.47, 0.406, 0.443, 0.241, 0.381, 0.516, 
##     0.311, 0.572, 0.255, 0.418, 0.026, 0.483, 0.39, 0.293, 0.225, 
##     0.332, 0.591, 0.178, 0.283, 0.154, 0.159, 0.564, 0.344, 0.292, 
##     0.231, 0.631, 0.516, 0.558, 0.346, 0.212, 0.313, 0.555, 0.282, 
##     0.327, 0.334, 0.585, 0.264, 0.532, 0.351, 0.47, 0.435, 0.609, 
##     0.389, 0.317, 0.309, 0.457, 0.462, 0.495, 0.498, 0.508, 0.318, 
##     0.494, 0.305, 0.45, 0.436, 0.245, 0.431, 0.167, 0.362, 0.498, 
##     0.401, 0.148, 0.383, 0.547, 0.514, 0.066, 0.557, 0.225, 0.143, 
##     0.372), generosity = c(0.194, 0.359, 0.372, 0.144, 0.202, 0.158, 
##     0.058, 0.147, 0.331, 0.37, 0.2, 0.175, 0.119, 0.175, 0.267, 0.151, 
##     0.094, 0.185, 0.276, 0.13, 0.269, 0.083, 0.127, 0.243, 0.275, 
##     0.285, 0.043, 0.113, 0.074, 0.08, 0.081, 0.103, 0.198, 0.166, 
##     0.212, 0.154, 0.185, 0.498, 0.093, 0.187, 0.133, 0.107, 0.074, 
##     0.047, 0.261, 0.215, 0.285, 0.025, 0.187, 0.252, 0.262, 0, 0.244, 
##     0.19, 0.218, 0.148, 0.245, 0.298, 0.081, 0.263, 0.177, 0.036, 
##     0.419, 0.117, 0.099, 0.177, 0.235, 0.207, 0.354, 0.187, 0.095, 
##     0.064, 0.175, 0.375, 0.209, 0.153, 0.158, 0.322, 0.109, 0.144, 
##     0.043, 0.263, 0.22, 0.217, 0.137, 0.153, 0.121, 0.33, 0.075, 
##     0.244, 0.035, 0.099, 0.2, 0.232, 0.23, 0.235, 0.252, 0.046, 0.083, 
##     0.261, 0.215, 0.26, 0.188, 0.197, 0.27, 0.348, 0.111, 0.181, 
##     0.247, 0.059, 0.153, 0.126, 0.07, 0.169, 0.178, 0.266, 0.184, 
##     0.106, 0.322, 0.103, 0.108, 0.105), perceptions_of_corruption = c(0.316, 
##     0.028, 0.053, 0.057, 0.091, 0.025, 0.1, 0.073, 0.141, 0.167, 
##     0.085, 0.082, 0.022, 0.078, 0.373, 0.089, 0.142, 0.034, 0.147, 
##     0.055, 0.167, 0.106, 0.15, 0.27, 0.078, 0.308, 0.042, 0.101, 
##     0.073, 0.132, 0.02, 0.161, 0.078, 0.143, 0.053, 0.09, 0.016, 
##     0.028, 0.074, 0.037, 0.152, 0.093, 0.135, 0.025, 0.082, 0.041, 
##     0.089, 0.1, 0.056, 0.06, 0.182, 0.034, 0.047, 0.041, 0.089, 0.089, 
##     0.04, 0.31, 0.004, 0.343, 0.113, 0.076, 0.11, 0.05, 0.086, 0.085, 
##     0.035, 0.086, 0.118, 0.01, 0.064, 0.047, 0.056, 0.151, 0.1, 0.072, 
##     0.03, 0.24, 0.054, 0.093, 0.164, 0.006, 0.098, 0.411, 0.039, 
##     0.052, 0.014, 0.38, 0.064, 0.226, 0.182, 0.034, 0.127, 0.062, 
##     0.144, 0.038, 0.045, 0.036, 0.005, 0.265, 0.06, 0.024, 0.102, 
##     0.138, 0.125, 0.278, 0.183, 0, 0.087, 0.055, 0.079, 0.087, 0.056, 
##     0.041, 0.027, 0.164, 0.08, 0.088, 0.298, 0.066, 0.077, 0.093), 
##         hdi = c(0.922, 0.798, 0.628, 0.931, 0.388, 0.496, 0.797, 
##         0.766, 0.564, 0.698, 0.685, 0.515, 0.889, 0.662, 0.959, 0.598, 
##         0.824, 0.815, 0.555, 0.741, 0.524, 0.853, 0.862, 0.404, 0.603, 
##         0.939, 0.895, 0.776, 0.789, 0.9, 0.87, 0.905, 0.416, 0.685, 
##         0.522, 0.582, 0.807, 0.728, 0.678, 0.588, 0.721, 0.55, 0.695, 
##         0.89, 0.919, 0.56, 0.622, 0.731, 0.878, 0.582, 0.94, 0.908, 
##         0.776, 0.913, 0.517, 0.695, 0.628, 0.949, 0.845, 0.97, 0.459, 
##         0.71, 0.554, 0.906, 0.786, 0.571, 0.414, 0.5, 0.972, 0.779, 
##         0.811, 0.709, 0.937, 0.924, 0.497, 0.53, 0.915, 0.74, 0.839, 
##         0.833, 0.844, 0.804, 0.544, 0.578, 0.833, 0.419, 0.88, 0.938, 
##         0.889, 0.93, 0.789, 0.788, 0.706, 0.606, 0.691, 0.747, 0.467, 
##         0.915, 0.845, 0.959, 0.806, 0.819, 0.419, 0.493, 0.799, 0.946, 
##         0.92, 0.785, 0.595, 0.746, 0.918, 0.777, 0.665, 0.487, 0.81, 
##         0.617, 0.756, 0.563, 0.955, 0.674, 0.47, 0.649)), row.names = c(14L, 
##     50L, 118L, 43L, 153L, 151L, 90L, 91L, 146L, 92L, 137L, 99L, 72L, 
##     26L, 7L, 143L, 78L, 81L, 150L, 103L, 117L, 76L, 32L, 109L, 139L, 
##     9L, 41L, 74L, 23L, 27L, 60L, 53L, 129L, 122L, 124L, 96L, 38L, 
##     89L, 34L, 93L, 69L, 141L, 132L, 63L, 13L, 82L, 97L, 145L, 25L, 
##     133L, 21L, 79L, 127L, 47L, 147L, 123L, 95L, 16L, 94L, 6L, 112L, 
##     86L, 144L, 39L, 31L, 136L, 152L, 115L, 4L, 130L, 113L, 105L, 
##     52L, 22L, 131L, 108L, 35L, 40L, 30L, 12L, 116L, 75L, 64L, 149L, 
##     67L, 125L, 37L, 8L, 51L, 10L, 87L, 42L, 44L, 106L, 71L, 80L, 
##     126L, 20L, 46L, 17L, 55L, 77L, 111L, 120L, 114L, 15L, 24L, 68L, 
##     135L, 121L, 29L, 48L, 110L, 140L, 104L, 102L, 61L, 119L, 5L, 
##     107L, 148L, 100L), class = "data.frame"), type = "eps-regression", 
##     kernel = structure(1L, levels = c("linear", "radial"), class = "factor"), 
##     cost = 1, epsilon = 0.1, scale = TRUE)
## 
## 
## Parameters:
##    SVM-Type:  eps-regression 
##  SVM-Kernel:  linear 
##        cost:  1 
##       gamma:  0.1666667 
##     epsilon:  0.1 
## 
## 
## Number of Support Vectors:  81

Jumlah support vectors pada model terbaik adalah 78 observasi. Nilai ini lebih sedikit dibandingkan model linear baseline yang menggunakan 102 support vectors. Namun, kualitas model tetap ditentukan berdasarkan kemampuan prediksi dan hasil evaluasi, bukan hanya jumlah support vectors.

5.3.7 Evaluasi Model Terbaik pada Testing Set

# Prediksi menggunakan model terbaik
pred_best <- predict(
  best_fit,
  newdata = test_hdi
)

# Membandingkan tiga model
result_hdi <- rbind(

  data.frame(
    Model = "SVR Linear Baseline",
    t(metrik_svr(test_hdi$hdi, pred_linear))
  ),

  data.frame(
    Model = "SVR Radial Baseline",
    t(metrik_svr(test_hdi$hdi, pred_radial))
  ),

  data.frame(
    Model = "SVR Terbaik (Tuned 5-Fold & 10-Fold)",
    t(metrik_svr(test_hdi$hdi, pred_best))
  )
)

# Menampilkan hasil
kable(
  result_hdi,
  digits = 4,
  caption = "Perbandingan Kinerja Model SVR pada Testing Set"
)
Perbandingan Kinerja Model SVR pada Testing Set
Model MAE RMSE R2
SVR Linear Baseline 0.0286 0.0372 0.9213
SVR Radial Baseline 0.0312 0.0384 0.9163
SVR Terbaik (Tuned 5-Fold & 10-Fold) 0.0287 0.0366 0.9241

Berdasarkan hasil evaluasi pada testing set, SVR Terbaik (Tuned 5-Fold & 10-Fold) menunjukkan kinerja terbaik dibandingkan kedua model baseline. Model ini menghasilkan nilai RMSE terendah sebesar 0,0366 dan nilai \(R^2\) tertinggi sebesar 0,9241. Artinya, model mampu menjelaskan sekitar 92,41% variasi nilai HDI pada data testing.

Meskipun nilai MAE model tuned (0,0287) sedikit lebih tinggi dibandingkan SVR Linear Baseline (0,0286), model tuned tetap unggul dari segi RMSE dan \(R^2\). Dengan demikian, SVR Terbaik hasil tuning dipilih sebagai model final karena secara keseluruhan memberikan hasil prediksi yang lebih baik pada data testing dibandingkan model baseline.

5.3.8 Visualisasi Aktual dan Prediksi

plot_hdi <- data.frame(
  Aktual = test_hdi$hdi,
  Prediksi = as.numeric(pred_best)
)

ggplot(
  plot_hdi,
  aes(x = Aktual, y = Prediksi)
) +

  geom_point(
    color = "steelblue",
    size = 2.5
  ) +

  geom_abline(
    intercept = 0,
    slope = 1,
    linetype = "dashed",
    color = "red"
  ) +

  coord_equal() +

  labs(
    title = "Perbandingan HDI Aktual dan Prediksi SVR",
    subtitle = "Model terbaik berdasarkan 5-Fold dan 10-Fold Cross Validation",
    x = "HDI Aktual",
    y = "HDI Prediksi"
  ) +

  theme_minimal()

Berdasarkan scatter plot tersebut, titik-titik prediksi HDI sebagian besar berada di sekitar garis merah putus-putus yang menunjukkan kondisi prediksi ideal (HDI prediksi = HDI aktual). Hal ini menunjukkan bahwa model SVR mampu menghasilkan prediksi yang cukup mendekati nilai HDI sebenarnya. Meskipun masih terdapat beberapa titik yang cukup jauh dari garis, secara umum hasil prediksi model sudah cukup baik.

5.4 Kesimpulan

Berdasarkan hasil pemodelan Support Vector Regression (SVR), model dengan kernel linear menunjukkan kinerja yang sedikit lebih baik dibandingkan kernel radial pada tahap baseline. Hal ini ditunjukkan oleh nilai MAE, RMSE, dan \(R^2\) yang lebih baik. Selanjutnya, dilakukan optimasi parameter menggunakan 5-Fold dan 10-Fold Cross-Validation. Kedua skema tersebut sama-sama menghasilkan kombinasi terbaik dengan kernel linear, meskipun nilai parameter cost yang diperoleh berbeda.

Setelah dilakukan evaluasi pada testing set, model SVR hasil tuning menggunakan kombinasi 5-Fold dan 10-Fold menghasilkan kinerja terbaik secara keseluruhan, dengan nilai MAE sebesar 0,0287, RMSE sebesar 0,0366, dan \(R^2\) sebesar 0,9241. Nilai tersebut menunjukkan bahwa model mampu menjelaskan sekitar 92,41% variasi nilai HDI pada data testing. Dengan demikian, model SVR hasil tuning dipilih sebagai model final karena memberikan nilai RMSE paling rendah dan \(R^2\) paling tinggi dibandingkan model baseline. Hasil ini menunjukkan bahwa optimasi parameter dapat meningkatkan kemampuan model dalam memprediksi nilai HDI, meskipun peningkatannya relatif kecil.

6 Neural Network (NN)

6.1 Persiapan untuk NN

metrik <- function(y, p) {
  stopifnot(length(y)==length(p), all(is.finite(y)), all(is.finite(p)))
  sst <- sum((y-mean(y))^2)
  c(MAE=mean(abs(y-p)), RMSE=sqrt(mean((y-p)^2)),
    R2=if (sst>0) 1-sum((y-p)^2)/sst else NA_real_)
}

fit_nn <- function(d, target, prediktor, size=3, decay=.01, seed=2026) {
  x <- as.matrix(d[, prediktor, drop=FALSE])
  y <- d[[target]]
  mu <- colMeans(x)
  sx <- apply(x, 2, sd)
  sx[!is.finite(sx) | sx==0] <- 1
  my <- mean(y); sy <- sd(y)
  if (!is.finite(sy) || sy==0) stop("Respons latih harus bervariasi.")
  xs <- sweep(sweep(x, 2, mu, "-"), 2, sx, "/")
  set.seed(seed)
  model <- nnet::nnet(x=xs, y=(y-my)/sy, size=size, decay=decay,
                      linout=TRUE, maxit=2000, trace=FALSE, MaxNWts=10000)
  list(model=model, prediktor=prediktor, mu=mu, sx=sx, my=my, sy=sy)
}

pred_nn <- function(obj, baru) {
  x <- as.matrix(baru[, obj$prediktor, drop=FALSE])
  xs <- sweep(sweep(x, 2, obj$mu, "-"), 2, obj$sx, "/")
  as.numeric(predict(obj$model, xs))*obj$sy + obj$my
}

Seperti pada modul SVR, kita membagi data menjadi 80% data latih dan 20% data uji dengan seed = 123. Pembagian ini menghasilkan kelompok negara yang sama jika isi, urutan, dan penyaringan datanya juga sama. Agar hasil kedua model bisa dibandingkan secara langsung, gunakan kembali indeks data latih dan data uji dari modul SVR. Pilih model akhir melalui cross-validation pada data latih, lalu gunakan data uji untuk mengevaluasi model terpilih.

# Pembagian data testing & data training (80:20)
set.seed(123)
idx <- sample(seq_len(nrow(hdi)), floor(.8*nrow(hdi))) #(.8*nrow(hdi) untuk hitung banyak baris yg diambil untuk data latih) tl'dr idx berisi nomor baris yang terpilih menjadi data latih.
latih <- hdi[idx, , drop=FALSE] # yg jadi data latih (training)
uji <- hdi[-idx, , drop=FALSE] # yg jadi data uji (testing)

# Pembagian data testing jadi 5 fold cross validation
# 80% dibagi 5
set.seed(123)
fold <- sample(rep(1:5, length.out=nrow(latih)))
cat("Latih:", nrow(latih), "| Uji:", nrow(uji), "\n")
## Latih: 122 | Uji: 31

6.2 Shallow Neural Network

6.2.1 Tuning jumlah neuron, decay, dan kelompok prediktor

Kita membandingkan model dengan 2 dan 6 prediktor menggunakan pembagian fold yang sama. Pada setiap fold, parameter standardisasi prediktor dan respons dihitung dari bagian training, lalu digunakan untuk mentransformasi bagian validasi. Setiap konfigurasi dilatih tiga kali dengan bobot awal yang berbeda, kemudian ketiga prediksinya dirata-ratakan. Cara yang sama digunakan untuk model akhir. Nilai seed ditetapkan sejak awal agar hasilnya dapat direproduksi.

kelompok <- list(dua=c("gdp", "health"), enam=aman)
grid <- expand.grid(kelompok=c("dua", "enam"), size=c(2, 4, 6), # jumlah neuron pd hiddenlayer
                    decay=c(.001, .01, .1), #decay mengendalikan besar bobot
                    stringsAsFactors=FALSE) 

grid$CV_RMSE <- NA_real_
grid$Belum_konvergen <- 0L

# Pengujian konfigurasi
for (g in seq_len(nrow(grid))) {
  pred_oof <- rep(NA_real_, nrow(latih)) #prediksi yang disimpan adalah prediksi terhadap data yang sedang menjadi bagian validasi, bukan data yang digunakan untuk training.
  
  # 5 fold cross validation
  for (k in 1:5) {
    a <- latih[fold!=k, , drop=FALSE] #training
    b <- latih[fold==k, , drop=FALSE] #validasi
    
    fits <- lapply(1:3, function(s)
      fit_nn(a, "hdi", kelompok[[grid$kelompok[g]]],
             size=grid$size[g], decay=grid$decay[g], seed=1000*k+s))
    grid$Belum_konvergen[g] <- grid$Belum_konvergen[g] +
      sum(vapply(fits, function(z) z$model$convergence!=0, logical(1)))
    pp <- vapply(fits, function(z) pred_nn(z, b), numeric(nrow(b)))
    pred_oof[fold==k] <- rowMeans(pp)
  }
  grid$CV_RMSE[g] <- sqrt(mean((latih$hdi-pred_oof)^2))
}

6.2.2 Memilih konfigurasi terbaik

grid <- grid[order(grid$CV_RMSE), ]
knitr::kable(grid, digits=4, row.names=FALSE,
             caption="RMSE out-of-fold gabungan; seluruh prediksi dari fold validasi")
RMSE out-of-fold gabungan; seluruh prediksi dari fold validasi
kelompok size decay CV_RMSE Belum_konvergen
dua 2 0.010 0.0393 0
dua 2 0.001 0.0394 0
dua 2 0.100 0.0398 0
dua 4 0.010 0.0398 0
dua 6 0.010 0.0398 0
dua 4 0.100 0.0400 0
dua 6 0.100 0.0403 0
enam 6 0.100 0.0407 0
enam 4 0.100 0.0407 0
dua 6 0.001 0.0411 0
enam 2 0.010 0.0413 0
enam 2 0.100 0.0421 0
enam 2 0.001 0.0426 0
dua 4 0.001 0.0430 0
enam 4 0.010 0.0456 0
enam 6 0.010 0.0528 0
enam 6 0.001 0.0531 0
enam 4 0.001 0.0537 0
if (any(grid$Belum_konvergen>0))
  warning("Ada fit CV belum konvergen. Periksa tabel sebelum menerima hasil tuning.")
best <- grid[1, , drop=FALSE]
knitr::kable(best, digits=4, caption="Konfigurasi terpilih melalui CV")
Konfigurasi terpilih melalui CV
kelompok size decay CV_RMSE Belum_konvergen
7 dua 2 0.01 0.0393 0

Pada tahap cross-validation (CV), kita melatih 270 jaringan. Setiap satu dari 18 konfigurasi dicoba pada lima fold, dengan tiga set bobot awal pada tiap fold.

Konfigurasi dengan RMSE CV terendah menentukan kelompok prediktor, jumlah neuron, dan besarnya penalti.

6.2.3 Pelatihan model akhir

if (best$Belum_konvergen>0)
  stop("Konfigurasi terpilih belum konvergen. Naikkan maxit dalam fit_nn lalu ulangi CV.")
fit_final <- lapply(1:3, function(s)
  fit_nn(latih, "hdi", kelompok[[best$kelompok]],
         size=best$size, decay=best$decay, seed=9000+s))
if (any(vapply(fit_final, function(z) z$model$convergence!=0, logical(1))))
  stop("Fit final belum konvergen. Periksa optimisasi sebelum evaluasi uji.")

6.3 Deep Neural Network

DNN menggunakan enam prediktor dengan pembagian data latih (training), data uji (testing), dan fold yang sama seperti pada percobaan NN. Kita membandingkan dua arsitektur, yaitu 6–8–4–1 dengan 97 parameter dan 6–8–4–2–1 dengan 105 parameter (8, 4 & 2yaitu hidden layer 1, 2, & 3).

6.3.1 Persiapan DNN

fit_dnn <- function(d, target, prediktor, hidden=c(8L,4L),
                    lambda=.01, epochs=500L, lr=.01, seed=2026) {
  stopifnot(length(hidden)>=1, all(hidden>=1), epochs>=1, lr>0, lambda>=0)
  x <- as.matrix(d[, prediktor, drop=FALSE]); y <- d[[target]]
  mu <- colMeans(x); sx <- apply(x, 2, sd)
  sx[!is.finite(sx) | sx==0] <- 1
  my <- mean(y); sy <- sd(y)
  if (!is.finite(sy) || sy==0) stop("Respons latih harus bervariasi.")
  x <- sweep(sweep(x, 2, mu, "-"), 2, sx, "/")
  y <- matrix((y-my)/sy, ncol=1)
  dims <- c(ncol(x), as.integer(hidden), 1L)
  L <- length(dims)-1L; n <- nrow(x)
  set.seed(seed)
  # Inisialisasi Xavier untuk aktivasi tanh.
  W <- lapply(seq_len(L), function(l) {
    batas <- sqrt(6/(dims[l]+dims[l+1]))
    matrix(runif(dims[l]*dims[l+1], -batas, batas), dims[l], dims[l+1])
  })
  b <- lapply(seq_len(L), function(l) matrix(0, 1, dims[l+1]))
  mW <- vW <- lapply(W, function(z) z*0)
  mb <- vb <- lapply(b, function(z) z*0)
  history <- data.frame(epoch=seq_len(epochs), MSE=NA_real_, Loss=NA_real_)
  beta1 <- .9; beta2 <- .999; eps <- 1e-8
  for (tt in seq_len(epochs)) {
    A <- vector("list", L+1L); A[[1]] <- x
    for (l in seq_len(L)) {
      z <- sweep(A[[l]] %*% W[[l]], 2, as.numeric(b[[l]]), "+")
      A[[l+1L]] <- if (l<L) tanh(z) else z
    }
    err <- A[[L+1L]]-y
    history$MSE[tt] <- mean(err^2)
    history$Loss[tt] <- mean(err^2)+lambda*sum(vapply(W, function(z) sum(z^2), numeric(1)))
    if (!is.finite(history$Loss[tt])) stop("Loss tidak finite; periksa input atau turunkan lr.")
    delta <- 2*err/n
    gW <- gb <- vector("list", L)
    for (l in rev(seq_len(L))) {
      gW[[l]] <- crossprod(A[[l]], delta)+2*lambda*W[[l]]
      gb[[l]] <- matrix(colSums(delta), nrow=1)
      if (l>1L) delta <- (delta %*% t(W[[l]]))*(1-A[[l]]^2)
    }
    # Semua gradien dihitung sebelum bobot diperbarui.
    for (l in seq_len(L)) {
      mW[[l]] <- beta1*mW[[l]]+(1-beta1)*gW[[l]]
      vW[[l]] <- beta2*vW[[l]]+(1-beta2)*gW[[l]]^2
      mb[[l]] <- beta1*mb[[l]]+(1-beta1)*gb[[l]]
      vb[[l]] <- beta2*vb[[l]]+(1-beta2)*gb[[l]]^2
      W[[l]] <- W[[l]]-lr*(mW[[l]]/(1-beta1^tt))/(sqrt(vW[[l]]/(1-beta2^tt))+eps)
      b[[l]] <- b[[l]]-lr*(mb[[l]]/(1-beta1^tt))/(sqrt(vb[[l]]/(1-beta2^tt))+eps)
    }
  }
  list(W=W, b=b, prediktor=prediktor, mu=mu, sx=sx, my=my, sy=sy,
       hidden=hidden, lambda=lambda, epochs=epochs, lr=lr, history=history,
       n_parameter=sum(vapply(W, length, integer(1)))+
                   sum(vapply(b, length, integer(1))))
}
pred_dnn <- function(obj, baru) {
  a <- as.matrix(baru[, obj$prediktor, drop=FALSE])
  a <- sweep(sweep(a, 2, obj$mu, "-"), 2, obj$sx, "/")
  L <- length(obj$W)
  for (l in seq_len(L)) {
    a <- sweep(a %*% obj$W[[l]], 2, as.numeric(obj$b[[l]]), "+")
    if (l<L) a <- tanh(a)
  }
  as.numeric(a)*obj$sy+obj$my
}

6.3.2 Tuning arsitektur dan hyperparameter

arsitektur <- list(dua_hidden=c(8L,4L), tiga_hidden=c(8L,4L,2L))
grid_dnn <- expand.grid(arsitektur=names(arsitektur), lambda=c(.001,.01),
                        epochs=c(300L,800L), stringsAsFactors=FALSE)
grid_dnn$CV_RMSE <- NA_real_
for (g in seq_len(nrow(grid_dnn))) {
  oof <- rep(NA_real_, nrow(latih))
  for (k in 1:5) {
    a <- latih[fold!=k, , drop=FALSE]; b <- latih[fold==k, , drop=FALSE]
    fits <- lapply(1:3, function(ss)
      fit_dnn(a, "hdi", aman, hidden=arsitektur[[grid_dnn$arsitektur[g]]],
              lambda=grid_dnn$lambda[g], epochs=grid_dnn$epochs[g],
              lr=.01, seed=1000*k+ss))
    oof[fold==k] <- rowMeans(vapply(fits, function(z) pred_dnn(z,b), numeric(nrow(b))))
  }
  grid_dnn$CV_RMSE[g] <- sqrt(mean((latih$hdi-oof)^2))
}
grid_dnn <- grid_dnn[order(grid_dnn$CV_RMSE), ]
best_dnn <- grid_dnn[1, , drop=FALSE]
knitr::kable(grid_dnn, digits=4, row.names=FALSE,
             caption="DNN: pemilihan konfigurasi hanya melalui training CV")
DNN: pemilihan konfigurasi hanya melalui training CV
arsitektur lambda epochs CV_RMSE
dua_hidden 0.010 300 0.0423
dua_hidden 0.010 800 0.0426
tiga_hidden 0.010 300 0.0437
tiga_hidden 0.010 800 0.0440
dua_hidden 0.001 300 0.0442
tiga_hidden 0.001 300 0.0491
dua_hidden 0.001 800 0.0505
tiga_hidden 0.001 800 0.0548

6.3.3 Pemilihan konfigurasi terbaik

keluarga_cv <- data.frame(Model=c("Shallow NN", "Deep NN"),
                         CV_RMSE=c(best$CV_RMSE, best_dnn$CV_RMSE))
knitr::kable(keluarga_cv, digits=4)
Model CV_RMSE
Shallow NN 0.0393
Deep NN 0.0423
cat("Keluarga terpilih berdasarkan CV:",
    keluarga_cv$Model[which.min(keluarga_cv$CV_RMSE)], "\n")
## Keluarga terpilih berdasarkan CV: Shallow NN

6.3.4 Pelatihan model akhir

p_nn <- rowMeans(vapply(fit_final, function(z) pred_nn(z, uji), numeric(nrow(uji))))
fit_deep <- lapply(1:3, function(ss)
  fit_dnn(latih, "hdi", aman, hidden=arsitektur[[best_dnn$arsitektur]],
          lambda=best_dnn$lambda, epochs=best_dnn$epochs, lr=.01, seed=9000+ss))
p_deep <- rowMeans(vapply(fit_deep, function(z) pred_dnn(z, uji), numeric(nrow(uji))))
plot_hdi <- data.frame(Aktual=uji$hdi, Prediksi=p_nn, Residual=uji$hdi-p_nn)
ggplot(plot_hdi, aes(Aktual, Prediksi)) +
  geom_point(color="#176b87", size=2.5) +
  geom_abline(slope=1, intercept=0, linetype=2) + coord_equal() +
  labs(title="HDI aktual vs prediksi NN pada test set",
       subtitle="Garis putus-putus menunjukkan prediksi sempurna") + theme_minimal()

ggplot(plot_hdi, aes(Prediksi, Residual)) +
  geom_point(color="#b85c38", size=2.5) + geom_hline(yintercept=0, linetype=2) +
  labs(title="Residual pada test set", y="Aktual − prediksi") + theme_minimal()

cat("Prediksi di luar [0,1]:", sum(p_nn<0 | p_nn>1), "\n")
## Prediksi di luar [0,1]: 1

Pada grafik pertama, semakin dekat titik ke garis diagonal, semakin dekat prediksi HDI dengan nilai aktualnya. Titik di atas garis menunjukkan bahwa model memprediksi HDI terlalu tinggi.

Pada grafik residual, nilai positif berarti HDI aktual lebih tinggi daripada prediksi. Jika titik membentuk pola melengkung, model mungkin belum menangkap pola hubungan dalam data. Jika sebarannya makin lebar, besar kesalahan prediksi mungkin berbeda pada tiap kisaran HDI. Titik yang menyebar acak di sekitar nol menunjukkan bahwa grafik ini tidak memperlihatkan pola kesalahan yang jelas.

6.3.5 Perbandingan NN dan DNN

banding_plot <- rbind(
  data.frame(Aktual=uji$hdi, Prediksi=p_nn, Model="Shallow NN"),
  data.frame(Aktual=uji$hdi, Prediksi=p_deep, Model="Deep NN"))
ggplot(banding_plot, aes(Aktual, Prediksi)) + geom_point(color="#176b87") +
  geom_abline(slope=1, intercept=0, linetype=2) + facet_wrap(~Model) +
  coord_equal() + labs(title="NN dan DNN pada test set HDI yang sama") + theme_minimal()

ggplot(fit_deep[[1]]$history, aes(epoch, Loss)) + geom_line(color="#7c3aed") +
  labs(title="Loss latih anggota pertama ensemble DNN") + theme_minimal()

cat("Parameter per DNN:", fit_deep[[1]]$n_parameter,
    "| Prediksi DNN di luar [0,1]:", sum(p_deep<0 | p_deep>1), "\n")
## Parameter per DNN: 97 | Prediksi DNN di luar [0,1]: 0

6.4 Kesimpulan

Berdasarkan hasil pengujian, Shallow NN memperoleh performa lebih baik dibandingkan Deep NN berdasarkan nilai Cross Validation RMSE, yaitu sebesar 0,0393, sedangkan Deep NN sebesar 0,0423. Konfigurasi terbaik Shallow NN menggunakan 2 neuron dengan decay 0,01, sedangkan Deep NN menggunakan arsitektur 6–8–4–1 dengan lambda 0,01 dan 300 epoch. Hasil ini menunjukkan bahwa jaringan yang lebih sederhana mampu memberikan kesalahan prediksi lebih rendah dibandingkan jaringan yang lebih kompleks pada percobaan ini. Dengan demikian, Shallow NN menjadi model regresi terbaik berdasarkan nilai Cross Validation.

7 Neural Network (NN) Classification

7.1 Persiapan NN Klasifikasi

# Menggunakan pembagian data yang sama dengan SVM
latih_cls <- train_raw
uji_cls <- test_raw

# Menggunakan daftar prediktor yang sama dengan SVM
prediktor_cls <- predictors

# Memastikan urutan kategori sesuai dengan fungsi NN klasifikasi
latih_cls$hdi_class <- factor(
  latih_cls$hdi_class,
  levels = c("Rendah", "Tinggi")
)

uji_cls$hdi_class <- factor(
  uji_cls$hdi_class,
  levels = c("Rendah", "Tinggi")
)

# Memeriksa data
str(latih_cls)
## 'data.frame':    122 obs. of  8 variables:
##  $ hdi          : num  0.645 0.544 0.691 0.698 0.588 0.628 0.582 0.622 0.515 0.649 ...
##  $ GDP          : num  0.642 0.677 0.493 0.813 0.549 0.611 0.569 0.446 0.393 0.673 ...
##  $ SocialSupport: num  1.236 0.886 1.098 1.321 0.91 ...
##  $ HealthyLife  : num  0.828 0.535 0.718 0.604 0.331 0.486 0.232 0.677 0.397 0.508 ...
##  $ Freedom      : num  0.507 0.313 0.389 0.457 0.381 0.381 0.352 0.439 0.349 0.372 ...
##  $ Generosity   : num  0.246 0.22 0.23 0.37 0.187 0.245 0.154 0.285 0.175 0.105 ...
##  $ Corruption   : num  0.078 0.098 0.144 0.167 0.037 0.04 0.09 0.089 0.082 0.093 ...
##  $ hdi_class    : Factor w/ 2 levels "Rendah","Tinggi": 1 1 1 1 1 1 1 1 1 1 ...
table(latih_cls$hdi_class)
## 
## Rendah Tinggi 
##     43     79
table(uji_cls$hdi_class)
## 
## Rendah Tinggi 
##     11     20

7.2 Fungsi Neural Network klasifikasi

Pada regresi, fungsi fit_nn() menggunakan output linear karena target yang diprediksi berupa angka kontinu. Untuk klasifikasi, targetnya berupa faktor dengan dua kategori, yaitu Rendah dan Tinggi.

Package nnet dapat menangani respons kategorik. Model menghasilkan prediksi kelas berdasarkan probabilitas kelas yang dipelajari selama pelatihan.

Kita membuat fungsi fit_nn_cls() dan pred_nn_cls() agar proses pelatihan dan prediksi klasifikasi terpisah dari fungsi regresi sebelumnya.

fit_nn_cls <- function(d, prediktor, size=3, decay=.01, seed=2026) {
  x <- as.matrix(d[, prediktor, drop=FALSE])
  mu <- colMeans(x)
  sx <- apply(x, 2, sd)
  sx[!is.finite(sx) | sx==0] <- 1

  xs <- sweep(sweep(x, 2, mu, "-"), 2, sx, "/")
  data_model <- as.data.frame(xs)
  data_model$hdi_class <- factor(d$hdi_class,
                                levels=c("Rendah", "Tinggi"))

  set.seed(seed)
  model <- nnet::nnet(
    hdi_class ~ ., data=data_model,
    size=size, decay=decay,
    maxit=2000, trace=FALSE, MaxNWts=10000
  )

  list(model=model, prediktor=prediktor, mu=mu, sx=sx)
}

pred_nn_cls <- function(obj, baru) {
  x <- as.matrix(baru[, obj$prediktor, drop=FALSE])
  xs <- sweep(sweep(x, 2, obj$mu, "-"), 2, obj$sx, "/")
  data_pred <- as.data.frame(xs)

  factor(
    predict(obj$model, newdata=data_pred, type="class"),
    levels=c("Rendah", "Tinggi")
  )
}

7.3 Fungsi evaluasi klasifikasi

Kinerja model klasifikasi dinilai menggunakan confusion matrix, accuracy, precision, recall, dan F1-score. Kategori Tinggi digunakan sebagai kelas positif.

evaluasi_cls <- function(aktual, prediksi) {
  cm <- table(
    Aktual=factor(aktual, levels=c("Rendah", "Tinggi")),
    Prediksi=factor(prediksi, levels=c("Rendah", "Tinggi"))
  )

  TN <- cm["Rendah", "Rendah"]
  FP <- cm["Rendah", "Tinggi"]
  FN <- cm["Tinggi", "Rendah"]
  TP <- cm["Tinggi", "Tinggi"]

  accuracy <- (TP+TN)/sum(cm)
  precision <- if (TP+FP == 0) 0 else TP/(TP+FP)
  recall <- if (TP+FN == 0) 0 else TP/(TP+FN)
  f1 <- if (precision+recall == 0) 0 else
    2*precision*recall/(precision+recall)

  list(
    confusion_matrix=cm,
    metrik=data.frame(
      Accuracy=accuracy,
      Precision=precision,
      Recall=recall,
      F1_Score=f1
    )
  )
}

7.4 Pembagian fold cross-validation

Seperti pada regresi, kita membandingkan 5-fold dan 10-fold CV. Setiap fold digunakan bergantian sebagai data validasi, sedangkan fold lainnya menjadi data training.

buat_fold_cls <- function(y, k) {
  if (any(table(y) < k))
    stop("Jumlah observasi pada salah satu kelas kurang dari jumlah fold.")

  fold <- integer(length(y))

  for (kelas in levels(y)) {
    idx <- which(y == kelas)
    fold[idx] <- sample(rep(1:k, length.out=length(idx)))
  }

  fold
}

set.seed(123)
fold5_cls <- buat_fold_cls(latih_cls$hdi_class, 5)

set.seed(123)
fold10_cls <- buat_fold_cls(latih_cls$hdi_class, 10)

table(Fold=fold5_cls, Kategori=latih_cls$hdi_class)
##     Kategori
## Fold Rendah Tinggi
##    1      9     16
##    2      9     16
##    3      9     16
##    4      8     16
##    5      8     15
table(Fold=fold10_cls, Kategori=latih_cls$hdi_class)
##     Kategori
## Fold Rendah Tinggi
##   1       5      8
##   2       5      8
##   3       5      8
##   4       4      8
##   5       4      8
##   6       4      8
##   7       4      8
##   8       4      8
##   9       4      8
##   10      4      7

7.5 Fungsi cross-validation

Fungsi berikut menjalankan proses pelatihan dan validasi untuk setiap fold. Hasil evaluasi dari seluruh fold kemudian dirata-ratakan untuk memperoleh nilai kinerja CV

cv_nn_cls <- function(d, prediktor, fold, k, size, decay) {
  hasil_fold <- data.frame()

  for (i in 1:k) {
    a <- d[fold!=i, , drop=FALSE]
    b <- d[fold==i, , drop=FALSE]

    model <- fit_nn_cls(
      a, prediktor,
      size=size, decay=decay,
      seed=2026+i
    )

    prediksi <- pred_nn_cls(model, b)
    evaluasi <- evaluasi_cls(b$hdi_class, prediksi)

    hasil_fold <- rbind(
      hasil_fold,
      data.frame(Fold=i, evaluasi$metrik)
    )
  }

  rata_rata <- data.frame(
    Accuracy=mean(hasil_fold$Accuracy),
    Precision=mean(hasil_fold$Precision),
    Recall=mean(hasil_fold$Recall),
    F1_Score=mean(hasil_fold$F1_Score)
  )

  list(hasil_fold=hasil_fold, rata_rata=rata_rata)
}

7.6 Tuning hyperparameter

Jumlah neuron pada hidden layer yang diuji adalah 2, 4, dan 6 neuron. Selain itu, digunakan tiga nilai decay, yaitu 0,001; 0,01; dan 0,1.

Dengan demikian, terdapat sembilan kombinasi hyperparameter yang akan dibandingkan menggunakan 5-fold dan 10-fold CV.

grid_cls <- expand.grid(
  size=c(2, 4, 6),
  decay=c(.001, .01, .1)
)

uji_grid_cls <- function(d, prediktor, fold, k, grid) {
  hasil <- data.frame()

  for (g in 1:nrow(grid)) {
    cv <- cv_nn_cls(
      d=d,
      prediktor=prediktor,
      fold=fold,
      k=k,
      size=grid$size[g],
      decay=grid$decay[g]
    )

    hasil <- rbind(
      hasil,
      data.frame(
        Neuron=grid$size[g],
        Decay=grid$decay[g],
        cv$rata_rata
      )
    )
  }

  hasil[order(-hasil$F1_Score, -hasil$Accuracy), ]
}

# 5-fold cross-validation
hasil_cv5_cls <- uji_grid_cls(
  latih_cls, prediktor_cls,
  fold5_cls, 5, grid_cls
)

knitr::kable(
  hasil_cv5_cls, digits=4, row.names=FALSE,
  caption="Hasil Tuning Neural Network dengan 5-Fold CV"
)
Hasil Tuning Neural Network dengan 5-Fold CV
Neuron Decay Accuracy Precision Recall F1_Score
2 0.100 0.9513 0.9528 0.9750 0.9632
4 0.100 0.9513 0.9528 0.9750 0.9632
6 0.100 0.9513 0.9528 0.9750 0.9632
4 0.010 0.9506 0.9653 0.9617 0.9624
4 0.001 0.9426 0.9528 0.9617 0.9563
2 0.010 0.9426 0.9528 0.9617 0.9563
6 0.010 0.9426 0.9528 0.9617 0.9563
6 0.001 0.9339 0.9528 0.9483 0.9489
2 0.001 0.9353 0.9653 0.9375 0.9486
# 10-fold cross-validation
hasil_cv10_cls <- uji_grid_cls(
  latih_cls, prediktor_cls,
  fold10_cls, 10, grid_cls
)

knitr::kable(
  hasil_cv10_cls, digits=4, row.names=FALSE,
  caption="Hasil Tuning Neural Network dengan 10-Fold CV"
)
Hasil Tuning Neural Network dengan 10-Fold CV
Neuron Decay Accuracy Precision Recall F1_Score
4 0.001 0.9519 0.9542 0.9750 0.9632
6 0.001 0.9519 0.9542 0.9750 0.9632
4 0.100 0.9513 0.9542 0.9750 0.9632
6 0.100 0.9513 0.9542 0.9750 0.9632
4 0.010 0.9436 0.9542 0.9625 0.9565
6 0.010 0.9436 0.9542 0.9625 0.9565
2 0.100 0.9429 0.9542 0.9625 0.9565
2 0.001 0.9429 0.9653 0.9500 0.9557
2 0.010 0.9429 0.9653 0.9500 0.9557

Konfigurasi dengan F1-score tertinggi menjadi konfigurasi terbaik pada masing-masing CV. Jika terdapat nilai F1-score yang sama, accuracy digunakan sebagai pertimbangan berikutnya.

7.7 Memilih konfigurasi terbaik

best_cls_5 <- hasil_cv5_cls[1, , drop=FALSE]
best_cls_10 <- hasil_cv10_cls[1, , drop=FALSE]

perbandingan_best_cls <- rbind(
  data.frame(Metode="5-Fold CV", best_cls_5),
  data.frame(Metode="10-Fold CV", best_cls_10)
)

knitr::kable(
  perbandingan_best_cls,
  digits=4,
  row.names=FALSE,
  caption="Konfigurasi Terbaik Neural Network"
)
Konfigurasi Terbaik Neural Network
Metode Neuron Decay Accuracy Precision Recall F1_Score
5-Fold CV 2 0.100 0.9513 0.9528 0.975 0.9632
10-Fold CV 4 0.001 0.9519 0.9542 0.975 0.9632

7.8 Pelatihan model akhir dan evaluasi data testing

Setelah konfigurasi terbaik diperoleh, model dilatih kembali menggunakan seluruh data training. Selanjutnya, kedua model diuji menggunakan data testing yang sama.

# Model akhir berdasarkan 5-fold CV
model_cls_5 <- fit_nn_cls(
  latih_cls, prediktor_cls,
  size=best_cls_5$Neuron,
  decay=best_cls_5$Decay,
  seed=9001
)

# Model akhir berdasarkan 10-fold CV
model_cls_10 <- fit_nn_cls(
  latih_cls, prediktor_cls,
  size=best_cls_10$Neuron,
  decay=best_cls_10$Decay,
  seed=9001
)

# Prediksi data testing
prediksi_cls_5 <- pred_nn_cls(model_cls_5, uji_cls)
prediksi_cls_10 <- pred_nn_cls(model_cls_10, uji_cls)

# Evaluasi model
evaluasi_cls_5 <- evaluasi_cls(
  uji_cls$hdi_class, prediksi_cls_5
)

evaluasi_cls_10 <- evaluasi_cls(
  uji_cls$hdi_class, prediksi_cls_10
)

# Confusion matrix
knitr::kable(
  evaluasi_cls_5$confusion_matrix,
  caption="Confusion Matrix NN dengan 5-Fold CV"
)
Confusion Matrix NN dengan 5-Fold CV
Rendah Tinggi
Rendah 9 2
Tinggi 2 18
knitr::kable(
  evaluasi_cls_10$confusion_matrix,
  caption="Confusion Matrix NN dengan 10-Fold CV"
)
Confusion Matrix NN dengan 10-Fold CV
Rendah Tinggi
Rendah 8 3
Tinggi 2 18
# Perbandingan metrik data testing
hasil_test_cls <- rbind(
  data.frame(
    Metode="5-Fold CV",
    Neuron=best_cls_5$Neuron,
    Decay=best_cls_5$Decay,
    evaluasi_cls_5$metrik
  ),
  data.frame(
    Metode="10-Fold CV",
    Neuron=best_cls_10$Neuron,
    Decay=best_cls_10$Decay,
    evaluasi_cls_10$metrik
  )
)

knitr::kable(
  hasil_test_cls,
  digits=4,
  row.names=FALSE,
  caption="Evaluasi NN Klasifikasi pada Data Testing"
)
Evaluasi NN Klasifikasi pada Data Testing
Metode Neuron Decay Accuracy Precision Recall F1_Score
5-Fold CV 2 0.100 0.8710 0.9000 0.9 0.900
10-Fold CV 4 0.001 0.8387 0.8571 0.9 0.878

7.9 Perbandingan 5-fold dan 10-fold CV

Tabel ini membandingkan hasil validasi dan hasil pengujian pada data testing. Perlu diingat bahwa nilai CV merupakan rata-rata hasil validasi, sedangkan nilai testing berasal dari data yang tidak digunakan dalam proses tuning.

hasil_perbandingan_cls <- rbind(
  data.frame(
    Metode="5-Fold CV",
    hasil_cv5_cls[1, c("Accuracy", "Precision", "Recall", "F1_Score")]
  ),
  data.frame(
    Metode="10-Fold CV",
    hasil_cv10_cls[1, c("Accuracy", "Precision", "Recall", "F1_Score")]
  ),
  data.frame(
    Metode="Testing 5-Fold",
    evaluasi_cls_5$metrik
  ),
  data.frame(
    Metode="Testing 10-Fold",
    evaluasi_cls_10$metrik
  )
)

knitr::kable(
  hasil_perbandingan_cls,
  digits=4,
  row.names=FALSE,
  caption="Perbandingan Hasil 5-Fold dan 10-Fold CV"
)
Perbandingan Hasil 5-Fold dan 10-Fold CV
Metode Accuracy Precision Recall F1_Score
5-Fold CV 0.9513 0.9528 0.975 0.9632
10-Fold CV 0.9519 0.9542 0.975 0.9632
Testing 5-Fold 0.8710 0.9000 0.900 0.9000
Testing 10-Fold 0.8387 0.8571 0.900 0.8780

7.10 Menyimpan hasil klasifikasi

dir.create("output", showWarnings=FALSE)

write.csv(
  hasil_cv5_cls,
  "output/nn_klasifikasi_hdi_cv5.csv",
  row.names=FALSE
)

write.csv(
  hasil_cv10_cls,
  "output/nn_klasifikasi_hdi_cv10.csv",
  row.names=FALSE
)

write.csv(
  hasil_test_cls,
  "output/nn_klasifikasi_hdi_testing.csv",
  row.names=FALSE
)

write.csv(
  data.frame(
    Aktual=uji_cls$hdi_class,
    Prediksi_5Fold=prediksi_cls_5,
    Prediksi_10Fold=prediksi_cls_10
  ),
  "output/nn_klasifikasi_hdi_prediksi.csv",
  row.names=FALSE
)

saveRDS(
  list(
    model_5fold=model_cls_5,
    model_10fold=model_cls_10,
    best_5fold=best_cls_5,
    best_10fold=best_cls_10,
    prediktor=prediktor_cls
  ),
  "output/nn_klasifikasi_hdi_model.rds"
)

cat("Hasil klasifikasi disimpan di folder output.\n")
## Hasil klasifikasi disimpan di folder output.
list.files("output")
##  [1] "data_preprocessed.csv"           "dnn_hdi_cv.csv"                 
##  [3] "dnn_hdi_model.rds"               "dnn_hdi_prediksi.csv"           
##  [5] "happiness_2019.csv"              "happiness_clean.csv"            
##  [7] "hdi_wikipedia.csv"               "nn_hdi_cv.csv"                  
##  [9] "nn_hdi_metrik.csv"               "nn_hdi_model.rds"               
## [11] "nn_hdi_prediksi.csv"             "nn_klasifikasi_hdi_cv10.csv"    
## [13] "nn_klasifikasi_hdi_cv5.csv"      "nn_klasifikasi_hdi_model.rds"   
## [15] "nn_klasifikasi_hdi_prediksi.csv" "nn_klasifikasi_hdi_testing.csv"

7.11 Kesimpulan

Berdasarkan hasil pengujian, Shallow NN memperoleh performa lebih baik dibandingkan Deep NN berdasarkan nilai Cross Validation RMSE, yaitu sebesar 0,0393, sedangkan Deep NN sebesar 0,0423. Konfigurasi terbaik Shallow NN menggunakan 2 neuron dengan decay 0,01, sedangkan Deep NN menggunakan arsitektur 6–8–4–1 dengan lambda 0,01 dan 300 epoch. Hasil ini menunjukkan bahwa jaringan yang lebih sederhana mampu memberikan kesalahan prediksi lebih rendah dibandingkan jaringan yang lebih kompleks pada percobaan ini. Dengan demikian, Shallow NN menjadi model regresi terbaik berdasarkan nilai Cross Validation.

8 Kesimpulan dan Perbandingan Metode

Berdasarkan hasil percobaan yang telah dilakukan, terdapat empat model yang digunakan, yaitu Support Vector Machine (SVM) untuk klasifikasi, Support Vector Regression (SVR) untuk regresi, Neural Network untuk klasifikasi, dan Neural Network untuk regresi. Keempat model tersebut dibandingkan berdasarkan hasil evaluasi masing-masing untuk mengetahui metode yang memberikan performa terbaik.

8.1 Perbandingan Metode Klasifikasi

Pada permasalahan klasifikasi, SVM memperoleh nilai accuracy sebesar 90.32% dan F1-score sebesar 92.68%. Sementara itu, Neural Network dengan 5-Fold Cross Validation memperoleh accuracy sebesar 87.10% dan F1-score sebesar 90%, sedangkan Neural Network dengan 10-Fold memperoleh accuracy sebesar 83.87% dan F1-score sebesar 87.80%.

Hasil tersebut menunjukkan bahwa SVM memiliki performa klasifikasi terbaik dibandingkan Neural Network berdasarkan hasil pengujian. SVM juga memiliki nilai recall sebesar 95%, yang menunjukkan kemampuan model dalam mengenali kelas positif dengan baik. Dengan demikian, SVM menjadi metode yang lebih unggul untuk permasalahan klasifikasi pada percobaan ini.

hasil_klasifikasi <- data.frame(
  Metode = c(
    "SVM",
    "NN Klasifikasi (5-Fold)",
    "NN Klasifikasi (10-Fold)"
  ),
  Accuracy = c(0.9032, 0.8710, 0.8387),
  Precision = c(0.9048, 0.9000, 0.8571),
  Recall = c(0.9500, 0.9000, 0.9000),
  F1_Score = c(0.9268, 0.9000, 0.8780)
)

knitr::kable(
  hasil_klasifikasi,
  caption = "Perbandingan Performa Metode Klasifikasi",
  digits = 4
)
Perbandingan Performa Metode Klasifikasi
Metode Accuracy Precision Recall F1_Score
SVM 0.9032 0.9048 0.95 0.9268
NN Klasifikasi (5-Fold) 0.8710 0.9000 0.90 0.9000
NN Klasifikasi (10-Fold) 0.8387 0.8571 0.90 0.8780

8.2 Perbandingan Metode Regresi

Pada permasalahan regresi, metode SVR Linear hasil tuning memperoleh nilai RMSE sebesar 0.0366 dan R-squared sebesar 0.9241. Hasil tersebut menunjukkan bahwa model mampu menjelaskan sekitar 92.41% variasi data. Meskipun nilai MAE SVR Linear baseline sedikit lebih rendah, yaitu 0.0286, SVR Linear hasil tuning tetap memberikan hasil lebih baik berdasarkan RMSE dan R-squared.

Sementara itu, pada metode Neural Network regresi, model Shallow memperoleh nilai Cross Validation RMSE sebesar 0.0393, lebih rendah dibandingkan model Deep yang memperoleh nilai 0.0423. Hal ini menunjukkan bahwa Shallow Neural Network memberikan performa validasi yang lebih baik dibandingkan Deep Neural Network.

Namun, perbandingan langsung antara SVR dan Neural Network regresi belum dapat dilakukan secara menyeluruh karena hasil evaluasi yang tersedia menggunakan skema pengujian yang berbeda.

hasil_regresi <- data.frame(
  Metode = c(
    "SVR Linear Baseline",
    "SVR Radial Baseline",
    "SVR Linear Tuning",
    "NN Regresi Shallow",
    "NN Regresi Deep"
  ),
  MAE = c(0.0286, 0.0312, 0.0287, NA, NA),
  RMSE = c(0.0372, 0.0384, 0.0366, 0.0393, 0.0423),
  R_Squared = c(0.9213, 0.9163, 0.9241, NA, NA)
)

knitr::kable(
  hasil_regresi,
  caption = "Perbandingan Performa Metode Regresi",
  digits = 4
)
Perbandingan Performa Metode Regresi
Metode MAE RMSE R_Squared
SVR Linear Baseline 0.0286 0.0372 0.9213
SVR Radial Baseline 0.0312 0.0384 0.9163
SVR Linear Tuning 0.0287 0.0366 0.9241
NN Regresi Shallow NA 0.0393 NA
NN Regresi Deep NA 0.0423 NA

8.3 Kesimpulan Akhir

Berdasarkan keseluruhan hasil percobaan, dapat disimpulkan bahwa metode terbaik bergantung pada jenis permasalahan yang diselesaikan. Untuk permasalahan klasifikasi, SVM menjadi metode terbaik karena menghasilkan accuracy dan F1-score tertinggi dibandingkan Neural Network pada data testing.

Untuk permasalahan regresi, SVR Linear hasil tuning memberikan performa terbaik di antara model SVR yang diuji, dengan nilai RMSE sebesar 0.0366 dan R-squared sebesar 0.9241. Adapun pada Neural Network regresi, model Shallow lebih unggul dibandingkan model Deep berdasarkan nilai Cross Validation RMSE.

Dengan demikian, SVM direkomendasikan untuk permasalahan klasifikasi, sedangkan SVR Linear hasil tuning menjadi pilihan terbaik di antara model SVR untuk regresi. Sementara itu, Shallow Neural Network merupakan konfigurasi Neural Network regresi yang memberikan hasil validasi terbaik. Secara keseluruhan, pemilihan metode perlu disesuaikan dengan jenis permasalahan serta hasil evaluasi model yang diperoleh.