1 Transformasi

Banyak metode statistika yang mensyaratkan bahwa peubah numerik yang digunakan harus mengikuti sebaran normal. Contohnya: uji-T pada Uji sigifikansi parameter model Regresi atau uji-F pada ANOVA

Asumsi bahwa data berdistribusi normal dengan ragam yang homogen harus selalu diperiksa, Jika asumsi terpenuhi maka kita dapat menganalisis data tsb dengan analisis statistika parametrik. Namun jika tidak, maka perlu dilakukan penanganan terhadap data tersebut.

Transformasi data dapat “memperbaiki” penyimpangan dari normalitas dan keragaman yang tidak homogen (heteroskedastisitas). Transformasi yang tepat memungkinkan kita menggunakan analisis statistika parametrik.

1.1 Normalitas

1.1.1 Sebaran Simetris

Simulasi Sebaran Simetris (Normal)

set.seed(123)

# Membangkitkan 1000 bilangan acak yang menyebar Normal dengan mean=10, sd = 1.5
data.norm <- rnorm(1000, mean=10, sd=1.5)
hist(data.norm, breaks=25)

# melihat 20 data pertama yang dibangkitkan
head(data.norm, 20)
##  [1]  9.159287  9.654734 12.338062 10.105763 10.193932 12.572597 10.691374
##  [8]  8.102408  8.969721  9.331507 11.836123 10.539721 10.601157 10.166024
## [15]  9.166238 12.680370 10.746776  7.050074 11.052034  9.290813
# summary statistik
# perhatikan pada data menyebar normal, nilai mean dan median cenderung mirip
summary(data.norm)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   5.785   9.058  10.014  10.024  10.997  14.862

1.1.1.1 Normal QQ Plot

Dapat dilihat bahwa sebaran data simulasi yang kita bangkitkan berdasarkan QQPlot sudah sesuai karena secara umum setiap amatan berada pada garis Normal

qqnorm(data.norm)
qqline(data.norm,col=2)

1.1.1.2 Uji Normalitas

Uji Normalitas Nilai p-value > 0.05, Terima Ho (Data menyebar Normal)

shapiro.test(data.norm)
## 
##  Shapiro-Wilk normality test
## 
## data:  data.norm
## W = 0.99838, p-value = 0.4765

1.1.2 Sebaran Menjulur

Simulasi Sebaran Menjulur (Contoh : Chi-Square)

set.seed(123)
# membangkitan 1000 data acak yang menyebar chi^2 dengan DF=5 
data.chi<-rchisq(1000, df=5)
hist(data.chi, breaks=20)

# summary statistik
# note : pada data menjulur ke kanan, nilai mean cederung lebih besar dari median
summary(data.chi)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.1821  2.6257  4.3015  4.7939  6.3206 19.3367

Berdasarkan Normal QQ Plot, terlihat sebaran data secara umum tidak berada pada kisaran garis Normal. Begitu juga melalui uji Shapiro-Wilk, nilai p-value < 0.05 sehingga tolak Ho (Data tidak menyebar Normal)

qqnorm(data.chi)
qqline(data.chi, col="red")

shapiro.test(data.chi)
## 
##  Shapiro-Wilk normality test
## 
## data:  data.chi
## W = 0.92826, p-value < 2.2e-16

Pada data yang menjulur ke-kanan, kita dapat mencoba transformasi data menggunakan fungsi akar misalnya (X)^1/3 Proses ini mungkin memerlukan trial & error untuk mendapatkan hasil yang sesuai.

1.1.2.1 Transformasi X

# transformasi x = x^1/2
# memberikan hasil yang juga mendekati sebaran Normal

chi.transs<-data.chi^(1/2)

hist(chi.transs, breaks=20)

qqnorm(chi.transs)
qqline(chi.transs, col="red")

shapiro.test(chi.transs)
## 
##  Shapiro-Wilk normality test
## 
## data:  chi.transs
## W = 0.99434, p-value = 0.0008126

Melalui transformasi (X)^1/3 , data yang sebelumnya sangat menjulur menjadi lebih simetris. Berdasarkan hasil Normal QQ Plot maupun uji Shapiro-Wick, data hasil transformasi mengikuti sebaran Normal

# transformasi x  x1/3

chi.trans<-data.chi^(1/3)

hist(chi.trans, breaks=20)

qqnorm(chi.trans)
qqline(chi.trans, col="red")

shapiro.test(chi.trans)
## 
##  Shapiro-Wilk normality test
## 
## data:  chi.trans
## W = 0.99908, p-value = 0.9118
# transformasi x  x1/4
# memberikan hasil yang juga mendekati sebaran Normal

chi.trans2<-data.chi^(1/4)

hist(chi.trans2, breaks=20)

qqnorm(chi.trans2)
qqline(chi.trans2, col="red")

shapiro.test(chi.trans2)
## 
##  Shapiro-Wilk normality test
## 
## data:  chi.trans2
## W = 0.9972, p-value = 0.07918
# Memuat pustaka yang diperlukan
library(ggplot2)
library(gridExtra)

# TRANSFORMASI LINEAR TIDAK AKAN MERUBAH BENTUK SEBARAN DATA

chi.trans3 <- (3/4*data.chi+100)

plot1 <- ggplot(data.frame(x=data.chi), aes(x = x)) + 
         geom_density(lwd=1, color="darkgreen")
plot2 <- ggplot(data.frame(x=chi.trans3), aes(x = x)) + 
         geom_density(lwd=1, color="darkred") 

# Mengatur grid 2x1
grid.arrange(
  plot1, plot2,
  ncol = 1
)

shapiro.test(data.chi)
## 
##  Shapiro-Wilk normality test
## 
## data:  data.chi
## W = 0.92826, p-value < 2.2e-16
shapiro.test(chi.trans3)
## 
##  Shapiro-Wilk normality test
## 
## data:  chi.trans3
## W = 0.92826, p-value < 2.2e-16

