BAB I PENDAHULUAN

1.1 Latar Belakang Masalah

Perkembangan teknologi menghasilkan data dalam jumlah besar yang kini dimanfaatkan machine learning untuk menemukan pola dan mendukung pengambilan keputusan. Namun, data mentah jarang berada dalam kondisi siap pakai dan harus diolah dengan baik sebelum dimodelkan. Salah satu masalah yang paling sering muncul adalah missing value, yang dapat memengaruhi hasil analisis sehingga perlu ditangani, misalnya dengan teknik imputasi (Baihaqi dkk., 2023).

Selain itu, distribusi kelas pada data sering tidak seimbang, sehingga jumlah data pada satu kelas jauh lebih sedikit daripada kelas lainnya. Kondisi ini berdampak buruk pada hasil klasifikasi karena model cenderung kurang mengenali kelas minoritas. Penerapan metode SMOTE terbukti dapat meningkatkan akurasi beberapa algoritma klasifikasi pada data yang tidak seimbang (Syukron dkk., 2023).

Data juga perlu disiapkan melalui penanganan outlier, penyamaan skala (scaling), dan pengubahan variabel kategoris menjadi numerik (encoding), yang secara keseluruhan dikenal sebagai feature engineering. Skala fitur yang tidak seimbang dapat menghambat kinerja algoritma sehingga normalisasi data menjadi langkah yang penting (Allorerung dkk., 2024). Oleh karena itu, praktikum ini dilakukan agar mahasiswa memahami konsep sekaligus mampu menerapkan teknik feature engineering menggunakan RStudio.

1.2 Rumusan Masalah

  1. Bagaimana cara memahami konsep dari berbagai jenis feature engineering pada RStudio?
  2. Bagaimana cara melakukan teknik feature engineering di program RStudio?

1.3 Tujuan Penelitian

  1. Mahasiswa memahami konsep dari berbagai jenis feature engineering pada RStudio.
  2. Mahasiswa dapat melakukan teknik feature engineering di program RStudio.

BAB II TINJAUAN PUSTAKA

2.1 Handling Missing Value

Missing values adalah permasalahan ketika terdapat data yang tidak lengkap atau hilang. Missing values umum terjadi karena beberapa alasan, seperti kerusakan alat, perhitungan yang tidak akurat, data yang tidak tercatat, dan berbagai kendala teknis lainnya. Data hilang dapat menghambat proses analisis karena menyebabkan hasil analisis menjadi kurang efisien dan menurunkan akurasi (Prasetya dkk., 2023).

Berdasarkan mekanismenya, data hilang dibedakan menjadi tiga jenis, yaitu Missing Completely at Random (MCAR), ketika data hilang sepenuhnya secara acak dan tidak berhubungan dengan atribut lain; Missing at Random (MAR), ketika kehilangan data berhubungan dengan atribut lain yang diamati; serta Missing Not at Random (MNAR), ketika kehilangan data bergantung pada nilai data itu sendiri. Oleh karena itu, pemilihan metode penanganan perlu mempertimbangkan mekanisme data hilang agar hasil analisis tetap dapat diandalkan (Widyawati, 2025). Penanganan missing values dapat dilakukan menggunakan metode konvensional, seperti imputasi rata-rata dan nilai maksimum, maupun metode modern berbasis data mining, seperti K-Nearest Neighbors (KNN) dan Neural Network. Metode konvensional relatif sederhana, tetapi berisiko menimbulkan masalah lain dalam analisis (Prasetya dkk., 2023).

2.2 Kardinalitas

Kardinalitas merupakan jumlah kategori atau nilai unik yang terdapat dalam suatu variabel kategorik. Tingkat kardinalitas perlu diperhatikan dalam proses feature engineering, terutama ketika menerapkan teknik One-Hot Encoding. Penggunaan One-Hot Encoding pada variabel dengan kardinalitas tinggi dapat menghasilkan vektor fitur berdimensi tinggi sehingga meningkatkan jumlah kolom dalam dataset. Oleh karena itu, pengelolaan variabel dengan kardinalitas tinggi diperlukan agar proses pengolahan data lebih efisien sebelum pemodelan (Faqih & Sabita, 2025).

2.3 Splitting Data

Data splitting merupakan proses pembagian dataset menjadi data pelatihan (training data) dan data pengujian (testing data) untuk mengevaluasi kemampuan model dalam memprediksi data yang belum pernah dipelajari. Proporsi pembagian data umumnya berkisar antara 70–80% untuk data pelatihan dan 20–30% untuk data pengujian. Rasio 80:20 dapat digunakan untuk menyediakan data yang cukup dalam proses pelatihan sekaligus evaluasi model, tetapi pemilihannya tetap perlu disesuaikan dengan karakteristik dan ukuran dataset yang digunakan (Alviona dkk, 2023).

