Perkembangan machine learning menyebabkan kebutuhan terhadap data yang berkualitas semakin penting. Data yang diperoleh dari berbagai sumber tidak selalu dapat langsung digunakan dalam pemodelan karena dapat memiliki perbedaan skala, bentuk data yang beragam, variabel kategorik, maupun fitur yang kurang relevan. Oleh karena itu, diperlukan proses pengolahan fitur agar data dapat memberikan informasi yang lebih sesuai bagi model machine learning. Salah satu proses penting dalam tahap tersebut adalah feature engineering, yaitu proses mengubah, membuat, atau memilih fitur dari data yang tersedia sehingga dapat meningkatkan kualitas representasi data untuk pemodelan (Vasques, 2024).
Feature engineering mencakup berbagai teknik, seperti transformasi variabel, encoding data kategorik, pembuatan fitur baru, serta pemilihan fitur yang relevan. Penerapan teknik tersebut dapat membantu menyederhanakan data dan menghasilkan fitur yang lebih informatif bagi algoritma machine learning (Mumuni & Mumuni, 2025). RStudio sebagai lingkungan pengembangan untuk bahasa pemrograman R dapat digunakan untuk melakukan berbagai teknik feature engineering. Dengan memanfaatkan fungsi-fungsi dalam R maupun berbagai paket pendukung, proses transformasi dan pembentukan fitur dapat dilakukan secara sistematis dan dapat direproduksi. Pemahaman terhadap feature engineering di RStudio penting bagi mahasiswa karena memungkinkan proses pengolahan data dilakukan secara langsung sebelum data digunakan untuk membangun model statistik maupun machine learning.
Berdasarkan latar belakang, rumusan masalah yang dapat disimpulkan adalah:
1. Bagaimana cara memahami konsep dari berbagai jenis feature engineering pada RStudio?
2. Bagaimana cara melakukan teknik feature engineering di program RStudio?
Berdasarkan rumusan masalah, tujuan yang dapat disimpulkan adalah:
1. Mahasiswa memahami konsep dari berbagai jenis feature engineering pada RStudio.
2. Mahasiswa dapat melakukan teknik feature engineering di program RStudio.
Adapun manfaat pada penelitian ini, yaitu:
1. Bagi penulis
a. Mengembangkan dan menerapkan wawasan ilmu yang diperoleh dalam bidang statistika khususnya tentang feature engineering pada RStudio.
b. Dapat memberikan informasi tentang feature engineering pada RStudio.
2. Bagi pembaca
a. Menambah ilmu pengetahuan dan pemahaman yang berkaitan tentang feature engineering pada RStudio.
b. Menjadi rujukan bagi peneliti selanjutnya
Adapun batasan masalah pada laporan praktikum pertemuan ini adalah Gunakan dataset data curah hujan.xlsx untuk melakukan preprocessing data. Menggunakan mode imputation, stratify splitting, dan Standard Scaler. Melakukan interpolasi pada Keadaan_Cuaca, capping pada data numerik, serta penanganan imbalance data menggunakan SMOTE. Tampilkan dan interpretasikan setiap output!
Feature engineering merupakan proses mengolah fitur yang terdapat dalam suatu dataset agar menghasilkan representasi data yang lebih sesuai untuk digunakan dalam proses machine learning. Proses ini dapat dilakukan dengan melakukan transformasi terhadap fitur yang sudah tersedia, membuat fitur baru, mengekstraksi informasi dari fitur, maupun memilih fitur yang dianggap penting. Feature engineering merupakan bagian dari tahapan persiapan data yang dapat dilakukan sebelum data digunakan dalam pemodelan (Mumuni & Mumuni, 2025).
Beberapa proses yang termasuk dalam feature engineering antara lain feature transformation, feature construction, feature extraction, dan feature selection. Pemilihan teknik bergantung pada karakteristik data serta tujuan pemodelan yang dilakukan. Feature engineering yang tepat dapat membantu menghasilkan data yang lebih informatif dan sesuai dengan algoritma yang digunakan (Rachmatullah, 2025; Mumuni & Mumuni, 2025).
Feature engineering memerlukan pemahaman yang baik karena melibatkan kombinasi analisis data, pengetahuan tentang data, dan sedikit intuisi. Tujuan dari feature engineering adalah menyederhanakan dan mempercepat transformasi data sekaligus meningkatkan akurasi model. kualitas fitur jauh lebih penting daripada seberapa canggih algoritma yang dipilih. Berikut merupakan beberapa jenis feature engineering yang harus dilakukan (Fransiska, 2026):
Jenis data yang digunakan di dalam penelitian ini berupa data kuantitatif. Data kuantitatif merupakan data atau informasi yang didapatkan dalam bentuk angka atau numerik. Data yang digunakan dalam penelitian ini merupakan data sekunder yang diperoleh dari Asisten Praktikum Machine Learning and Modern Prediction.
Variabel penelitian adalah karakter atau segala sesuatu yang menjadi perhatian dalam suatu penelitian. Pada penelitian ini terdapat 5 variabel. Variabel yang digunakan dalam praktikum ini meliputi hujan, suhu, kelembapan, keadaan cuaca dan kecepatan angin.
Berikut adalah algoritma untuk menyelesaiankan batasan masalah:
1.Mulai.
2.Menghitung distribusi kelas pada variabel target y_train.
3.Menggabungkan data prediktor hasil encoding dengan variabel target Hujan.
4.Mengubah variabel target Hujan menjadi tipe factor agar dapat digunakan dalam proses SMOTE.
5.Menentukan seed untuk menjaga hasil proses tetap konsisten.
6.Membuat recipe SMOTE dengan variabel Hujan sebagai target.
7.Menentukan over_ratio = 1 dan jumlah tetangga (neighbors) sebanyak 5.
8.Melakukan prep() berdasarkan data training.
9.Menerapkan SMOTE menggunakan bake() untuk menghasilkan data training yang lebih seimbang.
10.Menghasilkan train_balanced sebagai dataset hasil penanganan imbalanced dataset.
11.Selesai.
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.2.1 ✔ readr 2.2.0
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.3 ✔ tibble 3.3.1
## ✔ lubridate 1.9.5 ✔ tidyr 1.3.2
## ✔ purrr 1.2.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
##
## Attaching package: 'e1071'
##
## The following object is masked from 'package:rsample':
##
## permutations
##
## The following object is masked from 'package:ggplot2':
##
## element
##
## Attaching package: 'gridExtra'
##
## The following object is masked from 'package:dplyr':
##
## combine
##
## Attaching package: 'recipes'
##
## The following object is masked from 'package:stringr':
##
## fixed
##
## The following object is masked from 'package:stats':
##
## step
## Loading required package: lattice
##
## Attaching package: 'caret'
##
## The following objects are masked from 'package:DescTools':
##
## MAE, RMSE
##
## The following object is masked from 'package:rsample':
##
## calibration
##
## The following object is masked from 'package:purrr':
##
## lift
data <- read_excel("C:/Users/LENOVO/Downloads/data curah hujan (1).xlsx")
# Melihat 6 baris pertama
head(data)## # A tibble: 6 × 5
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 23 95 5 NA
## 2 1 24 90 1 NA
## 3 1 26.8 77 1 NA
## 4 1 29.6 62 2 2
## 5 1 30.8 56 1 7
## 6 1 31 55 1 7
## tibble [743 × 5] (S3: tbl_df/tbl/data.frame)
## $ Hujan : num [1:743] 1 1 1 1 1 1 1 2 2 2 ...
## $ Suhu : num [1:743] 23 24 26.8 29.6 30.8 31 30.4 30.9 30.2 29.7 ...
## $ Kelembapan : num [1:743] 95 90 77 62 56 55 57 58 62 62 ...
## $ Keadaan_Cuaca : num [1:743] 5 1 1 2 1 1 3 2 2 2 ...
## $ Kecepatan_Angin: num [1:743] NA NA NA 2 7 7 9 10 8 7 ...
## [1] 743 5
## [1] "Hujan" "Suhu" "Kelembapan" "Keadaan_Cuaca"
## [5] "Kecepatan_Angin"
## Hujan Suhu Kelembapan Keadaan_Cuaca
## Min. :1.000 Min. :22.60 Min. : 52.00 Min. : 1.00
## 1st Qu.:2.000 1st Qu.:24.30 1st Qu.: 75.00 1st Qu.: 2.00
## Median :2.000 Median :26.00 Median : 86.00 Median : 2.00
## Mean :1.779 Mean :26.54 Mean : 83.88 Mean :15.11
## 3rd Qu.:2.000 3rd Qu.:28.90 3rd Qu.: 94.00 3rd Qu.:15.00
## Max. :2.000 Max. :32.00 Max. :100.00 Max. :97.00
## NAs :9
## Kecepatan_Angin
## Min. : 2.000
## 1st Qu.: 4.000
## Median : 6.000
## Mean : 6.655
## 3rd Qu.: 9.000
## Max. :21.000
## NAs :314
Data terdiri atas 743 observasi dan 5 variabel, yaitu Hujan, Suhu, Kelembapan, Keadaan_Cuaca, dan Kecepatan_Angin. Variabel Hujan digunakan sebagai target klasifikasi, sedangkan variabel lainnya digunakan sebagai prediktor. Statistik deskriptif menunjukkan bahwa Suhu memiliki rata-rata 26,54°C dan median 26°C, sedangkan Kelembapan memiliki rata-rata 83,88% dan median 86%. Kecepatan_Angin memiliki rata-rata 6,655 dan median 6. Pada variabel Hujan, rata-rata sebesar 1,779 menunjukkan bahwa kelas 2 lebih banyak ditemukan daripada kelas 1. Selain itu, Keadaan_Cuaca memiliki rentang nilai 1–97, sehingga makna dan pengkodean variabel tersebut perlu diperiksa.
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
## 0 0 0 9 314
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
## 0.00000000 0.00000000 0.00000000 0.01211306 0.42261104
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
## 0.000000 0.000000 0.000000 1.211306 42.261104
# Melihat baris yang memiliki missing value pada
# variabel Keadaan_Cuaca
data %>%
filter(is.na(Keadaan_Cuaca))## # A tibble: 9 × 5
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2 26 92 NA NA
## 2 1 23.8 98 NA 5
## 3 1 23.3 97 NA NA
## 4 2 28.8 79 NA 12
## 5 2 24.6 92 NA NA
## 6 1 22.8 98 NA 4
## 7 2 25.5 96 NA NA
## 8 2 27 80 NA 4
## 9 2 31 57 NA 6
# 4. PENANGANAN MISSING VALUE
# 4.1 Drop Missing Value
data_dropna <- data %>%
drop_na()
cat("Jumlah baris sebelum drop NA :", nrow(data), "\n")## Jumlah baris sebelum drop NA : 743
## Jumlah baris setelah drop NA : 424
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
## 0 0 0 0 0
# 4.2 Forward Fill
df_imp1 <- data %>%
fill(Keadaan_Cuaca, .direction = "down")
cat("\nMissing value setelah Forward Fill:\n")##
## Missing value setelah Forward Fill:
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
## 0 0 0 0 314
# 4.3 Mean Imputation
df_imp2 <- data
df_imp2$Kecepatan_Angin[
is.na(df_imp2$Kecepatan_Angin)
] <- mean(
df_imp2$Kecepatan_Angin,
na.rm = TRUE
)
cat("\nMissing value setelah Mean Imputation:\n")##
## Missing value setelah Mean Imputation:
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
## 0 0 0 9 0
# 4.4 Median Imputation
df_imp3 <- data
df_imp3$Kecepatan_Angin[
is.na(df_imp3$Kecepatan_Angin)
] <- median(
df_imp3$Kecepatan_Angin,
na.rm = TRUE
)
cat("\nMissing value setelah Median Imputation:\n")##
## Missing value setelah Median Imputation:
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
## 0 0 0 9 0
# 4.5 Mode Imputation
df_imp5 <- data
# Mencari modus
modus <- names(
sort(
table(df_imp5$Kecepatan_Angin),
decreasing = TRUE
)
)[1]
cat("\nModus Kecepatan_Angin :", modus, "\n")##
## Modus Kecepatan_Angin : 3
# Mengganti missing value dengan modus
df_imp5$Kecepatan_Angin[
is.na(df_imp5$Kecepatan_Angin)
] <- as.numeric(modus)
cat("\nMissing value setelah Mode Imputation:\n")##
## Missing value setelah Mode Imputation:
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
## 0 0 0 9 0
# 5. INTERPOLASI
df_imp4 <- data
# Interpolasi Keadaan_Cuaca
df_imp4$Keadaan_Cuaca <- na.approx(
df_imp4$Keadaan_Cuaca,
na.rm = FALSE
)
cat("\nMissing value setelah interpolasi:\n")##
## Missing value setelah interpolasi:
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
## 0 0 0 0 314
# Median Imputation untuk Kecepatan_Angin
median_value <- median(
df_imp4$Kecepatan_Angin,
na.rm = TRUE
)
df_imp4$Kecepatan_Angin[
is.na(df_imp4$Kecepatan_Angin)
] <- median_value
cat("\nMissing value setelah Median Imputation:\n")##
## Missing value setelah Median Imputation:
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
## 0 0 0 0 0
## # A tibble: 10 × 5
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 23 95 5 6
## 2 1 24 90 1 6
## 3 1 26.8 77 1 6
## 4 1 29.6 62 2 2
## 5 1 30.8 56 1 7
## 6 1 31 55 1 7
## 7 1 30.4 57 3 9
## 8 2 30.9 58 2 10
## 9 2 30.2 62 2 8
## 10 2 29.7 62 2 7
Pemeriksaan jumlah dan persentase missing value, kemudian dibandingkan beberapa metode penanganannya, yaitu penghapusan baris, forward fill, imputasi mean, median, modus, dan interpolasi. Hasil pemeriksaan menunjukkan bahwa Keadaan_Cuaca memiliki 9 nilai hilang (1,21%), sedangkan Kecepatan_Angin memiliki 314 nilai hilang (42,26%). Pada data akhir, nilai hilang Keadaan_Cuaca diisi menggunakan interpolasi, sedangkan Kecepatan_Angin diisi menggunakan median. Hasilnya, seluruh 743 observasi berhasil dipertahankan tanpa missing value. Namun, interpolasi pada Keadaan_Cuaca perlu dipastikan sesuai dengan makna variabelnya, terutama jika nilai tersebut merupakan kode kategori cuaca.
##
## Nilai unik Keadaan_Cuaca:
## [1] 5.0 1.0 2.0 3.0 14.0 60.0 61.0 21.0 29.0 10.0 16.0 62.0 63.0 65.0 15.0
## [16] 17.0 13.0 95.0 91.0 49.0 97.0 56.0 1.5
# Menentukan batas bin
bins <- c(
0, 10, 20, 30, 40,
50, 60, 70, 80, 90, 100
)
# Menentukan label
labels <- 0:9
# Melakukan binning
df_imp4$Keadaan_Cuaca_reduced <- cut(
df_imp4$Keadaan_Cuaca,
breaks = bins,
labels = labels,
right = TRUE,
include.lowest = TRUE
)
# Melihat hasil reduksi kardinalitas
cat("\n--- HASIL REDUKSI KARDINALITAS ---\n")##
## --- HASIL REDUKSI KARDINALITAS ---
## # A tibble: 10 × 6
## Hujan Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin Keadaan_Cuaca_reduced
## <dbl> <dbl> <dbl> <dbl> <dbl> <fct>
## 1 1 23.2 97 61 4 6
## 2 2 24.6 91 2 2 0
## 3 2 29.2 72 2 8 0
## 4 2 29.2 74 2 9 0
## 5 1 23.7 98 60 6 5
## 6 2 23.6 96 61 6 6
## 7 2 23.6 96 17 6 1
## 8 2 29.3 70 3 9 0
## 9 1 23.8 97 65 9 6
## 10 2 24.2 94 2 6 0
# Jumlah kategori sebelum
cat(
"\nJumlah kategori Keadaan_Cuaca asli :",
length(unique(df_imp4$Keadaan_Cuaca)),
"\n"
)##
## Jumlah kategori Keadaan_Cuaca asli : 23
# Jumlah kategori setelah
cat(
"Jumlah kategori Keadaan_Cuaca_reduced :",
length(unique(df_imp4$Keadaan_Cuaca_reduced)),
"\n"
)## Jumlah kategori Keadaan_Cuaca_reduced : 7
##
## Kategori unik setelah reduksi:
## [1] 0 1 5 6 2 9 4
## Levels: 0 1 2 3 4 5 6 7 8 9
Reduksi kardinalitas dilakukan dengan mengelompokkan nilai Keadaan_Cuaca ke dalam interval 0–100 menggunakan metode binning. Hasilnya, terdapat 7 kategori yang teramati dari sebelumnya 23 nilai unik. Pengelompokan ini menyederhanakan variasi nilai, tetapi perlu disesuaikan dengan arti kode cuaca agar kategori yang berbeda tidak tergabung secara keliru.
set.seed(200)
# Membagi data 80% training dan 20% testing
# dengan stratifikasi berdasarkan Hujan
split_stratify <- initial_split(
df_imp4,
prop = 0.8,
strata = Hujan
)
# Data training
train_data <- training(split_stratify)
# Data testing
test_data <- testing(split_stratify)
# Menampilkan ukuran data
cat("\n--- UKURAN DATA ---\n")##
## --- UKURAN DATA ---
## Jumlah data awal : 743
## Jumlah data training : 594
## Jumlah data testing : 149
##
## --- PROPORSI TARGET DATA AWAL ---
##
## 1 2
## 0.2207268 0.7792732
##
## --- PROPORSI TARGET TRAINING ---
##
## 1 2
## 0.2205387 0.7794613
##
## --- PROPORSI TARGET TESTING ---
##
## 1 2
## 0.2214765 0.7785235
# 8. MEMISAHKAN X DAN Y
X_train <- train_data %>%
select(-Hujan)
X_test <- test_data %>%
select(-Hujan)
y_train <- train_data$Hujan
y_test <- test_data$Hujan
# Melihat dimensi
cat("\nDimensi X_train :")##
## Dimensi X_train :
## [1] 594 5
##
## Dimensi X_test :
## [1] 149 5
# 9. IDENTIFIKASI DATA NUMERIK
list_num <- c(
"Suhu",
"Kelembapan",
"Kecepatan_Angin"
)
cat("\nVariabel numerik yang digunakan:\n")##
## Variabel numerik yang digunakan:
## [1] "Suhu" "Kelembapan" "Kecepatan_Angin"
Pembagian data dilakukan dengan proporsi 80% untuk training dan 20% untuk testing menggunakan stratifikasi berdasarkan Hujan. Hasilnya, diperoleh 594 observasi training dan 149 observasi testing. Proporsi kelas 1 dan 2 tetap mendekati distribusi data awal, sehingga pembagian data tidak mengubah keseimbangan relatif kedua kelas secara berarti.
## Handling Outlier
list_outlier <- c()
list_lower_bound <- c()
list_upper_bound <- c()
for (i in list_num) {
Q1 <- quantile(
X_train[[i]],
0.25,
na.rm = TRUE
)
Q3 <- quantile(
X_train[[i]],
0.75,
na.rm = TRUE
)
IQR_val <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR_val
upper_bound <- Q3 + 1.5 * IQR_val
list_lower_bound <- c(
list_lower_bound,
lower_bound
)
list_upper_bound <- c(
list_upper_bound,
upper_bound
)
num_outliers_lower <- sum(
X_train[[i]] < lower_bound,
na.rm = TRUE
)
num_outliers_upper <- sum(
X_train[[i]] > upper_bound,
na.rm = TRUE
)
total_outliers <-
num_outliers_lower +
num_outliers_upper
list_outlier <- c(
list_outlier,
total_outliers
)
}
# Membuat tabel outlier
outliers <- data.frame(
Kolom = list_num,
Jumlah_Outlier = list_outlier,
Lower_Bound = list_lower_bound,
Upper_Bound = list_upper_bound
)
cat("\n--- HASIL IDENTIFIKASI OUTLIER ---\n")##
## --- HASIL IDENTIFIKASI OUTLIER ---
## Kolom Jumlah_Outlier Lower_Bound Upper_Bound
## 1 Suhu 0 17.4625 35.7625
## 2 Kelembapan 0 46.5000 122.5000
## 3 Kecepatan_Angin 106 2.6250 9.6250
handling outlier atau identifikasi pencilan dilakukan menggunakan metode Interquartile Range (IQR) dengan menentukan batas bawah dan batas atas pada setiap variabel numerik. Berdasarkan hasil analisis dan plot boxplot, variabel Suhu memiliki batas IQR sebesar 17,4625–35,7625 dan Kelembapan sebesar 46,5–122,5, tetapi keduanya tidak menunjukkan adanya outlier. Sementara itu, variabel Kecepatan_Angin memiliki 106 outlier dengan batas bawah 2,625 dan batas atas 9,625, yang terlihat sebagai titik-titik di luar whisker pada boxplot. Oleh karena itu, penanganan outlier dilakukan menggunakan metode capping atau Winsorization, yaitu membatasi nilai yang berada di luar batas IQR tanpa menghapus data. Hasilnya, nilai ekstrem pada Kecepatan_Angin dibatasi sehingga pengaruh outlier terhadap analisis selanjutnya dapat dikurangi, sedangkan data Suhu dan Kelembapan relatif tidak mengalami perubahan.
X_train_capped <- X_train
X_test_capped <- X_test
# Melakukan capping untuk setiap variabel numerik
for (i in seq_along(list_num)) {
col <- list_num[i]
lower <- list_lower_bound[i]
upper <- list_upper_bound[i]
# Capping training
X_train_capped[[col]] <- Winsorize(
X_train[[col]],
val = c(lower, upper)
)
# Capping testing
X_test_capped[[col]] <- Winsorize(
X_test[[col]],
val = c(lower, upper)
)
}
# Melihat ringkasan data setelah capping
cat("\n--- DATA SETELAH CAPPING ---\n")##
## --- DATA SETELAH CAPPING ---
## Suhu Kelembapan Kecepatan_Angin
## Min. :22.60 Min. : 52.00 Min. :2.625
## 1st Qu.:24.32 1st Qu.: 75.00 1st Qu.:5.250
## Median :26.00 Median : 86.00 Median :6.000
## Mean :26.56 Mean : 83.76 Mean :6.170
## 3rd Qu.:28.90 3rd Qu.: 94.00 3rd Qu.:7.000
## Max. :32.00 Max. :100.00 Max. :9.625
# ============================================================
# 13. VISUALISASI SEBELUM DAN SESUDAH CAPPING
# ============================================================
diagnostic_plots <- function(
df,
variable,
judul = ""
) {
p1 <- ggplot(
df,
aes(x = .data[[variable]])
) +
geom_histogram(
bins = 30,
fill = "#008080",
color = "black"
) +
ggtitle(
paste("Histogram", judul)
) +
theme_minimal()
p2 <- ggplot(
df,
aes(y = .data[[variable]])
) +
geom_boxplot(
fill = "#008080"
) +
ggtitle(
paste("Boxplot", judul)
) +
theme_minimal()
grid.arrange(
p1,
p2,
ncol = 2
)
}
# Visualisasi setiap variabel
for (col in list_num) {
cat(
"\n",
col,
"- SEBELUM CAPPING\n"
)
diagnostic_plots(
X_train,
col,
"Sebelum Capping"
)
cat(
"\n",
col,
"- SETELAH CAPPING\n"
)
diagnostic_plots(
X_train_capped,
col,
"Setelah Capping"
)
}##
## Suhu - SEBELUM CAPPING
##
## Suhu - SETELAH CAPPING
##
## Kelembapan - SEBELUM CAPPING
##
## Kelembapan - SETELAH CAPPING
##
## Kecepatan_Angin - SEBELUM CAPPING
##
## Kecepatan_Angin - SETELAH CAPPING
Berdasarkan hasil penanganan outlier menggunakan fungsi Winsorize() dengan membatasi nilai berdasarkan batas bawah dan batas atas IQR. Berdasarkan plot histogram dan boxplot sebelum capping, variabel Suhu menunjukkan sebaran data yang terkonsentrasi pada beberapa rentang suhu, sedangkan Kelembapan didominasi oleh nilai kelembapan yang relatif tinggi. Kedua variabel tersebut tidak memiliki outlier berdasarkan metode IQR sehingga bentuk distribusinya relatif tidak berubah setelah capping. Sementara itu, plot Kecepatan_Angin menunjukkan beberapa titik di luar whisker boxplot yang menandakan adanya outlier. Setelah capping, nilai Kecepatan_Angin dibatasi pada rentang 2,625–9,625 sehingga titik ekstrem tidak lagi berada di luar batas tersebut. Dengan demikian, capping terutama memengaruhi variabel Kecepatan_Angin dengan membatasi nilai ekstrem tanpa menghapus observasi dari data.
nilai_skew <- c()
nilai_skew_normal <- c()
for (i in list_num) {
skew_val <- skewness(
X_train_capped[[i]],
na.rm = TRUE
)
if (
skew_val >= -0.5 &&
skew_val <= 0.5
) {
nilai_skew_normal <-
c(nilai_skew_normal, i)
} else {
nilai_skew <-
c(nilai_skew, i)
}
}
cat(
"\nKolom untuk Standard Scaler:\n"
)##
## Kolom untuk Standard Scaler:
## [1] "Suhu" "Kecepatan_Angin"
##
## Kolom dengan skewness tinggi:
## [1] "Kelembapan"
# ------------------------------------------------------------
# Standard Scaler
# ------------------------------------------------------------
mean_val <- sapply(
X_train_capped[nilai_skew_normal],
mean,
na.rm = TRUE
)
sd_val <- sapply(
X_train_capped[nilai_skew_normal],
sd,
na.rm = TRUE
)
for (col in nilai_skew_normal) {
X_train_capped[[col]] <-
(
X_train_capped[[col]] -
mean_val[col]
) /
sd_val[col]
X_test_capped[[col]] <-
(
X_test_capped[[col]] -
mean_val[col]
) /
sd_val[col]
}
# ------------------------------------------------------------
# Robust Scaler untuk variabel skewed
# ------------------------------------------------------------
if (length(nilai_skew) > 0) {
median_val <- sapply(
X_train_capped[nilai_skew],
median,
na.rm = TRUE
)
iqr_val <- sapply(
X_train_capped[nilai_skew],
IQR,
na.rm = TRUE
)
for (col in nilai_skew) {
X_train_capped[[col]] <-
(
X_train_capped[[col]] -
median_val[col]
) /
iqr_val[col]
X_test_capped[[col]] <-
(
X_test_capped[[col]] -
median_val[col]
) /
iqr_val[col]
}
}
# Menyimpan hasil scaling
X_train_scale <- X_train_capped
X_test_scale <- X_test_capped
# Melihat hasil scaling
cat("\n--- HASIL STANDARD SCALER ---\n")##
## --- HASIL STANDARD SCALER ---
## # A tibble: 6 × 3
## Suhu Kelembapan Kecepatan_Angin
## <dbl> <dbl> <dbl>
## 1 -1.39 0.474 -0.0874
## 2 -1.00 0.211 -0.0874
## 3 0.0947 -0.474 -0.0874
## 4 1.19 -1.26 -1.82
## 5 1.66 -1.58 0.425
## 6 1.50 -1.53 1.45
##
## Mean setelah scaling:
## Suhu Kecepatan_Angin
## -7.045562e-17 -2.388123e-16
##
## Standar deviasi setelah scaling:
## Suhu Kecepatan_Angin
## 1 1
Pada tahap scaling data, variabel Suhu dan Kecepatan_Angin ditransformasikan menggunakan metode Standard Scaling, sedangkan variabel Kelembapan menggunakan median dan Interquartile Range (IQR). Proses ini bertujuan menyamakan skala data agar perbedaan rentang nilai antarvariabel tidak terlalu memengaruhi pemodelan. Hasil scaling menunjukkan bahwa Suhu dan Kecepatan_Angin memiliki rata-rata mendekati nol dan standar deviasi mendekati satu. Parameter scaling dihitung dari data latih, kemudian diterapkan pada data uji agar transformasi tetap konsisten.
X_train_scale$Keadaan_Cuaca_reduced <-
as.factor(
X_train_scale$Keadaan_Cuaca_reduced
)
X_test_scale$Keadaan_Cuaca_reduced <-
as.factor(
X_test_scale$Keadaan_Cuaca_reduced
)
# Variabel kategorik
list_cat <- c(
"Keadaan_Cuaca_reduced"
)
# Membuat recipe One Hot Encoding
resep_encode <- recipe(
~ Keadaan_Cuaca_reduced,
data = X_train_scale
) %>%
step_dummy(
all_of(list_cat),
one_hot = TRUE
) %>%
prep(
training = X_train_scale
)
# Melakukan encoding
X_train_encoded <- bake(
resep_encode,
new_data = X_train_scale
)
X_test_encoded <- bake(
resep_encode,
new_data = X_test_scale
)
# Melihat hasil encoding
cat("\n--- HASIL ONE HOT ENCODING TRAINING ---\n")##
## --- HASIL ONE HOT ENCODING TRAINING ---
## # A tibble: 6 × 10
## Keadaan_Cuaca_reduced_X0 Keadaan_Cuaca_reduced_X1 Keadaan_Cuaca_reduced_X2
## <dbl> <dbl> <dbl>
## 1 1 0 0
## 2 1 0 0
## 3 1 0 0
## 4 1 0 0
## 5 1 0 0
## 6 1 0 0
## # ℹ 7 more variables: Keadaan_Cuaca_reduced_X3 <dbl>,
## # Keadaan_Cuaca_reduced_X4 <dbl>, Keadaan_Cuaca_reduced_X5 <dbl>,
## # Keadaan_Cuaca_reduced_X6 <dbl>, Keadaan_Cuaca_reduced_X7 <dbl>,
## # Keadaan_Cuaca_reduced_X8 <dbl>, Keadaan_Cuaca_reduced_X9 <dbl>
##
## --- HASIL ONE HOT ENCODING TESTING ---
## # A tibble: 6 × 10
## Keadaan_Cuaca_reduced_X0 Keadaan_Cuaca_reduced_X1 Keadaan_Cuaca_reduced_X2
## <dbl> <dbl> <dbl>
## 1 1 0 0
## 2 1 0 0
## 3 1 0 0
## 4 1 0 0
## 5 1 0 0
## 6 1 0 0
## # ℹ 7 more variables: Keadaan_Cuaca_reduced_X3 <dbl>,
## # Keadaan_Cuaca_reduced_X4 <dbl>, Keadaan_Cuaca_reduced_X5 <dbl>,
## # Keadaan_Cuaca_reduced_X6 <dbl>, Keadaan_Cuaca_reduced_X7 <dbl>,
## # Keadaan_Cuaca_reduced_X8 <dbl>, Keadaan_Cuaca_reduced_X9 <dbl>
Berdasarkan hasil, variabel kategorik Keadaan_Cuaca_reduced diubah menjadi beberapa variabel biner menggunakan metode One-Hot Encoding. Berdasarkan hasil pengolahan, terbentuk 10 variabel dummy, yaitu X0 hingga X9, yang mewakili kategori cuaca. Setiap variabel dummy menunjukkan ada atau tidaknya suatu kategori pada observasi, sehingga data kategorik dapat digunakan dalam proses pemodelan. Dengan demikian, encoding membantu mengubah informasi cuaca menjadi format numerik tanpa memberikan urutan tertentu pada setiap kategori.
##
## --- DISTRIBUSI KELAS SEBELUM SMOTE ---
## y_train
## 1 2
## 131 463
##
## Proporsi kelas sebelum SMOTE:
## y_train
## 1 2
## 0.2205387 0.7794613
# Menggabungkan X dan y
train_full <- X_train_encoded %>%
mutate(
Hujan = y_train
)
# Target harus berupa factor
train_full$Hujan <-
factor(train_full$Hujan)
# SMOTE
set.seed(42)
resep_smote <- recipe(
Hujan ~ .,
data = train_full
) %>%
step_smote(
Hujan,
over_ratio = 1,
neighbors = 5
)
# Prep recipe
resep_smote_prep <- prep(
resep_smote,
training = train_full
)
# Melakukan SMOTE
train_balanced <- bake(
resep_smote_prep,
new_data = NULL
)
# Memisahkan kembali X dan y
X_train_balanced <-
train_balanced %>%
select(-Hujan)
y_train_balanced <-
train_balanced$Hujan
# HASIL SETELAH SMOTE
cat("\n--- DISTRIBUSI KELAS SETELAH SMOTE ---\n")##
## --- DISTRIBUSI KELAS SETELAH SMOTE ---
## y_train_balanced
## 1 2
## 463 463
##
## Proporsi kelas setelah SMOTE:
## y_train_balanced
## 1 2
## 0.5 0.5
# 18. VISUALISASI SEBELUM DAN SESUDAH SMOTE
par(mfrow = c(1, 2))
barplot(
table(y_train),
main = "Sebelum SMOTE",
xlab = "Kelas Hujan",
ylab = "Frekuensi"
)
barplot(
table(y_train_balanced),
main = "Setelah SMOTE",
xlab = "Kelas Hujan",
ylab = "Frekuensi"
)##
## ============================================
## HASIL AKHIR PREPROCESSING
## ============================================
## Jumlah data awal : 743
## Data training awal : 594
## Data testing : 149
## Data training setelah SMOTE : 926
## Jumlah fitur setelah encoding : 10
##
## Distribusi kelas sebelum SMOTE:
## y_train
## 1 2
## 131 463
##
## Distribusi kelas setelah SMOTE:
## y_train_balanced
## 1 2
## 463 463
Dilakukan penyeimbangan kelas variabel target Hujan menggunakan metode Synthetic Minority Over-sampling Technique (SMOTE). Sebelum balancing, kelas 1 berjumlah 131 observasi, sedangkan kelas 2 berjumlah 463 observasi, sehingga distribusi kelas tidak seimbang. Setelah SMOTE diterapkan dengan over_ratio = 1 dan neighbors = 5, jumlah masing-masing kelas menjadi 463 observasi, dengan total 926 observasi pada data latih. Hal ini terlihat pada plot setelah balancing, ketika jumlah kedua kelas menjadi sama. Proses ini bertujuan mengurangi ketidakseimbangan kelas agar model dapat mempelajari kedua kelas dengan lebih baik. Balancing hanya diterapkan pada data latih, sedangkan data uji tetap dipertahankan agar evaluasi model lebih objektif.
Berdasarkan hasil dapat disimpulkan bahwa data curah hujan yang terdiri dari 743 observasi dan 5 variabel telah melalui proses prapemrosesan, mulai dari analisis deskriptif, penanganan missing value, pengurangan kardinalitas, pembagian data, penanganan outlier, scaling, encoding, hingga balancing menggunakan SMOTE. Penanganan missing value dilakukan dengan metode yang dipilih untuk setiap variabel, sedangkan pengurangan kardinalitas bertujuan menyederhanakan kategori cuaca. Selanjutnya, analisis outlier menunjukkan bahwa variabel Kecepatan_Angin memiliki 106 pencilan yang ditangani menggunakan metode capping. Proses scaling dilakukan untuk menyesuaikan skala variabel numerik, sementara encoding mengubah kategori cuaca menjadi 10 variabel dummy. Pada tahap balancing, SMOTE menyeimbangkan jumlah kelas pada data latih dari 131 dan 463 observasi menjadi masing-masing 463 observasi. Secara keseluruhan, rangkaian prapemrosesan ini menghasilkan data yang lebih terstruktur dan siap digunakan untuk tahap pemodelan serta evaluasi kinerja model.
Fransiska, H. (2026). Modul Praktikum Machine Learning and Modern Prediction. Laboratorium Matematika Fakultas Matematika dan Ilmu Pengetahuan Alam Universitas Bengkulu. Mumuni, A., & Mumuni, F. (2025). Automated data processing and feature engineering for deep learning and big data applications: A survey. Journal of Information and Intelligence, 3(2), 113–153. Rachmatullah, M. I. C. (2025). Ekstraksi Fitur pada Machine Learning: Tinjauan Konsep, Metode, Tren Representasi Modern, dan Arah Riset. Jurnal Improve, 17(1). Vasques, X. (2024). Feature Engineering Techniques in Machine Learning. Wiley.