2 Heterokedastisitas

Asumsi lainnya yang penting adalah Ragam Homogen. Kehomogenan ragam dapat kita lihat melalui plot residual. Jika ragam homogen, maka pada plot residual, sisaan akan bersifat acak dengan memusat di sekitar nilai 0 dengan simpangan baku tertentu dan tidak terdapat pola-pola hubungan tertentu (misal pola berbentuk corong). Jika ingin menggunakan ukuran tertentu, adpat pula dilakukan uji seperti Uji Breusch-Pagan (BP), white atau Uji Glejser.

2.1 Contoh

Berikut Contoh Data dengan Ragam Yang Tidak Homogen

set.seed(123)

x<-rep(seq(1,5,),each=10)
e<-c()
for (i in 1:5) {
    e0<-rnorm(10,0,0.5*i)
    e<-c(e,e0) }

y<-15+(4*x)+e

mod<-lm(y~x)
res<-residuals(mod)
fits<-fitted(mod)

plot(x,y)

plot(fits,res)

2.2 Transformasi

# Transformasi x dengan 1/x dan y dengan y/x
xt<-1/x; yt<-y/x
mod.t<-lm(yt~xt)
res.t<-residuals(mod.t)
fitst<-fitted(mod.t)

plot(xt,yt)

plot(fitst,res.t)

library("rcompanion")
## Warning: package 'rcompanion' was built under R version 4.3.2
# Membuat fungsi transformasi Tukey's Ladder of Powers (Manual)
tfm.tukey <- function(X, lambda=0) {
    if(lambda < 0) return(X^lambda)
    else if (lambda == 0) return(log(X))
    else return(X^lambda)
}
# Transformasi Tukey dengan lambda = 1/4
chi.tk.0.5 <- sapply(data.chi, tfm.tukey, lambda=1/4)
hist(chi.tk.0.5, breaks=20)

qqnorm(chi.tk.0.5)
qqline(chi.tk.0.5, col="red")

shapiro.test(chi.tk.0.5)
## 
##  Shapiro-Wilk normality test
## 
## data:  chi.tk.0.5
## W = 0.9972, p-value = 0.07918

Fungsi transformTukey dari package rcompanion dapat digunakan untuk melakukan transformasi tukey. pada package ini, nilai lambda ditentukan batas bawah dan atas. Fungsi akan melakukan proses pencarian secara iteratif untuk memperoleh nilai terbaik dalam proses transformasi.

3 Transformasi Box-Cox

# Membuat fungsi transformasi Box-Cox (Manual)
box.cox <- function(X, lambda=0) {
    if(lambda==0) return(log(X))     # secara default fungsi log pada R menggunakan basis `e`
    else {
        return((X^lambda-1)/lambda)
    }
}
# Transformasi Box-Cox dengan lambda = 0.5 # belum menyebar normal
chi.bc.0.5 <- sapply(data.chi, box.cox, lambda=0.5)
hist(chi.bc.0.5, breaks=20)

qqnorm(chi.bc.0.5)
qqline(chi.bc.0.5, col="red")

shapiro.test(chi.bc.0.5)
## 
##  Shapiro-Wilk normality test
## 
## data:  chi.bc.0.5
## W = 0.99434, p-value = 0.0008126
# Transformasi Box-Cox dengan lambda = 0.3 # sudah menyebar normal 
chi.bc.0.3 <- sapply(data.chi, box.cox, lambda=0.3)
hist(chi.bc.0.3, breaks=20)

qqnorm(chi.bc.0.3)
qqline(chi.bc.0.3, col="red")

shapiro.test(chi.bc.0.3)
## 
##  Shapiro-Wilk normality test
## 
## data:  chi.bc.0.3
## W = 0.99869, p-value = 0.6839
library(caret)
## Loading required package: lattice
library(lattice)

df.chi = data.frame(bc=data.chi)

# Transformasi BoxCox menggunakan fungsi preProcess  (package caret)
# lambda dicari secara iteratif sehingga menghasilkan nilai lambda yang paling sesuai
preprocessParams <- preProcess(df.chi, method=c("BoxCox"))
chi.bc.caret <- predict(preprocessParams, df.chi)

hist(chi.bc.caret$bc, breaks=20)

qqnorm(chi.bc.caret$bc)
qqline(chi.bc.caret$bc, col="red")

shapiro.test(chi.bc.caret$bc)
## 
##  Shapiro-Wilk normality test
## 
## data:  chi.bc.caret$bc
## W = 0.99869, p-value = 0.6839
# Dapatkan nilai lambda
preprocessParams
## Created from 1000 samples and 1 variables
## 
## Pre-processing:
##   - Box-Cox transformation (1)
##   - ignored (0)
## 
## Lambda estimates for Box-Cox transformation:
## 0.3

Alternatif lainnya yaitu menggunakan fungsi boxcox dari package MASS.

4 Features Scalling

Feature scaling adalah salah satu langkah penting dalam pra-pemrosesan data khususnya dalam pembelajaran mesin. Features scaling merupakan teknik untuk mengubah rentang atau skala dari fitur-fitur (variabel-variabel) dalam dataset sehingga memiliki karakteristik yang serupa antar fitur. Hal ini penting karena banyak algoritma pembelajaran mesin yang sangat dipengaruhi oleh skala fitur, dan perbedaan skala pada fitur dapat memberikan hasil yang tidak diinginkan.

Terdapat dua metode umum yang digunakan dalam feature scaling:

  • Min-Max Scaling (Normalize Scaling): metode ini mengubah nilai-nilai fitur ke dalam rentang tertentu, biasanya antara (0 dan 1) atau (-1 dan 1).

  • Standardization (Z-score Scaling): metode ini mengubah nilai-nilai fitur sehingga memiliki rataan 0 dan simpangan baku 1.