2.4 Handling Outlier

Handling Outlier Outlier, atau dikenal sebagai pencilan, merupakan observasi atau titik data yang menunjukkan deviasi ekstrem dan berbeda secara signifikan dari sebagian besar data lain dalam suatu set data. Keberadaan outlier dapat diatribusikan ke berbagai sumber, mulai dari kesalahan pengukuran, kesalahan entri data (misinput), kontaminasi data, hingga representasi sah dari kejadian langka atau variabilitas inheren dalam populasi. Dalam analisis statistik dan pemodelan machine learning, outlier menuntut perhatian khusus karena memiliki potensi untuk memberikan pengaruh yang tidak proporsional (disproportionate influence) terhadap hasil. (Fransiska, 2026).

2.5 Scaling Data

Data scaling merupakan tahap prapemrosesan yang bertujuan menyamakan skala nilai antarfitur. Proses ini dilakukan agar perbedaan rentang nilai antarfitur tidak memengaruhi proses pembelajaran algoritma secara berlebihan. Metode yang umum digunakan meliputi Min-Max Normalization dan standardisasi Z-Score (Ambarwari dkk., 2020).

2.6 Encoding

Feature encoding adalah proses mengubah fitur kategoris atau non numerik menjadi format numerik yang dapat digunakan sebagai input untuk algoritma machine learning. Banyak algoritma machine learning membutuhkan input numerik. Ada beberapa teknik umum untuk feature encoding, seperti One Hot Encoding dan Ordinal Encoding (Fransiska, 2026).

2.7 Imbalanced Dataset

Imbalanced Dataset adalah sebuah dataset di mana distribusi kelas atau kategorinya tidak setara. Kelas-kelas yang mencakup proporsi besar dari set data disebut kelas mayoritas, sedangkan kelas-kelas yang mencakup proporsi lebih kecil disebut kelas minoritas. Dalam praktiknya, akan lebih sering menjumpai data yang tidak seimbang daripada yang seimbang. Hal ini tidak serta-merta menjadi masalah jika variabel target hanya memiliki sedikit ketidakseimbangan. Sayangnya, kenyataannya tidak selalu demikian dan variabel target Anda mungkin sangat tidak seimbang, sebagai contoh, dengan rasio 10:1 (Fransiska, 2026).

BAB III METODE PENELITIAN

3.1 Jenis dan Sumber Data

Penelitian ini merupakan penelitian terapan dengan pendekatan kuantitatif. Jenis data yang digunakan adalah data kuantitatif, berupa data hasil pengamatan kondisi cuaca yang terdiri atas variabel suhu, kelembapan, kecepatan angin, keadaan cuaca, dan hujan. Data yang digunakan merupakan data sekunder yang diperoleh dari Asisten Praktikum. Selanjutnya, data dianalisis secara statistik menggunakan program R.

3.2 Variabel Penelitian

Variabel yang digunakan dalam penelitian ini terdiri dari variabel target dan variabel prediktor. Variabel Hujan merupakan variabel target dengan dua kategori, yaitu kode 1 dan 2. Variabel prediktornya adalah Suhu, Kelembapan, Keadaan_Cuaca, dan Kecepatan_Angin. Suhu menunjukkan suhu udara, Kelembapan menunjukkan tingkat kelembapan udara, Keadaan_Cuaca menunjukkan kondisi cuaca berdasarkan kode yang kemudian dikelompokkan menjadi Keadaan_Cuaca_reduced, sedangkan Kecepatan_Angin menunjukkan kecepatan angin hasil pengamatan.

3.3 Langkah-langkah Analisis

Berikut merupakan algoritma pengolahan data: 1. Mulai. 2. Impor dan eksplorasi data. 3. Tangani missing value. 4. Reduksi kategori Keadaan_Cuaca. 5. Bagi data training 80% dan testing 20%. 6. Tangani outlier dengan IQR. 7. Lakukan scaling dan one-hot encoding. 8. Terapkan SMOTE pada data training. 9. Selesai.

BAB IV HASIL DAN PEMBAHASAN

library(readxl) 
library(dplyr) 
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(tidyr) 
library(zoo) 
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
library(rsample)
library(DescTools) 
library(recipes)
## 
## Attaching package: 'recipes'
## The following object is masked from 'package:stats':
## 
##     step
library(themis)
library(e1071)
## 
## Attaching package: 'e1071'
## The following object is masked from 'package:rsample':
## 
##     permutations
library(ggplot2)
## 
## Attaching package: 'ggplot2'
## The following object is masked from 'package:e1071':
## 
##     element
library(gridExtra)
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
# Dataset
data <- read_excel("data curah hujan.xlsx")
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
str(data)
## 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 ...