Features scaling membantu algoritma pembelajaran mesin seperti regresi linier, K-Nearest Neighbors (KNN), dan Support Vector Machines (SVM) untuk beroperasi dengan lebih baik dan menghasilkan hasil yang lebih baik. Selain itu, ini juga dapat mempercepat konvergensi algoritma yang berbasis iterasi seperti Gradient Descent pada model Gradient Boosting atau Neural Network.

Dalam pemrosesan data, penting untuk memilih metode scaling yang sesuai dengan karakteristik data yand dimiliki. Pemilihan yang tepat akan membantu meningkatkan kualitas model dan analisis yang dilakukan.

Penting : Features scaling TIDAK MERUBAH sebaran data. Bentuk sebaran data asli dengan data yang sudah di scaling akan tetap sama persis

4.1 Data Contoh (Simulasi)

set.seed(111)

n = 1000

# Membangkitan 1000 data simulasi dengan 4 fitur (distribusi bervariasi)
X1 <- rnorm(n, mean=50, sd=6)
X2 <- rpois(n, lambda=7)
X3 <- rgamma(n,shape=5, scale=2000)
X4 <- rweibull(n, shape = 20)
Y <- sample(c("Sukses", "Gagal"), n, replace=T) 
# Y berupa data kategorik : SUKSES atau GAGAL

# Menggabungkan vektor-vektor menjadi data frame
fake.data <- data.frame(
  X1=round(X1, 2), 
  X2, # sudah diskrit, jadi dibiarkan apa adanya
  X3=round(X3, 1), 
  X4=round(X4, 2), 
  Y=as.factor(Y))

head(fake.data, 10)
##       X1 X2      X3   X4      Y
## 1  51.41  5 16381.2 1.03 Sukses
## 2  48.02  4 14043.3 1.05 Sukses
## 3  48.13  9  4572.4 0.98 Sukses
## 4  36.19  5  7031.2 0.94 Sukses
## 5  48.97  6  8682.9 1.04 Sukses
## 6  50.84 10 11677.3 0.98  Gagal
## 7  41.02  1 15417.9 0.97  Gagal
## 8  43.94 17  9079.8 0.98 Sukses
## 9  44.31  8 10782.9 0.88 Sukses
## 10 47.04 11 19129.3 0.91 Sukses
summary(fake.data)
##        X1              X2               X3              X4              Y      
##  Min.   :30.06   Min.   : 1.000   Min.   : 1230   Min.   :0.7000   Gagal :506  
##  1st Qu.:46.08   1st Qu.: 5.000   1st Qu.: 6686   1st Qu.:0.9300   Sukses:494  
##  Median :50.12   Median : 7.000   Median : 9549   Median :0.9800               
##  Mean   :50.06   Mean   : 6.884   Mean   :10279   Mean   :0.9703               
##  3rd Qu.:54.05   3rd Qu.: 8.000   3rd Qu.:12908   3rd Qu.:1.0200               
##  Max.   :67.56   Max.   :20.000   Max.   :36638   Max.   :1.1100
plot1 <- ggplot(fake.data, aes(x = X1)) + 
         geom_density(lwd=1, color="darkgreen")
plot2 <- ggplot(fake.data, aes(x = X2)) + 
         geom_density(lwd=1, color="darkgreen") 
plot3 <- ggplot(fake.data, aes(x = X3)) + 
         geom_density(lwd=1, color="darkgreen")
plot4 <- ggplot(fake.data, aes(x = X4)) + 
         geom_density(lwd=1, color="darkgreen")

# Mengatur grid 2x2
grid.arrange(
  plot1, plot2, plot3, plot4,
  ncol = 2
)

4.2 Min-Max Scaling (Normalized)

Contoh formula untuk menghitung Min-Max Scaling Manual (Min=0, Max=1):

# membuat fungsi Min-Max 
# (dengan nilai default min=0 dan max=1, namun dapat berlaku umum untuk min-max berapapun)
minmax.scaling <- function(col, min=0, max=1) {
    min.col <- min(col)
    max.col <- max(col)
    col.minmax <- (col - min.col)/(max.col-min.col) * (max - min) + min
  
    col.minmax
}

# contoh Min-Max scaling (0 dan 1)
fake.data.mm1 = data.frame(sapply(fake.data[,1:4], minmax.scaling))

summary(fake.data.mm1)
##        X1               X2               X3               X4        
##  Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:0.4271   1st Qu.:0.2105   1st Qu.:0.1541   1st Qu.:0.5610  
##  Median :0.5349   Median :0.3158   Median :0.2349   Median :0.6829  
##  Mean   :0.5335   Mean   :0.3097   Mean   :0.2556   Mean   :0.6593  
##  3rd Qu.:0.6399   3rd Qu.:0.3684   3rd Qu.:0.3298   3rd Qu.:0.7805  
##  Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000
head(fake.data.mm1)
##          X1        X2         X3        X4
## 1 0.5693333 0.2105263 0.42789401 0.8048780
## 2 0.4789333 0.1578947 0.36186581 0.8536585
## 3 0.4818667 0.4210526 0.09438369 0.6829268
## 4 0.1634667 0.2105263 0.16382641 0.5853659
## 5 0.5042667 0.2631579 0.21047459 0.8292683
## 6 0.5541333 0.4736842 0.29504400 0.6829268
# Contoh Min-Max scaling (-1 dan 1)
fake.data.mm2 = data.frame(sapply(fake.data[,1:4], minmax.scaling, min=-1, max=1))

summary(fake.data.mm2)
##        X1                 X2                X3                X4         
##  Min.   :-1.00000   Min.   :-1.0000   Min.   :-1.0000   Min.   :-1.0000  
##  1st Qu.:-0.14573   1st Qu.:-0.5789   1st Qu.:-0.6918   1st Qu.: 0.1220  
##  Median : 0.06987   Median :-0.3684   Median :-0.5302   Median : 0.3659  
##  Mean   : 0.06693   Mean   :-0.3806   Mean   :-0.4889   Mean   : 0.3186  
##  3rd Qu.: 0.27973   3rd Qu.:-0.2632   3rd Qu.:-0.3404   3rd Qu.: 0.5610  
##  Max.   : 1.00000   Max.   : 1.0000   Max.   : 1.0000   Max.   : 1.0000
head(fake.data.mm2)
##             X1          X2         X3        X4
## 1  0.138666667 -0.57894737 -0.1442120 0.6097561
## 2 -0.042133333 -0.68421053 -0.2762684 0.7073171
## 3 -0.036266667 -0.15789474 -0.8112326 0.3658537
## 4 -0.673066667 -0.57894737 -0.6723472 0.1707317
## 5  0.008533333 -0.47368421 -0.5790508 0.6585366
## 6  0.108266667 -0.05263158 -0.4099120 0.3658537
0.3658537
## [1] 0.3658537
# Density Plot Data Asli
plot1 <- ggplot(fake.data, aes(x = X1)) + 
         geom_density(lwd=1, color="darkgreen")
plot2 <- ggplot(fake.data, aes(x = X2)) + 
         geom_density(lwd=1, color="darkgreen") 
plot3 <- ggplot(fake.data, aes(x = X3)) + 
         geom_density(lwd=1, color="darkgreen")
plot4 <- ggplot(fake.data, aes(x = X4)) + 
         geom_density(lwd=1, color="darkgreen")

# Density Plot Scaled Data (Min: 0 Max: 1)
plot5 <- ggplot(fake.data.mm1, aes(x = X1)) + 
         geom_density(lwd=1, color="darkgreen")
plot6 <- ggplot(fake.data.mm1, aes(x = X2)) + 
         geom_density(lwd=1, color="darkgreen") 
plot7 <- ggplot(fake.data.mm1, aes(x = X3)) + 
         geom_density(lwd=1, color="darkgreen")
plot8 <- ggplot(fake.data.mm1, aes(x = X4)) + 
         geom_density(lwd=1, color="darkgreen")

# Density Plot Scaled Data (Min: -1 Max: 1)
plot9 <- ggplot(fake.data.mm2, aes(x = X1)) + 
         geom_density(lwd=1, color="darkgreen")
plot10 <- ggplot(fake.data.mm2, aes(x = X2)) + 
         geom_density(lwd=1, color="darkgreen") 
plot11 <- ggplot(fake.data.mm2, aes(x = X3)) + 
         geom_density(lwd=1, color="darkgreen")
plot12 <- ggplot(fake.data.mm2, aes(x = X4)) + 
         geom_density(lwd=1, color="darkgreen")


# Mengatur grid 4 * 3
grid.arrange(
  plot1, plot2, plot3, plot4,     # Plot sebaran data Asli
  plot5, plot6, plot7, plot8,     # Plot sebaran data (Min:0 Max:1) 
  plot9, plot10, plot11, plot12,  # Plot sebaran data (Min:-1 Max:1) 
  ncol = 4
)

4.3 Standardized

Formula untuk menghitung nilai standardized:

# membuat fungsi Standardized 
std.scaling <- function(col) {
    mean.col <- mean(col)
    sd.col <- sd(col)
    col.std <- (col - mean.col)/sd.col
  
    col.std
}

# contoh 
fake.data.std = data.frame(sapply(fake.data[,1:4], std.scaling))

summary(fake.data.std)
##        X1                  X2                 X3                X4         
##  Min.   :-3.374843   Min.   :-2.29607   Min.   :-1.9044   Min.   :-4.0935  
##  1st Qu.:-0.672678   1st Qu.:-0.73518   1st Qu.:-0.7562   1st Qu.:-0.6106  
##  Median : 0.009295   Median : 0.04527   Median :-0.1538   Median : 0.1466  
##  Mean   : 0.000000   Mean   : 0.00000   Mean   : 0.0000   Mean   : 0.0000  
##  3rd Qu.: 0.673133   3rd Qu.: 0.43549   3rd Qu.: 0.5532   3rd Qu.: 0.7523  
##  Max.   : 2.951438   Max.   : 5.11816   Max.   : 5.5473   Max.   : 2.1152
cat("\n\nVarians :\n")
## 
## 
## Varians :
sapply(fake.data.std, var)
## X1 X2 X3 X4 
##  1  1  1  1
head(fake.data.std)
##           X1         X2         X3         X4
## 1  0.2269195 -0.7351801  1.2841528  0.9037386
## 2 -0.3449763 -1.1254030  0.7921276  1.2066000
## 3 -0.3264193  0.8257118 -1.2010806  0.1465849
## 4 -2.3407073 -0.7351801 -0.6836113 -0.4591380
## 5 -0.1847106 -0.3449571 -0.3360010  1.0551693
## 6  0.1307600  1.2159347  0.2941886  0.1465849
# Density Plot Data Asli
plot1 <- ggplot(fake.data, aes(x = X1)) + 
         geom_density(lwd=1, color="darkgreen")
plot2 <- ggplot(fake.data, aes(x = X2)) + 
         geom_density(lwd=1, color="darkgreen") 
plot3 <- ggplot(fake.data, aes(x = X3)) + 
         geom_density(lwd=1, color="darkgreen")
plot4 <- ggplot(fake.data, aes(x = X4)) + 
         geom_density(lwd=1, color="darkgreen")

# Density Plot Scaled Data (Standardized)
plot5 <- ggplot(fake.data.std, aes(x = X1)) + 
         geom_density(lwd=1, color="darkgreen")
plot6 <- ggplot(fake.data.std, aes(x = X2)) + 
         geom_density(lwd=1, color="darkgreen") 
plot7 <- ggplot(fake.data.std, aes(x = X3)) + 
         geom_density(lwd=1, color="darkgreen")