Interpretasi: Dataset berisi 743 observasi dengan 5 variabel. Seluruh variabel terbaca dengan tipe data numerik. Variabel hujan diubah menjadi faktor karena berperan sebagai target klasifikasi.

4.1 Statsitik Deskriptif

summary(data)
##      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
table(data$Hujan)
## 
##   1   2 
## 164 579
round(prop.table(table(data$Hujan)) * 100, 2)
## 
##     1     2 
## 22.07 77.93

Interpretasi: Statistik deskriptif menunjukkan bahwa Keadaan_Cuaca memiliki sebaran menceng ke kanan dan berupa kode kategori. Missing value terdapat pada Keadaan_Cuaca dan Kecepatan_Angin, sedangkan kelas Hujan tidak seimbang, yaitu kelas 2 sebesar 77,93% dan kelas 1 sebesar 22,07%, sehingga diperlukan penanganan missing value dan SMOTE.

4.2 Handling Missing Value

#Cek Missing Value
colSums(is.na(data)) 
##           Hujan            Suhu      Kelembapan   Keadaan_Cuaca Kecepatan_Angin 
##               0               0               0               9             314
round(colMeans(is.na(data)) * 100, 2)
##           Hujan            Suhu      Kelembapan   Keadaan_Cuaca Kecepatan_Angin 
##            0.00            0.00            0.00            1.21           42.26
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

Interpretasi: Missing value terdapat pada Keadaan_Cuaca sebanyak 9 data (1,21%) dan Kecepatan_Angin sebanyak 314 data (42,26%), sedangkan variabel lainnya lengkap.

# Penanganan Missing Value
data_awal <- data

# 1. Mean Imputation Kecepatan_Angin
mean_angin <- mean(data_awal$Kecepatan_Angin, na.rm=TRUE)
jumlah_na <- sum(is.na(data_awal$Kecepatan_Angin))

data$Kecepatan_Angin[is.na(data$Kecepatan_Angin)] <- mean_angin

cat("Mean Imputasi:", round(mean_angin, 2), "\n")
## Mean Imputasi: 6.66
cat("Jumlah Data Diimputasi:", jumlah_na, "\n")
## Jumlah Data Diimputasi: 314
cat("NA Setelah Imputasi:", sum(is.na(data$Kecepatan_Angin)), "\n")
## NA Setelah Imputasi: 0
# 2. Interpolasi Keadaan_Cuaca
na_awal <- sum(is.na(data_awal$Keadaan_Cuaca))
data$Keadaan_Cuaca <- na.approx(data$Keadaan_Cuaca, na.rm=FALSE)

cat("NA Keadaan_Cuaca Sebelum:", na_awal, "\n")
## NA Keadaan_Cuaca Sebelum: 9
cat("NA Keadaan_Cuaca Sesudah:",
    sum(is.na(data$Keadaan_Cuaca)), "\n")
## NA Keadaan_Cuaca Sesudah: 0
# 3. Dampak Imputasi
hasil <- data.frame(
  Kondisi = c("Sebelum", "Sesudah"),
  Mean = c(mean(data_awal$Kecepatan_Angin, na.rm=TRUE),
           mean(data$Kecepatan_Angin, na.rm=TRUE)),
  SD = c(sd(data_awal$Kecepatan_Angin, na.rm=TRUE),
         sd(data$Kecepatan_Angin, na.rm=TRUE))
)

print(transform(hasil, Mean=round(Mean, 4), SD=round(SD, 4)))
##   Kondisi  Mean     SD
## 1 Sebelum 6.655 3.3527
## 2 Sesudah 6.655 2.5463
# Data hasil penanganan missing value yang dipakai selanjutnya
df_clean <- data

Interpretasi: 1. Interpolasi mengisi 9 nilai hilang pada variabel Keadaan_Cuaca hingga tidak ada NA. 2. Mean imputation mengisi 314 nilai hilang pada Kecepatan_Angin menggunakan rata-rata 6,655. 3. Mean tetap, sedangkan simpangan baku turun dari 3,353 menjadi 2,546 karena imputasi mengurangi variasi data.

4.3 Kardinalitas