plot8 <- ggplot(fake.data.std, aes(x = X4)) + 
         geom_density(lwd=1, color="darkgreen")

# Mengatur grid 4 * 2
grid.arrange(
  plot1, plot2, plot3, plot4,     # Plot sebaran data (Asli)
  plot5, plot6, plot7, plot8,     # Plot sebaran (Standardized)  
  ncol = 4
)

4.4 Penggunaan Package Caret

Fungsi preProcess pada package caret dapat digunakan untuk melakukan scaling, baik untuk Min-Max scaling maupun standardization

library(caret)

# Min-Max (0 dan 1)
preprocessParams <- preProcess(fake.data[,1:4], method=c("range"))
normalized <- predict(preprocessParams, fake.data[,1:4])

cat("Min-Max Scaling (0-1):\n\n")
## Min-Max Scaling (0-1):
summary(normalized)
##        X1               X2               X3               X4        
##  Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:0.4271   1st Qu.:0.2105   1st Qu.:0.1541   1st Qu.:0.5610  
##  Median :0.5349   Median :0.3158   Median :0.2349   Median :0.6829  
##  Mean   :0.5335   Mean   :0.3097   Mean   :0.2556   Mean   :0.6593  
##  3rd Qu.:0.6399   3rd Qu.:0.3684   3rd Qu.:0.3298   3rd Qu.:0.7805  
##  Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000
# Standardized
preprocessParams <- preProcess(fake.data[,1:4], method=c("center", "scale"))
standardized <- predict(preprocessParams, fake.data[,1:4])

cat("\n\nStandardized:\n\n")
## 
## 
## Standardized:
summary(standardized)
##        X1                  X2                 X3                X4         
##  Min.   :-3.374843   Min.   :-2.29607   Min.   :-1.9044   Min.   :-4.0935  
##  1st Qu.:-0.672678   1st Qu.:-0.73518   1st Qu.:-0.7562   1st Qu.:-0.6106  
##  Median : 0.009295   Median : 0.04527   Median :-0.1538   Median : 0.1466  
##  Mean   : 0.000000   Mean   : 0.00000   Mean   : 0.0000   Mean   : 0.0000  
##  3rd Qu.: 0.673133   3rd Qu.: 0.43549   3rd Qu.: 0.5532   3rd Qu.: 0.7523  
##  Max.   : 2.951438   Max.   : 5.11816   Max.   : 5.5473   Max.   : 2.1152
cat("\n\nVarians :\n")
## 
## 
## Varians :
sapply(standardized, var)
## X1 X2 X3 X4 
##  1  1  1  1
head(normalized)
##          X1        X2         X3        X4
## 1 0.5693333 0.2105263 0.42789401 0.8048780
## 2 0.4789333 0.1578947 0.36186581 0.8536585
## 3 0.4818667 0.4210526 0.09438369 0.6829268
## 4 0.1634667 0.2105263 0.16382641 0.5853659
## 5 0.5042667 0.2631579 0.21047459 0.8292683
## 6 0.5541333 0.4736842 0.29504400 0.6829268
head(standardized)
##           X1         X2         X3         X4
## 1  0.2269195 -0.7351801  1.2841528  0.9037386
## 2 -0.3449763 -1.1254030  0.7921276  1.2066000
## 3 -0.3264193  0.8257118 -1.2010806  0.1465849
## 4 -2.3407073 -0.7351801 -0.6836113 -0.4591380
## 5 -0.1847106 -0.3449571 -0.3360010  1.0551693
## 6  0.1307600  1.2159347  0.2941886  0.1465849

5 Features Encoding

Encoding adalah proses krusial yaitu mengubah data input, terutama data kategorikal atau teks, menjadi bentuk numerik agar dapat dimengerti dan diproses oleh algoritma atau model machine learning. Hal ini penting karena sebagian besar algoritma machine learning hanya dapat bekerja dengan data dalam bentuk numerik. Encoding memungkinkan model untuk memahami dan memanfaatkan informasi dalam data tersebut untuk tujuan seperti klasifikasi, prediksi, atau analisis data, membuatnya menjadi langkah awal yang kritis dalam pengembangan model machine learning yang efektif

Terdapat berbagai tipe encoding untuk transformasi data kategorik, beberapa diantaranya yaitu:

  • One-Hot Encoding

  • Binary Encoding

  • Label Encoding

5.1 Data Contoh (Simulasi)

set.seed(111)

n <- 1000

# Membangkitan 1000 data simulasi dengan 3 fitur Kategorik
Xa <- sample(c("Belum Menikah", "Menikah", "Cerai"), n, replace=T, prob=c(0.4, 0.5, 0.1))
Xb <- sample(c("Sumatera", "Jawa", "Kalimantan", "lainnya"), n, replace=T, prob=c(0.3, 0.5, 0.1, 0.1)) 
Xc <- sample(c("SD", "SMP", "SMA", "Sarjana"), n, replace=T, prob=c(0.05, 0.15, 0.4, 0.4))
Y <- sample(c("Sukses", "Gagal"), n, replace=T, prob=c(0.7, 0.3))

# Menggabungkan vektor-vektor menjadi data frame
fake.data.cat <- data.frame(    
  Xa=as.factor(Xa), 
  Xb=as.factor(Xb), 
  Xc=as.factor(Xc),
  Y=as.factor(Y))

head(fake.data.cat, 10)
##               Xa         Xb      Xc      Y
## 1  Belum Menikah    lainnya Sarjana  Gagal
## 2  Belum Menikah   Sumatera Sarjana Sukses
## 3        Menikah       Jawa     SMA  Gagal
## 4  Belum Menikah       Jawa Sarjana Sukses
## 5        Menikah Kalimantan     SMA Sukses
## 6        Menikah   Sumatera     SMP Sukses
## 7        Menikah   Sumatera Sarjana Sukses
## 8  Belum Menikah       Jawa      SD Sukses
## 9        Menikah       Jawa     SMA  Gagal
## 10       Menikah    lainnya     SMP Sukses

Sebagai ilustrasi, misal kita akan melakukan encoding untuk fitur Xa menggunakan One-Hot Encoding, Xb menggunakan Binary Encoding dan Xc menggunakan Label Encoding

5.2 Encoding

# Gunakan dummyVars() untuk one-hot encoding

one_hot_encoded <- dummyVars(~ Xa, data = fake.data.cat)   
one_hot_Xa <- predict(one_hot_encoded, newdata = fake.data.cat)

# jika diperlukan dapat menghapus 1 kolom referensi : one_hot_Xa[,-1]

head(one_hot_Xa)
##   Xa.Belum Menikah Xa.Cerai Xa.Menikah
## 1                1        0          0
## 2                1        0          0
## 3                0        0          1
## 4                1        0          0
## 5                0        0          1
## 6                0        0          1
one_hot_encoded <- dummyVars(~ Xb, data = fake.data.cat)   
one_hot_Xb <- predict(one_hot_encoded, newdata = fake.data.cat)

# jika diperlukan dapat menghapus 1 kolom referensi : one_hot_Xb[,-1]

head(one_hot_Xb)
##   Xb.Jawa Xb.Kalimantan Xb.lainnya Xb.Sumatera
## 1       0             0          1           0
## 2       0             0          0           1
## 3       1             0          0           0
## 4       1             0          0           0
## 5       0             1          0           0
## 6       0             0          0           1
# fungsi konversi teks angka binary menjadi int
bin.to.int <- function(bin_string) {
  bin_len <- nchar(bin_string)
  int_val <- 0

  for (i in 1:bin_len) {
    digit <- substr(bin_string, i, i)
    if (digit == "1") {
      int_val <- int_val + 2^(bin_len - i)
    }
  }

  return(int_val)
}
# menggabung setiap kolom One-hot Encoding Xb
bin_xb <- do.call(paste, c(data.frame(one_hot_Xb), sep = ""))

head(bin_xb, 10)
##  [1] "0010" "0001" "1000" "1000" "0100" "0001" "0001" "1000" "1000" "0010"
# konversi nilai binary menjadi integer
int_xb = sapply(bin_xb, bin.to.int) 
head(int_xb, 10)
## 0010 0001 1000 1000 0100 0001 0001 1000 1000 0010 
##    2    1    8    8    4    1    1    8    8    2
label_encoded_Xc <- as.integer(factor(fake.data.cat$Xc))

# Tampilkan hasil
head(label_encoded_Xc)
## [1] 1 1 3 1 3 4
# mengupdate dataframe menggunakan fitur hasil encoding
fake.data.cat <- cbind(one_hot_Xa, fake.data.cat[,-1])
fake.data.cat$Xb <- int_xb
fake.data.cat$Xc <- label_encoded_Xc

head(fake.data.cat)
##   Xa.Belum Menikah Xa.Cerai Xa.Menikah Xb Xc      Y
## 1                1        0          0  2  1  Gagal
## 2                1        0          0  1  1 Sukses
## 3                0        0          1  8  3  Gagal
## 4                1        0          0  8  1 Sukses
## 5                0        0          1  4  3 Sukses
## 6                0        0          1  1  4 Sukses

6 Penanganan Missing Values

Missing value seringkali terdapat pada data yang akan dilakukan proses analisis. Adanya missing value pada data dapat menyebabkan ukuran sampel berkurang, sehingga analisis yang dilakukan dapat menghasilkan pendugaan yang berbias, sampai kesimpulan yang tidak valid

6.1 Ilustrasi Missing Value

  • Diketahui data BostonHousing di R yang merupakan data Housing data untuk 506 census tracts di Boston pada 1970.

  • Akan dilakukan ilustrasi pemberian missing value pada data tersebut dan ilustrasi pengecekannya.

data("BostonHousing", package = 'mlbench')
original <- BostonHousing #backup original data
str(original)
## 'data.frame':    506 obs. of  14 variables:
##  $ crim   : num  0.00632 0.02731 0.02729 0.03237 0.06905 ...
##  $ zn     : num  18 0 0 0 0 0 12.5 12.5 12.5 12.5 ...
##  $ indus  : num  2.31 7.07 7.07 2.18 2.18 2.18 7.87 7.87 7.87 7.87 ...
##  $ chas   : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
##  $ nox    : num  0.538 0.469 0.469 0.458 0.458 0.458 0.524 0.524 0.524 0.524 ...
##  $ rm     : num  6.58 6.42 7.18 7 7.15 ...
##  $ age    : num  65.2 78.9 61.1 45.8 54.2 58.7 66.6 96.1 100 85.9 ...
##  $ dis    : num  4.09 4.97 4.97 6.06 6.06 ...
##  $ rad    : num  1 2 2 3 3 3 5 5 5 5 ...
##  $ tax    : num  296 242 242 222 222 222 311 311 311 311 ...
##  $ ptratio: num  15.3 17.8 17.8 18.7 18.7 18.7 15.2 15.2 15.2 15.2 ...
##  $ b      : num  397 397 393 395 397 ...
##  $ lstat  : num  4.98 9.14 4.03 2.94 5.33 ...
##  $ medv   : num  24 21.6 34.7 33.4 36.2 28.7 22.9 27.1 16.5 18.9 ...

Variabel rad (index of accessibility to radial highways) dan pratio (pupil-teacher ratio by town) dibuat missing value masing-masing sebanyak 40 obs secara acak.