set.seed(200)
head(df_clean, 10)
## # A tibble: 10 × 5
##    Hujan  Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin
##    <dbl> <dbl>      <dbl>         <dbl>           <dbl>
##  1     1  23           95             5            6.66
##  2     1  24           90             1            6.66
##  3     1  26.8         77             1            6.66
##  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
sort(unique(df_clean$Keadaan_Cuaca))
##  [1]  1.0  1.5  2.0  3.0  5.0 10.0 13.0 14.0 15.0 16.0 17.0 21.0 29.0 49.0 56.0
## [16] 60.0 61.0 62.0 63.0 65.0 91.0 95.0 97.0
bins <- c(0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
labels <- 0:9

df_clean$Keadaan_Cuaca_reduced <- cut(
  df_clean$Keadaan_Cuaca,
  breaks = bins,
  labels = labels,
  right = TRUE,
  include.lowest = TRUE
)

cat("--- Hasil Perbandingan ---\n")
## --- Hasil Perbandingan ---
print(head(df_clean[, c("Keadaan_Cuaca", "Keadaan_Cuaca_reduced")], 10))
## # A tibble: 10 × 2
##    Keadaan_Cuaca Keadaan_Cuaca_reduced
##            <dbl> <fct>                
##  1             5 0                    
##  2             1 0                    
##  3             1 0                    
##  4             2 0                    
##  5             1 0                    
##  6             1 0                    
##  7             3 0                    
##  8             2 0                    
##  9             2 0                    
## 10             2 0
cat("\nJumlah kategori asli    :", length(unique(na.omit(df_clean$Keadaan_Cuaca))))
## 
## Jumlah kategori asli    : 23
cat("\nJumlah kategori reduced :", length(unique(na.omit(df_clean$Keadaan_Cuaca_reduced))))
## 
## Jumlah kategori reduced : 7
cat("\n\nFrekuensi Kategori Reduced:\n")
## 
## 
## Frekuensi Kategori Reduced:
print(table(df_clean$Keadaan_Cuaca_reduced, useNA = "ifany"))
## 
##   0   1   2   3   4   5   6   7   8   9 
## 523  48  46   0   1  28  71   0   0  26

Interpretasi: Variabel Keadaan_Cuaca memiliki 20 nilai unik yang dikelompokkan ke dalam interval lebar 10 dan diberi label 0–9. Hasil reduksi menunjukkan 6 kategori terisi, dengan mayoritas observasi berada pada kategori 0 sebanyak 519 data. Beberapa kategori tidak memiliki observasi, sedangkan 9 data bernilai NA karena berada di luar rentang interval yang ditentukan atau memiliki nilai hilan

4.4 Splitting Data

# 1. Masukkan kolom hasil reduksi ke data
data$Keadaan_Cuaca_reduced <- df_clean$Keadaan_Cuaca_reduced

# 2. Shuffle Split 80:20
set.seed(200)
split_shuffle <- initial_split(data, prop = 0.8)
train <- training(split_shuffle)
test  <- testing(split_shuffle)

y_train <- train$Hujan
y_test  <- test$Hujan
X_train <- train %>% select(-Hujan)
X_test  <- test %>% select(-Hujan)

# 3. Samakan Level Kategori
all_levels <- levels(df_clean$Keadaan_Cuaca_reduced)
X_train$Keadaan_Cuaca_reduced <- factor(X_train$Keadaan_Cuaca_reduced, levels = all_levels)
X_test$Keadaan_Cuaca_reduced  <- factor(X_test$Keadaan_Cuaca_reduced, levels = all_levels)

# 4. Ukuran Data dan Distribusi Kelas
cat("Training:", nrow(X_train), "observasi |", ncol(X_train), "variabel\n")
## Training: 594 observasi | 5 variabel
cat("Testing :", nrow(X_test), "observasi |", ncol(X_test), "variabel\n")
## Testing : 149 observasi | 5 variabel
cat("\nDistribusi Kelas Training (%):\n")
## 
## Distribusi Kelas Training (%):
print(round(prop.table(table(y_train)) * 100, 2))
## y_train
##     1     2 
## 21.55 78.45
cat("\nDistribusi Kelas Testing (%):\n")
## 
## Distribusi Kelas Testing (%):
print(round(prop.table(table(y_test)) * 100, 2))
## y_test
##     1     2 
## 24.16 75.84

Interpretasi: Data dibagi secara acak (shuffle splitting, set.seed(200)) menjadi 594 observasi data latih dengan proporsi pembagian data latih (80%) dan 149 data uji (20%). Kedua subset didominasi kelas 2, sehingga ketidakseimbangan kelas ditangani menggunakan SMOTE. Tahap berikutnya menggunakan parameter dari data latih untuk menghindari data leakage.

4.5 Handling Outlier

# 1. Deteksi Outlier dengan IQR
list_num <- c("Suhu", "Kelembapan", "Kecepatan_Angin")

batas <- lapply(X_train[list_num], function(x) {
  q1 <- quantile(x, 0.25, na.rm = TRUE)
  q3 <- quantile(x, 0.75, na.rm = TRUE)
  iqr <- q3 - q1
  c(Bawah = q1 - 1.5 * iqr, Atas = q3 + 1.5 * iqr)
})

outliers <- data.frame(
  Variabel = list_num,
  Batas_Bawah = sapply(batas, `[`, 1),
  Batas_Atas = sapply(batas, `[`, 2),
  Jumlah_Outlier = sapply(seq_along(list_num), function(i) {
    x <- X_train[[list_num[i]]]
    sum(x < batas[[i]][1] | x > batas[[i]][2], na.rm = TRUE)
  })
)
print(outliers)
##                                  Variabel Batas_Bawah Batas_Atas Jumlah_Outlier
## Suhu.Bawah.25%                       Suhu        17.4       35.8              0
## Kelembapan.Bawah.25%           Kelembapan        46.5      122.5              0
## Kecepatan_Angin.Bawah.25% Kecepatan_Angin         2.0       10.0             50
# 2. Skewness Sebelum Capping
skew_sebelum <- sapply(X_train[list_num], skewness, na.rm = TRUE)
cat("\nSkewness Sebelum Capping:\n")
## 
## Skewness Sebelum Capping:
print(round(skew_sebelum, 3))
##            Suhu      Kelembapan Kecepatan_Angin 
##           0.279          -0.558           0.837
# 3. Capping pada Data Latih dan Uji
X_train_capped <- X_train
X_test_capped  <- X_test

for (i in seq_along(list_num)) {
  kol <- list_num[i]
  X_train_capped[[kol]] <- Winsorize(X_train[[kol]], val = batas[[i]])
  X_test_capped[[kol]]  <- Winsorize(X_test[[kol]], val = batas[[i]])
}

cat("\nRingkasan Sebelum Capping:\n")
## 
## Ringkasan Sebelum Capping:
print(summary(X_train[list_num]))
##       Suhu         Kelembapan  Kecepatan_Angin 
##  Min.   :22.60   Min.   : 52   Min.   : 2.000  
##  1st Qu.:24.30   1st Qu.: 75   1st Qu.: 5.000  
##  Median :26.00   Median : 86   Median : 6.655  
##  Mean   :26.54   Mean   : 84   Mean   : 6.642  
##  3rd Qu.:28.90   3rd Qu.: 94   3rd Qu.: 7.000  
##  Max.   :31.60   Max.   :100   Max.   :21.000
cat("\nRingkasan Sesudah Capping:\n")
## 
## Ringkasan Sesudah Capping:
print(summary(X_train_capped[list_num]))
##       Suhu         Kelembapan  Kecepatan_Angin 
##  Min.   :22.60   Min.   : 52   Min.   : 2.000  
##  1st Qu.:24.30   1st Qu.: 75   1st Qu.: 5.000  
##  Median :26.00   Median : 86   Median : 6.655  
##  Mean   :26.54   Mean   : 84   Mean   : 6.447  
##  3rd Qu.:28.90   3rd Qu.: 94   3rd Qu.: 7.000  
##  Max.   :31.60   Max.   :100   Max.   :10.000
# 4. Skewness Sesudah Capping
skew_sesudah <- sapply(X_train_capped[list_num], skewness, na.rm = TRUE)
cat("\nPerbandingan Skewness:\n")
## 
## Perbandingan Skewness:
print(round(data.frame(Sebelum = skew_sebelum, Sesudah = skew_sesudah), 3))
##                 Sebelum Sesudah
## Suhu              0.279   0.279
## Kelembapan       -0.558  -0.558
## Kecepatan_Angin   0.837  -0.163
# 5. Visualisasi Sebelum dan Sesudah Capping
for (kol in list_num) {
  p1 <- ggplot(X_train, aes(x = .data[[kol]])) +
    geom_histogram(bins = 30, fill = "#008080", color = "black") +
    labs(title = paste(kol, "- Sebelum Capping"), x = kol, y = "Frekuensi") +
    theme_minimal()

  p2 <- ggplot(X_train_capped, aes(x = .data[[kol]])) +
    geom_histogram(bins = 30, fill = "#008080", color = "black") +
    labs(title = paste(kol, "- Sesudah Capping"), x = kol, y = "Frekuensi") +
    theme_minimal()

  print(grid.arrange(p1, p2, ncol = 2))
}

## TableGrob (1 x 2) "arrange": 2 grobs
##   z     cells    name           grob
## 1 1 (1-1,1-1) arrange gtable[layout]
## 2 2 (1-1,2-2) arrange gtable[layout]

## TableGrob (1 x 2) "arrange": 2 grobs
##   z     cells    name           grob
## 1 1 (1-1,1-1) arrange gtable[layout]
## 2 2 (1-1,2-2) arrange gtable[layout]

## TableGrob (1 x 2) "arrange": 2 grobs
##   z     cells    name           grob
## 1 1 (1-1,1-1) arrange gtable[layout]
## 2 2 (1-1,2-2) arrange gtable[layout]

Interpretas 1. Berdasarkan aturan IQR, outlier ditemukan pada Kecepatan_Angin akibat IQR yang menyempit setelah imputasi rata-rata pada sekitar 42% data.

  1. Setelah capping, nilai maksimum Kecepatan_Angin turun dari 21 menjadi 10 dan seluruh outlier berhasil ditangani tanpa menghapus observasi.

  2. Skewness Kecepatan_Angin berubah dari 0,837 menjadi −0,163, sedangkan Suhu dan Kelembapan tetap pada 0,279 dan −0,558.

  3. Boxplot menunjukkan outlier Kecepatan_Angin tidak lagi muncul setelah capping, sementara histogram Suhu dan Kelembapan tidak berubah.

4.6 Scalling Data (Robust Scaler)

# Parameter Robust Scaler dihitung dari data latih
median_val <- sapply(X_train_capped[list_num], median, na.rm = TRUE)
iqr_val    <- sapply(X_train_capped[list_num], IQR, na.rm = TRUE)

rbind(Median = median_val, IQR = iqr_val)
##        Suhu Kelembapan Kecepatan_Angin
## Median 26.0         86        6.655012
## IQR     4.6         19        2.000000
X_train_scale <- X_train_capped
X_test_scale  <- X_test_capped

for (col in list_num) {
  X_train_scale[[col]] <- (X_train_capped[[col]] - unname(median_val[col])) / unname(iqr_val[col])
  X_test_scale[[col]]  <- (X_test_capped[[col]]  - unname(median_val[col])) / unname(iqr_val[col])
}

head(X_train_scale)
## # A tibble: 6 × 5
##      Suhu Kelembapan Keadaan_Cuaca Kecepatan_Angin Keadaan_Cuaca_reduced
##     <dbl>      <dbl>         <dbl>           <dbl> <fct>                
## 1 -0.500       0.579            61           0     6                    
## 2  1.09       -0.947             2           1.67  0                    
## 3  0.0870     -0.263             3          -0.828 0                    
## 4  1.15       -1.53              2           1.17  0                    
## 5  0.674      -0.421             2           0     0                    
## 6  0.391      -0.368             2          -1.83  0
cat("\nMedian & IQR data latih setelah scaling:\n")
## 
## Median & IQR data latih setelah scaling:
rbind(Median = sapply(X_train_scale[list_num], median),
      IQR    = sapply(X_train_scale[list_num], IQR))
##        Suhu Kelembapan Kecepatan_Angin
## Median    0          0               0
## IQR       1          1               1
summary(X_train_scale[list_num])
##       Suhu           Kelembapan      Kecepatan_Angin  
##  Min.   :-0.7391   Min.   :-1.7895   Min.   :-2.3275  
##  1st Qu.:-0.3696   1st Qu.:-0.5789   1st Qu.:-0.8275  
##  Median : 0.0000   Median : 0.0000   Median : 0.0000  
##  Mean   : 0.1177   Mean   :-0.1053   Mean   :-0.1041  
##  3rd Qu.: 0.6304   3rd Qu.: 0.4211   3rd Qu.: 0.1725  
##  Max.   : 1.2174   Max.   : 0.7368   Max.   : 1.6725
summary(X_test_scale[list_num])
##       Suhu            Kelembapan      Kecepatan_Angin   
##  Min.   :-0.69565   Min.   :-1.6316   Min.   :-2.32751  
##  1st Qu.:-0.34783   1st Qu.:-0.6842   1st Qu.:-0.32751  
##  Median :-0.04348   Median : 0.0000   Median : 0.00000  
##  Mean   : 0.11716   Mean   :-0.1363   Mean   :-0.05804  
##  3rd Qu.: 0.65217   3rd Qu.: 0.4211   3rd Qu.: 0.67249  
##  Max.   : 1.30435   Max.   : 0.7368   Max.   : 1.67249
X_train_scale %>%
  dplyr::select(all_of(list_num)) %>%
  pivot_longer(everything(), names_to = "Variabel", values_to = "Nilai") %>%
  ggplot(aes(x = Variabel, y = Nilai, fill = Variabel)) +
  geom_boxplot() +
  labs(title = "Boxplot Data Latih Setelah Robust Scaler") +
  theme_minimal() +
  theme(legend.position = "none")

Interpretasi: 1. Robust Scaler menggunakan median dan IQR data latih sebagai parameter transformasi, dengan median awal Suhu, Kelembapan, dan Kecepatan_Angin masing-masing 26, 86, dan 6,655 serta IQR 4,6, 19, dan 2.

  1. Setelah scaling, median ketiga variabel menjadi 0 dan IQR menjadi 1; nilai negatif menunjukkan data di bawah median, sedangkan nilai positif menunjukkan data di atas median.

  2. Parameter data latih diterapkan pada data uji untuk mencegah data leakage, sementara penggunaan median dan IQR membuat metode ini relatif tahan terhadap outlier.

4.7 Encoding

## Encoding - One Hot Encoder
list_cat <- c("Keadaan_Cuaca_reduced")

resep_encode <- recipe(~ Keadaan_Cuaca_reduced, data = X_train_scale) %>%
  step_dummy(all_of(list_cat), one_hot = TRUE) %>%
  prep(training = X_train_scale)

X_train_encoded <- bake(resep_encode, new_data = NULL)
X_test_encoded  <- bake(resep_encode, new_data = X_test_scale)

X_train_encoded
## # A tibble: 594 × 10
##    Keadaan_Cuaca_reduced_X0 Keadaan_Cuaca_reduced_X1 Keadaan_Cuaca_reduced_X2
##                       <dbl>                    <dbl>                    <dbl>
##  1                        0                        0                        0
##  2                        1                        0                        0
##  3                        1                        0                        0
##  4                        1                        0                        0
##  5                        1                        0                        0
##  6                        1                        0                        0
##  7                        0                        0                        0
##  8                        0                        0                        0
##  9                        1                        0                        0
## 10                        1                        0                        0
## # ℹ 584 more rows
## # ℹ 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>
X_test_encoded
## # A tibble: 149 × 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                        1                        0                        0
##  8                        1                        0                        0
##  9                        1                        0                        0
## 10                        1                        0                        0
## # ℹ 139 more rows
## # ℹ 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>
cat("Dimensi X_train_encoded :", dim(X_train_encoded), "\n")
## Dimensi X_train_encoded : 594 10
cat("Dimensi X_test_encoded  :", dim(X_test_encoded), "\n")
## Dimensi X_test_encoded  : 149 10
colnames(X_train_encoded)
##  [1] "Keadaan_Cuaca_reduced_X0" "Keadaan_Cuaca_reduced_X1"
##  [3] "Keadaan_Cuaca_reduced_X2" "Keadaan_Cuaca_reduced_X3"
##  [5] "Keadaan_Cuaca_reduced_X4" "Keadaan_Cuaca_reduced_X5"
##  [7] "Keadaan_Cuaca_reduced_X6" "Keadaan_Cuaca_reduced_X7"
##  [9] "Keadaan_Cuaca_reduced_X8" "Keadaan_Cuaca_reduced_X9"

Interpretasi: One-hot encoding mengubah variabel Keadaan_Cuaca_reduced menjadi 10 kolom biner yang mewakili kategori 0–9, dengan nilai 1 untuk kategori yang sesuai dan 0 untuk kategori lainnya. Data latih terdiri atas 594 baris dan data uji 149 baris dengan struktur kolom yang sama. Kategori yang tidak memiliki observasi, seperti 3, 7, dan 8, menghasilkan kolom bernilai 0 seluruhnya, tetapi tetap dipertahankan agar struktur data latih dan data uji konsisten.

##4.8 Handling Imbalance Data (SMOTE)

# 1. Gabungkan Fitur dan Target
X_train_final <- bind_cols(X_train_scale[list_num], X_train_encoded) %>%
  mutate(Hujan = factor(y_train))

X_test_final <- bind_cols(X_test_scale[list_num], X_test_encoded) %>%
  mutate(Hujan = factor(y_test, levels = levels(X_train_final$Hujan)))

# 2. Distribusi Kelas Sebelum SMOTE
cat("Sebelum SMOTE:\n")
## Sebelum SMOTE:
print(table(X_train_final$Hujan))
## 
##   1   2 
## 128 466
print(round(prop.table(table(X_train_final$Hujan)) * 100, 2))
## 
##     1     2 
## 21.55 78.45
# 3. SMOTE pada Data Latih
set.seed(200)
resep_smote <- recipe(Hujan ~ ., data = X_train_final) %>%
  step_smote(Hujan, over_ratio = 1, neighbors = 5)

prep_smote <- prep(resep_smote, training = X_train_final)
train_smote <- bake(prep_smote, new_data = NULL)

cat("\nSesudah SMOTE:\n")
## 
## Sesudah SMOTE:
print(table(train_smote$Hujan))
## 
##   1   2 
## 466 466
print(round(prop.table(table(train_smote$Hujan)) * 100, 2))
## 
##  1  2 
## 50 50
cat("\nDimensi Data Latih:\n")
## 
## Dimensi Data Latih:
cat("Sebelum:", dim(X_train_final), "| Sesudah:", dim(train_smote), "\n")
## Sebelum: 594 14 | Sesudah: 932 14
cat("\nDistribusi Kelas Data Uji:\n")
## 
## Distribusi Kelas Data Uji:
print(table(X_test_final$Hujan))
## 
##   1   2 
##  36 113
# 4. Visualisasi Distribusi Kelas
dist_kelas <- bind_rows(
  data.frame(Tahap = "Sebelum SMOTE", Hujan = X_train_final$Hujan),
  data.frame(Tahap = "Sesudah SMOTE", Hujan = train_smote$Hujan)
)

ggplot(dist_kelas, aes(x = Hujan, fill = Hujan)) +
  geom_bar() +
  geom_text(stat = "count", aes(label = after_stat(count)), vjust = -0.3) +
  facet_wrap(~Tahap) +
  labs(title = "Distribusi Kelas Hujan Sebelum dan Sesudah SMOTE",
       x = "Kelas Hujan", y = "Frekuensi") +
  theme_minimal() +
  theme(legend.position = "none")

Interpretasi: Sebelum SMOTE, data latih didominasi kelas 2 sebanyak 466 data atau 78,45%, sedangkan kelas 1 berjumlah 128 data atau 21,55%. Setelah SMOTE, kedua kelas menjadi seimbang dengan masing-masing 466 data, sehingga jumlah data latih meningkat dari 594 menjadi 932. SMOTE hanya diterapkan pada data latih, sedangkan 149 data uji tetap dipertahankan tanpa perubahan.

BAB V KESIMPULAN

5.1 Kesimpulan

Feature engineering merupakan tahapan pengolahan data yang mencakup berbagai teknik untuk meningkatkan kualitas data sebelum pemodelan. Teknik yang digunakan meliputi penanganan missing value, reduksi kardinalitas, deteksi dan penanganan pencilan, encoding, penskalaan, serta penyeimbangan kelas. Setiap teknik memiliki fungsi berbeda sesuai dengan karakteristik dan permasalahan yang ditemukan pada data.

Penerapan teknik feature engineering dilakukan menggunakan RStudio melalui beberapa tahapan pengolahan data. Penanganan missing value dilakukan dengan imputasi rata-rata dan interpolasi, sedangkan pencilan ditangani menggunakan metode capping. Selanjutnya, reduksi kardinalitas, one-hot encoding, Robust Scaling, dan SMOTE diterapkan untuk mempersiapkan data sebelum proses pemodelan.

Berdasarkan hasil pengolahan dataset curah hujan yang terdiri atas 743 observasi, data dibagi menjadi 594 observasi untuk data latih dan 149 observasi untuk data uji. Proses transformasi mencakup penanganan nilai hilang, pengendalian pencilan pada variabel kecepatan angin, penskalaan variabel numerik, serta encoding variabel kategorik. Penerapan SMOTE pada data latih juga berhasil menyeimbangkan jumlah kelas dari 466 data kelas 2 dan 128 data kelas 1 menjadi masing-masing 466 observasi, sehingga data lebih siap digunakan untuk tahap pemodelan.

DAFTAR PUSTAKA

Ambarwari, A., Adrian, Q. J., & Herdiyeni, Y. (2020). Analisis pengaruh data scaling terhadap performa algoritme machine learning untuk identifikasi tanaman. Jurnal RESTI (Rekayasa Sistem dan Teknologi Informasi), 4(1), 117–122.

Alviola, N. A., Fathurrahman, Z., Rifai, R. N., & Afrah, A. S. (2023). Sistem diagnosa penyakit liver menggunakan metode artificial neural network: Studi berdasarkan dataset Indian Liver Patient Dataset. Jurnal Informatika: Jurnal Pengembangan IT, 8(3), 308–314.

Baihaqi, M. R., Padilah, T. N., & Jajuli, M. (2023). Implementasi metode imputasi mean dan single center imputation chained. Jurnal JTIK (Jurnal Teknologi Informasi dan Komunikasi), 7(4).

Faiq, H. A., & Sabita, H. (2025). Pengembangan model deep learning untuk pengenalan wajah pada sistem keamanan. Teknika, 18(1), 197–209.

Fransiska, H. (2026). Machine learning and modern prediction. Laboratorium Matematika dan Ilmu Pengetahuan Alam, Universitas Bengkulu.

Prasetya, M. R. A., Priyatno, A. M., & Nurhaeni. (2023). Penanganan imputasi missing values pada data time series dengan menggunakan metode data mining. Jurnal Informasi dan Teknologi, 5(2), 56–62.

Syukron, A., Sardiarinto, S., Saputro, E., & Widodo, P. (2023). Penerapan metode SMOTE untuk mengatasi ketidakseimbangan kelas pada prediksi gagal jantung. Jurnal Teknologi Informasi dan Terapan, 10(1), 47–50.

Widyawati, A. S., Fitrianto, A., & Silvianti, P. (2025). Performance of multivariate missing data imputation methods on climate data. Journal of Applied Informatics and Computing (JAIC), 9(6), 3953–3963.