# Pengenalan Missing Values
set.seed(100)
BostonHousing[sample(1:nrow(BostonHousing), 40), "rad"] <- NA
BostonHousing[sample(1:nrow(BostonHousing), 40), "ptratio"] <- NA
head(BostonHousing,20)
##       crim   zn indus chas   nox    rm   age    dis rad tax ptratio      b
## 1  0.00632 18.0  2.31    0 0.538 6.575  65.2 4.0900   1 296    15.3 396.90
## 2  0.02731  0.0  7.07    0 0.469 6.421  78.9 4.9671   2 242    17.8 396.90
## 3  0.02729  0.0  7.07    0 0.469 7.185  61.1 4.9671   2 242    17.8 392.83
## 4  0.03237  0.0  2.18    0 0.458 6.998  45.8 6.0622  NA 222    18.7 394.63
## 5  0.06905  0.0  2.18    0 0.458 7.147  54.2 6.0622   3 222    18.7 396.90
## 6  0.02985  0.0  2.18    0 0.458 6.430  58.7 6.0622   3 222    18.7 394.12
## 7  0.08829 12.5  7.87    0 0.524 6.012  66.6 5.5605  NA 311      NA 395.60
## 8  0.14455 12.5  7.87    0 0.524 6.172  96.1 5.9505   5 311    15.2 396.90
## 9  0.21124 12.5  7.87    0 0.524 5.631 100.0 6.0821   5 311    15.2 386.63
## 10 0.17004 12.5  7.87    0 0.524 6.004  85.9 6.5921   5 311    15.2 386.71
## 11 0.22489 12.5  7.87    0 0.524 6.377  94.3 6.3467   5 311    15.2 392.52
## 12 0.11747 12.5  7.87    0 0.524 6.009  82.9 6.2267   5 311    15.2 396.90
## 13 0.09378 12.5  7.87    0 0.524 5.889  39.0 5.4509   5 311    15.2 390.50
## 14 0.62976  0.0  8.14    0 0.538 5.949  61.8 4.7075   4 307    21.0 396.90
## 15 0.63796  0.0  8.14    0 0.538 6.096  84.5 4.4619   4 307    21.0 380.02
## 16 0.62739  0.0  8.14    0 0.538 5.834  56.5 4.4986   4 307    21.0 395.62
## 17 1.05393  0.0  8.14    0 0.538 5.935  29.3 4.4986   4 307    21.0 386.85
## 18 0.78420  0.0  8.14    0 0.538 5.990  81.7 4.2579   4 307    21.0 386.75
## 19 0.80271  0.0  8.14    0 0.538 5.456  36.6 3.7965   4 307    21.0 288.99
## 20 0.72580  0.0  8.14    0 0.538 5.727  69.5 3.7965   4 307    21.0 390.95
##    lstat medv
## 1   4.98 24.0
## 2   9.14 21.6
## 3   4.03 34.7
## 4   2.94 33.4
## 5   5.33 36.2
## 6   5.21 28.7
## 7  12.43 22.9
## 8  19.15 27.1
## 9  29.93 16.5
## 10 17.10 18.9
## 11 20.45 15.0
## 12 13.27 18.9
## 13 15.71 21.7
## 14  8.26 20.4
## 15 10.26 18.2
## 16  8.47 19.9
## 17  6.58 23.1
## 18 14.67 17.5
## 19 11.69 20.2
## 20 11.28 18.2
# Pengecekan Pola Missing Value
library(mice)
## Warning: package 'mice' was built under R version 4.3.2
## 
## Attaching package: 'mice'
## The following object is masked from 'package:stats':
## 
##     filter
## The following objects are masked from 'package:base':
## 
##     cbind, rbind
md.pattern(BostonHousing)

##     crim zn indus chas nox rm age dis tax b lstat medv rad ptratio   
## 430    1  1     1    1   1  1   1   1   1 1     1    1   1       1  0
## 36     1  1     1    1   1  1   1   1   1 1     1    1   1       0  1
## 36     1  1     1    1   1  1   1   1   1 1     1    1   0       1  1
## 4      1  1     1    1   1  1   1   1   1 1     1    1   0       0  2
##        0  0     0    0   0  0   0   0   0 0     0    0  40      40 80

6.2 Penanganan Missing Value

6.2.1 Deleting the Observations

If you have large number of observations in your dataset, where all the classes to be predicted are sufficiently represented in the training data, then try deleting (or not to include missing values while model building, for example by setting na.action=na.omit) those observations (rows) that contain missing values. Make sure after deleting the observations, you have:

  1. Have sufficent data points, so the model doesn’t lose power.

  2. Not to introduce bias (meaning, disproportionate or non-representation of classes).

6.2.2 Deleting the Variable

  • If a particular variable is having more missing values that rest of the variables in the dataset, and, if by removing that one variable you can save many observations.

  • Then, the suggest to remove that particular variable, unless it is a really important predictor that makes a lot of business sense would be essential.

  • It is a matter of deciding between the importance of the variable and losing out on a number of observations.

6.2.3 Imputation with Mean or Median or Mode

  • Replacing the missing values with the mean / median / mode is a crude way of treating missing values.

  • Depending on the context, like if the variation is low or if the variable has low leverage over the response, such a rough approximation is acceptable and could possibly give satisfactory results.

6.2.4 Prediction

Prediction is most advanced method to impute your missing values nd includes different approaches such as:

  • kNN Imputation,

  • Rpart (Recursive Partitioning and Regression Trees),

  • Mice (Multivariate Imputation by Chained Equations).

6.3 Missing Values: Regresi

Ilustrasi menyisihkan missing value untuk pemodelan regresi

lm(medv ~ ptratio + rad, data=BostonHousing, na.action=na.omit)
## 
## Call:
## lm(formula = medv ~ ptratio + rad, data = BostonHousing, na.action = na.omit)
## 
## Coefficients:
## (Intercept)      ptratio          rad  
##     57.0465      -1.7691      -0.2032
library(Hmisc)
## Warning: package 'Hmisc' was built under R version 4.3.2
## 
## Attaching package: 'Hmisc'
## The following objects are masked from 'package:base':
## 
##     format.pval, units
head(data.frame(impute(BostonHousing$ptratio, mean)),20) # replace with mean
##    impute.BostonHousing.ptratio..mean.
## 1                             15.30000
## 2                             17.80000
## 3                             17.80000
## 4                             18.70000
## 5                             18.70000
## 6                             18.70000
## 7                             18.43884
## 8                             15.20000
## 9                             15.20000
## 10                            15.20000
## 11                            15.20000
## 12                            15.20000
## 13                            15.20000
## 14                            21.00000
## 15                            21.00000
## 16                            21.00000
## 17                            21.00000
## 18                            21.00000
## 19                            21.00000
## 20                            21.00000
head(data.frame(impute(BostonHousing$ptratio, median)),20) # median
##    impute.BostonHousing.ptratio..median.
## 1                                   15.3
## 2                                   17.8
## 3                                   17.8
## 4                                   18.7
## 5                                   18.7
## 6                                   18.7
## 7                                   19.0
## 8                                   15.2
## 9                                   15.2
## 10                                  15.2
## 11                                  15.2
## 12                                  15.2
## 13                                  15.2
## 14                                  21.0
## 15                                  21.0
## 16                                  21.0
## 17                                  21.0
## 18                                  21.0
## 19                                  21.0
## 20                                  21.0
head(data.frame(impute(BostonHousing$ptratio, 20)),20) # replace specific numberK
##    impute.BostonHousing.ptratio..20.
## 1                               15.3
## 2                               17.8
## 3                               17.8
## 4                               18.7
## 5                               18.7
## 6                               18.7
## 7                               20.0
## 8                               15.2
## 9                               15.2
## 10                              15.2
## 11                              15.2
## 12                              15.2
## 13                              15.2
## 14                              21.0
## 15                              21.0
## 16                              21.0
## 17                              21.0
## 18                              21.0
## 19                              21.0
## 20                              21.0

Keakuratan ketika diisi dengan nilai mean

# install package: DMwR dari github, karena tidak tersedia di R versi ini
#install.packages("devtools")
#devtools::install_github("cran/DMwR")
library(DMwR)
## Loading required package: grid
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
actuals <- original$ptratio[is.na(BostonHousing$ptratio)]
predicteds <- rep(mean(BostonHousing$ptratio, na.rm=T), 
length(actuals))
regr.eval(actuals, predicteds)
##        mae        mse       rmse       mape 
## 1.70028970 3.90808804 1.97688847 0.09577023
library(DMwR)
knnOutput <- knnImputation(BostonHousing[, 
!names(BostonHousing) %in% "medv"]) # perform knn imputation.
anyNA(knnOutput)
## [1] FALSE
actuals <- original$ptratio[is.na(BostonHousing$ptratio)]
predicteds <- knnOutput[is.na(BostonHousing$ptratio), "ptratio"]
regr.eval(actuals, predicteds)
##        mae        mse       rmse       mape 
## 0.55280823 1.07043179 1.03461673 0.03394082

6.4 Prediksi dengan Rpart

library(rpart)
class_mod <- rpart(rad ~ . - medv, 
data=BostonHousing[!is.na(BostonHousing$rad), ], 
method="class", na.action=na.omit)
anova_mod <- rpart(ptratio ~ . - medv, 
data=BostonHousing[!is.na(BostonHousing$ptratio), ], 
method="anova", na.action=na.omit)
rad_pred <- predict(class_mod, 
BostonHousing[is.na(BostonHousing$rad), ])
ptratio_pred <- predict(anova_mod, 
BostonHousing[is.na(BostonHousing$ptratio), ])
actuals <- original$ptratio[is.na(BostonHousing$ptratio)]
predicteds <- ptratio_pred
regr.eval(actuals, predicteds)
##        mae        mse       rmse       mape 
## 0.45583994 0.67213722 0.81983975 0.02592722
actuals <- original$rad[is.na(BostonHousing$rad)]
predicteds <- as.numeric(colnames(rad_pred)[apply(rad_pred, 1, 
which.max)])
mean(actuals != predicteds) # compute misclass error.
## [1] 0.225

6.5 Prediksi dengan Mice

library(mice)
miceMod <- mice(BostonHousing[, !names(BostonHousing) %in% 
"medv"], method="rf") # perform mice imputation, based on random forests.
## 
##  iter imp variable
##   1   1  rad  ptratio
##   1   2  rad  ptratio
##   1   3  rad  ptratio
##   1   4  rad  ptratio
##   1   5  rad  ptratio
##   2   1  rad  ptratio
##   2   2  rad  ptratio
##   2   3  rad  ptratio
##   2   4  rad  ptratio
##   2   5  rad  ptratio
##   3   1  rad  ptratio
##   3   2  rad  ptratio
##   3   3  rad  ptratio
##   3   4  rad  ptratio
##   3   5  rad  ptratio
##   4   1  rad  ptratio
##   4   2  rad  ptratio
##   4   3  rad  ptratio
##   4   4  rad  ptratio
##   4   5  rad  ptratio
##   5   1  rad  ptratio
##   5   2  rad  ptratio
##   5   3  rad  ptratio
##   5   4  rad  ptratio
##   5   5  rad  ptratio
miceOutput <- complete(miceMod) # generate the completeddata.
anyNA(miceOutput)
## [1] FALSE
actuals <- original$ptratio[is.na(BostonHousing$ptratio)]
predicteds <- miceOutput[is.na(BostonHousing$ptratio), "ptratio"]
regr.eval(actuals, predicteds)
##        mae        mse       rmse       mape 
## 0.25500000 0.59950000 0.77427385 0.01431601
actuals <- original$rad[is.na(BostonHousing$rad)]
predicteds <- miceOutput[is.na(BostonHousing$rad), "rad"]
mean(actuals != predicteds) # compute misclass error.
## [1] 0.3