Tugas Individu 2 - Pengantar Sains Data

Dicky Girsang

2022-12-4

Cakupan Materi Responsi

  • Introduction to Machine Learning:
    1. Regresi Linear
    2. Regresi Ridge
    3. Regresi Lasso
    4. Model Averaging
  • Variable Selection:
    1. Subset Selection
    2. Stepwise Selection
    3. Model optimal
  • Unsupervised Learning:
    1. Metode Linkage
    2. Metode K-Means
    3. Metode penentuan gerombol terbaik

Library

library(glmnet)
library(lars)
library(car)
library(corrplot)
library(Matrix)
library(plotmo)
library(dplyr)
library(caret)
library(repr)
library(cowplot)
library(factoextra)
library(knitr)
library(mlr3verse)
library(mlr3fselect)
library(DataExplorer)
library(qgraph)
library(PerformanceAnalytics)
library(tidyverse)
library(broom)
library(glmnetUtils)
library(leaps)
library(varbvs)
library(ggplot2)
library(tidyverse)
library(plyr)
library(readr)
library(skimr)
library(leaps)
library(FactoMineR)

Data

Data yang digunakan adalah Auto dari library ISLR. Data tersebut memuat informasi 392 kendaraan yang terdiri atas 9 peubah. Pada analisis ini, digunakan sebanyak enam peubah numerik, yaitu mpg, horsepower, weight, cylinders, displacement, dan acceleration .

Peubah Keterangan
mpg Miles per gallon
cylinders Number of cylinders
displacement Engine displacement (cu. inches)
horsepower Engine horsepower
weight Vehicle weight (lbs.)
acceleration Time to accelerate from 0 to 60 mph (sec.)
library(ISLR)
df.auto = Auto[,-7:-9]
tibble(df.auto)
## # A tibble: 392 x 6
##      mpg cylinders displacement horsepower weight acceleration
##    <dbl>     <dbl>        <dbl>      <dbl>  <dbl>        <dbl>
##  1    18         8          307        130   3504         12  
##  2    15         8          350        165   3693         11.5
##  3    18         8          318        150   3436         11  
##  4    16         8          304        150   3433         12  
##  5    17         8          302        140   3449         10.5
##  6    15         8          429        198   4341         10  
##  7    14         8          454        220   4354          9  
##  8    14         8          440        215   4312          8.5
##  9    14         8          455        225   4425         10  
## 10    15         8          390        190   3850          8.5
## # ... with 382 more rows
keterangan tiap peubah:
Peubah Keterangan
mpg Peubah respon (Y)
cylinders Peubah penjelas (X1)
displacement Peubah penjelas (X2)
horsepower Peubah penjelas (X3)
weight Peubah penjelas (X4)
acceleration Peubah penjelas (X5)

Data splitting

Training (70%) dan Testing Data (30%)

set.seed(123) 

index = sample(1:nrow(df.auto), 0.7*nrow(df.auto)) 

train = df.auto[index,] # Data train 
test = df.auto[-index,] # Data test

Supervised Learning (Non-Linear Regression)

RIDGE Regression

Regresi Ridge adalah suatu teknik yang dikembangkan untuk menstabilkan nilai koefisien regresi karena adanya Multikolinieritas. Regresi Ridge merupakan modifikasi dari Metode Kuadrat Terkecil yang menghasilkan penduga bias dari koefisien regresi (Kutner et al. 2005). ### Cara 1

custom <- trainControl(method = "repeatedcv",
                       number = 10,
                       repeats = 5,
                       verboseIter = F)
#fitting  Ridge Regression model
set.seed(1234)
ridge <- train(mpg~.,train,
               method = "glmnet",
               tuneGrid = expand.grid(alpha = 0, 
                                      lambda = seq(0.0001, 1, length = 10)),
               trControl = custom)
ridge
## glmnet 
## 
## 274 samples
##   5 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold, repeated 5 times) 
## Summary of sample sizes: 246, 247, 246, 246, 246, 247, ... 
## Resampling results across tuning parameters:
## 
##   lambda  RMSE      Rsquared   MAE     
##   0.0001  4.392848  0.7095722  3.345782
##   0.1112  4.392848  0.7095722  3.345782
##   0.2223  4.392848  0.7095722  3.345782
##   0.3334  4.392848  0.7095722  3.345782
##   0.4445  4.392848  0.7095722  3.345782
##   0.5556  4.392848  0.7095722  3.345782
##   0.6667  4.393057  0.7095606  3.345835
##   0.7778  4.396220  0.7092550  3.344699
##   0.8889  4.399518  0.7089377  3.344076
##   1.0000  4.402893  0.7086257  3.343903
## 
## Tuning parameter 'alpha' was held constant at a value of 0
## RMSE was used to select the optimal model using the smallest value.
## The final values used for the model were alpha = 0 and lambda = 0.5556.

Dengan menggunakan data training pada alpha = 0 menghasilkan output yang membentuk model regresi berbasis Ridge regression dari beberapa jenis lambda dengan cross validation sejumlah sepuluh lipatan yang diulang sebanyak lima kali. Dengan demikian, didapatkan lambda optimum sebesar 0.556.

#mean validation score
mean(ridge$resample$RMSE)
## [1] 4.392848

Mean validation score merupakan rata-rata dari 50 RMSE yang dikeluarkan, yakni hasil dari sepuluh lipatan yang diulang sebanyak lima kali. Didapatkan rata-ratanya berjumlah 4.2476

#plotting the model
plot1 <- plot(ridge, main = "Ridge Regression")

#plotting important variables
plot2 <- plot(varImp(ridge, scale = F), main = "Variables Importance")
plot_grid(plot1, plot2)

Berdasarkan kedua plot tersebut, plot pertama menunjukkan bahwa RMSE minimum terjadi pada saat regularization parameter berada di sekitar angka 0.5 (lambda optimum 0.56). Sementara, plot kedua menunjukkan peubah cylinders yang paling berpengaruh terhadap peubah respon mpg. Silinder atau cylinder block adalah komponen yang sangat penting dalam rangkaian mesin mobil. Komponen ini merupakan penopang untuk komponen mesin lainnya.

Cara 2

lambdas <- 10^seq(2,-3, by = -.1)
ridge_reg = glmnet(as.matrix(train[,-1]), train[,1], nlambda = 25, alpha = 0, family = 'gaussian', lambda = lambdas)
summary(ridge_reg)
##           Length Class     Mode   
## a0         51    -none-    numeric
## beta      255    dgCMatrix S4     
## df         51    -none-    numeric
## dim         2    -none-    numeric
## lambda     51    -none-    numeric
## dev.ratio  51    -none-    numeric
## nulldev     1    -none-    numeric
## npasses     1    -none-    numeric
## jerr        1    -none-    numeric
## offset      1    -none-    logical
## call        7    -none-    call   
## nobs        1    -none-    numeric
ridge_reg
## 
## Call:  glmnet::glmnet(x = as.matrix(train[, -1]), y = train[, 1], nlambda = 25,      alpha = 0, family = "gaussian", lambda = lambdas) 
## 
##    Df  %Dev  Lambda
## 1   5 29.13 100.000
## 2   5 33.61  79.430
## 3   5 38.23  63.100
## 4   5 42.83  50.120
## 5   5 47.27  39.810
## 6   5 51.39  31.620
## 7   5 55.08  25.120
## 8   5 58.26  19.950
## 9   5 60.91  15.850
## 10  5 63.07  12.590
## 11  5 64.77  10.000
## 12  5 66.09   7.943
## 13  5 67.11   6.310
## 14  5 67.88   5.012
## 15  5 68.47   3.981
## 16  5 68.92   3.162
## 17  5 69.27   2.512
## 18  5 69.54   1.995
## 19  5 69.76   1.585
## 20  5 69.94   1.259
## 21  5 70.09   1.000
## 22  5 70.21   0.794
## 23  5 70.31   0.631
## 24  5 70.39   0.501
## 25  5 70.46   0.398
## 26  5 70.51   0.316
## 27  5 70.55   0.251
## 28  5 70.58   0.200
## 29  5 70.60   0.158
## 30  5 70.62   0.126
## 31  5 70.63   0.100
## 32  5 70.64   0.079
## 33  5 70.65   0.063
## 34  5 70.65   0.050
## 35  5 70.65   0.040
## 36  5 70.66   0.032
## 37  5 70.66   0.025
## 38  5 70.66   0.020
## 39  5 70.66   0.016
## 40  5 70.66   0.013
## 41  5 70.66   0.010
## 42  5 70.66   0.008
## 43  5 70.66   0.006
## 44  5 70.66   0.005
## 45  5 70.66   0.004
## 46  5 70.66   0.003
## 47  5 70.66   0.003
## 48  5 70.66   0.002
## 49  5 70.66   0.002
## 50  5 70.66   0.001
## 51  5 70.66   0.001

Pada cara kedua ini dilakukan setting nilai lambda yang akan dikeluarkan, yaitu 10^seq(2,-3, by = -.1) yang berjumlah 51 lambda dari nilai 100 sampai dengan 0.001.

set.seed(100)
#tunning parameter lambda dg cv
cv_ridge <- cv.glmnet(as.matrix(train[,-1]), train[,1], alpha = 0, lambda = lambdas)
optimal_lambda <- cv_ridge$lambda.min
optimal_lambda
## [1] 0.3981072

lambda minimum bernilai 0.2511886 yang mendekati output ridge_reg dengan lambda = 0.251 pada indeks ke-27 dengan persentase deviasi adalah 68.46%.

coef(cv_ridge, s = "lambda.min")
## 6 x 1 sparse Matrix of class "dgCMatrix"
##                        s1
## (Intercept)  45.050319569
## cylinders    -0.509352541
## displacement -0.008765559
## horsepower   -0.043381131
## weight       -0.003957876
## acceleration -0.037746155
# Compute R^2 from true and predicted values
eval_results <- function(true, predicted, df) {
  SSE <- sum((predicted - true)^2)
  SST <- sum((true - mean(true))^2)
  R_square <- 1 - SSE / SST
  RMSE = sqrt(SSE/nrow(df))
 
  # Model performance metrics
  data.frame(RMSE = RMSE,
             Rsquare = R_square)
}

# Prediction and evaluation on train data
predictions_train <- predict(ridge_reg, s = optimal_lambda, newx = as.matrix(train[,-1]))
eval_results(train[,1], predictions_train, train)
##       RMSE   Rsquare
## 1 4.356121 0.7045943
# Prediction and evaluation on test data
predictions_test <- predict(ridge_reg, s = optimal_lambda, newx = as.matrix(test[,-1]))
eval_results(test[,1], predictions_test, test)
##       RMSE   Rsquare
## 1 3.945211 0.7022579
Data RMSE R-square
Training 4.2178 0.6846
Testing 4.2795 0.7520

Diperoleh hasil bahwa nilai akurasi (RMSE) dan R-square dari data training dan testing yang terlihat keduanya memiliki nilai RMSE tidak berbeda jauh

LASSO Regression

Metode ini menggunakan teknik L1 Regularization dalam fungsi tujuan. Keuntungan regresi lasso dibandingkan regresi ridge adalah regresi lasso dapat memilih variabel bawaan serta penyusutan parameter. Persamaan regresi ridge dan lasso adalah sama-sama digunakan untuk menangani multikolinieritas

lambdas <- 10^seq(2, -3, by = -.1)
lasso_reg <- cv.glmnet(as.matrix(train[,-1]), train[,1], alpha = 1, 
                       lambda = lambdas, standardize = TRUE, nfolds = 5)

# Best lambda
lambda_best <- lasso_reg$lambda.min 
lambda_best
## [1] 0.1258925

Model regresi LASSO dibangun dengan data training dengan alpha = 1. Dilakukan setting nilai lambda yang akan dikeluarkan, yaitu 10^seq(2, -3, by = -.1) yang berjumlah 51 lambda dari nilai 100 sampai dengan 0.001. Lambda optimum yakni 0.1585 yang didapatkan dari lambda minimum lasso_reg

lasso_model <- glmnet(as.matrix(train[,-1]), train[,1], alpha = 1, 
                      lambda = lambda_best, standardize = TRUE)
lasso_model
## 
## Call:  glmnet::glmnet(x = as.matrix(train[, -1]), y = train[, 1], alpha = 1,      lambda = lambda_best, standardize = TRUE) 
## 
##   Df  %Dev Lambda
## 1  4 70.63 0.1259

Diperoleh hasil persentase deviasi dari lambda optimum, yaitu 68.43%.

coef(lasso_reg, s = "lambda.min")
## 6 x 1 sparse Matrix of class "dgCMatrix"
##                        s1
## (Intercept)  45.487805889
## cylinders    -0.381526599
## displacement -0.001640779
## horsepower   -0.040096436
## weight       -0.005119180
## acceleration  .

Kemudian, dibuat model regresi lasso menggunakan koefisien hasil dari output di atas.

predictions_train <- predict(lasso_model, s = lambda_best, newx = as.matrix(train[,-1]))
eval_results(train[,1], predictions_train, train)
##       RMSE   Rsquare
## 1 4.343434 0.7063125
predictions_test <- predict(lasso_model, s = lambda_best, newx = as.matrix(test[,-1]))
eval_results(test[,1], predictions_test, test)
##       RMSE   Rsquare
## 1 3.920406 0.7059902
Data RMSE R-square
Training 4.2194 0.6843
Testing 4.2391 0.7567

Berdasarkan output di atas, didapatkan nilai akurasi (RMSE) dan R-square dari data training dan testing yang terlihat keduanya memiliki nilai RMSE tidak berbeda jauh.

Cluster Analysis

Standardisasi Peubah

# fungsi standardisasi
data.stdz = scale(df.auto)
# cek mean = 0
apply(data.stdz, 2, mean)
##           mpg     cylinders  displacement    horsepower        weight 
##  1.569283e-16 -9.915610e-17 -5.786680e-17 -1.767977e-16 -8.020330e-18 
##  acceleration 
## -2.319512e-16

Pada output di atas, rata-rata keenam peubah yang sudah terstandardisasi bernilai sangat kecil (mendekati nol).

# cek sd = 1
apply(data.stdz, 2, sd)
##          mpg    cylinders displacement   horsepower       weight acceleration 
##            1            1            1            1            1            1

Pada output di atas, simpangan baku dari keenam peubah yang sudah terstandardisasi bernilai satu.

Hierarchical Clustering

Pemilihan Banyaknya Cluster

Dalam hal ini, digunakan observasi terhadap dendogram serta melihat koefisien silhouette dan koefisien WSS.

Dendogram

Apabila metode yang digunakan berbeda, ternyata hasil dendogram juga berbeda. Plot di atas dikelompokkan berdasarkan kemiripan dendogram. Perbedaan yang tidak terlalu jauh terdapat pada dendogram dengan metode complete linkage dan average linkage. Selanjutnya, dilihat jumlah cluster yang terbentuk berdasarkan koefisien silhouette yang akan dicobakan selanjutnya.

Koefisien Sihouette

# Complete Linkage
plota <- fviz_nbclust(data.stdz, FUNcluster = hcut, method = "silhouette", hc_method = "complete", hc_metric="euclidean")

# Average Linkage
plotb <- fviz_nbclust(data.stdz, FUNcluster = hcut, method = "silhouette", hc_method = "average", hc_metric="euclidean")

plot_grid(plota, plotb)

# Centroid Linkage
plotc <- fviz_nbclust(data.stdz, FUNcluster = hcut, method = "silhouette", hc_method = "centroid", hc_metric="euclidean")

# Single Linkage
plotd <- fviz_nbclust(data.stdz, FUNcluster = hcut, method = "silhouette", hc_method = "single", hc_metric="euclidean")

plot_grid(plotc, plotd)

# Ward Linkage
fviz_nbclust(data.stdz, FUNcluster = hcut, method = "silhouette", hc_method = "ward.D", hc_metric="euclidean")

Hanya satu metode linkage yang menghasilkan koefisien silhouette tertinggi saat jumlah cluster sama dengan delapan (k = 8), yaitu single linkage. Sementara itu, keempat metode lainnya menghasilkan koefisien silhouette tertinggi saat jumlah cluster sama dengan dua (k = 2).

Hierarchical Clustering dengan Metode Complete Linkage

Diterapkan hierarchical clustering dengan menggunakan metode yang terlihat baik dari hasil dendogramnya, yaitu complete linkage dengan banyak cluster adalah 2 (k optimum)

hc.auto <- eclust(df.auto, stand = TRUE, FUNcluster = "hclust", k = 2, hc_method = "complete", hc_metric = "euclidean", graph = F)
aggregate(df.auto, by=list(cluster=hc.auto$cluster), FUN = mean)
##   cluster      mpg cylinders displacement horsepower   weight acceleration
## 1       1 14.68400  7.980000     345.4700  160.40000 4121.560      12.7020
## 2       2 26.44658  4.613014     142.6798   85.31507 2585.812      16.5137

Diketahui bahwa cluster 1 berisi kendaraan dengan spesifikasi yang lebih tinggi dibandingkan dengan cluster 2. Pernyataan ini ditunjukkan dengan nilai rata-rata beberapa peubah yang lebih tinggi, seperti blok silinder, kekuatan mesin, dan lainnya. Sementara itu, peubah mpg pada cluster 1 terlampau lebih rendah daripada cluster 2. Hal ini menunjukkan bahwa dengan banyaknya bahan bakar yang sama, kendaraan pada cluster 2 dapat menempuh jarak yang lebih jauh dibandingkan dengan kendaraan pada cluster 1.

fviz_cluster(hc.auto)

Berdasarkan plot di atas, dapat dilihat secara visual lebih banyak kendaraan yang berada di cluster 2 dibandingkan pada cluster 1. Kedua komponen utama dapat menjelaskan sekitar 91.9% keragaman pada data.

Non-Hierarchical Clustering (K-Means)

Pemilihan Banyaknya Cluster

Dalam hal ini penentuan banyaknya cluster dengan melihat koefisien silhouette dan koefisien WSS.

Koefisien Silhouette

fviz_nbclust(data.stdz, FUNcluster = kmeans, method = "silhouette")

Berdasarkan output di atas, dengan menggunakan koefisien silhouette jumlah cluster optimum yang didapatkan adalah dua (k = 2).

Within Sum of Square (WSS)

fviz_nbclust(data.stdz, FUNcluster = kmeans, method = "wss")

Banyaknya cluster yang dipilih didasarkan pada di mana garis berbentuk seperti siku (elbow) sehingga bersifat subjektif dan terpilih k = 3 untuk penggerombolan dengan metode K-Means.

K-Means

kmeans.auto <- eclust(df.auto, stand = TRUE, FUNcluster = "kmeans", k = 3, graph = F)
kmeans.auto$centers
##          mpg  cylinders displacement  horsepower     weight acceleration
## 1 -0.4653346  0.3347858    0.2281863 -0.06918306  0.2926493    0.3077339
## 2 -1.1424784  1.4699656    1.4679162  1.48955594  1.3731827   -1.0511897
## 3  0.7628541 -0.8600086   -0.8099465 -0.68343021 -0.7941342    0.3630997

Output di atas menghasilkan centroid untuk data yang masih terstandardisasi sehingga sulit diinterpretasikan, digunakan fungsi aggregate untuk menampilkan rata-rata peubah asli.

aggregate(df.auto, by=list(cluster=kmeans.auto$cluster), FUN = mean)
##   cluster      mpg cylinders displacement horsepower   weight acceleration
## 1       1 19.81398  6.043011     218.2903  101.80645 3226.161     16.39032
## 2       2 14.52887  7.979381     348.0206  161.80412 4143.969     12.64124
## 3       3 29.40000  4.004950     109.6559   78.16337 2303.045     16.54307

Tabel di atas adalah rata-rata setiap peubah pada masing-masing cluster yang dibuat. Cluster 2 berisi kendaraan dengan spesifikasi yang lebih tinggi dibandingkan dengan cluster 1 dan 3. Hal ini ditunjukkan dengan rata-rata beberapa peubah bernilai paling tinggi, misalnya blok silinder, kekuatan mesin, dan berat. Untuk peubah mpg, cluster 2 lebih rendah daripada cluster 1 dan 3. Hal ini menunjukkan bahwa dengan banyak bahan bakar yang sama, kendaraan pada cluster 2 dapat menempuh jarak yang lebih sedikit dibandingkan dengan kendaraan pada cluster 1 dan 3.

fviz_cluster(kmeans.auto)

Dengan membandingkan kedua metode yang digunakan, terjadi perbedaan yang signifikan terhadap hasil penggerombolan. Jumlah cluster yang lebih baik dalam mengelompokkan data adalah tiga (K-Means Clustering), contohnya dapat disesuaikan dengan data aktual peubah displacement yang bernilai 4, 6, dan 8. Selain itu, nilai rata-rata peubah pada setiap cluster dengan (k = 3) sangat berbeda satu sama lain. Hal ini sesuai dengan prinsip penggerombolan, di mana penggerombolan akan lebih baik jika antar gerombol heterogen. Pada kedua metode, tidak terjadi overlap sehingga penggerombolan terbilang cukup baik.

Variables Selection

Data

Data yang digunakan berasal dari library yang sama (ISLR), yaitu dataset Hitters. Data ini berisi informasi tentang indikator dalam pertandingan basket musim 1986 dan 1987 yang diamati per individu, yaitu berjumlah 322 pemain basket.

df.hitters <- Hitters
tibble(df.hitters)
## # A tibble: 322 x 20
##    AtBat  Hits HmRun  Runs   RBI Walks Years CAtBat CHits CHmRun CRuns  CRBI
##    <int> <int> <int> <int> <int> <int> <int>  <int> <int>  <int> <int> <int>
##  1   293    66     1    30    29    14     1    293    66      1    30    29
##  2   315    81     7    24    38    39    14   3449   835     69   321   414
##  3   479   130    18    66    72    76     3   1624   457     63   224   266
##  4   496   141    20    65    78    37    11   5628  1575    225   828   838
##  5   321    87    10    39    42    30     2    396   101     12    48    46
##  6   594   169     4    74    51    35    11   4408  1133     19   501   336
##  7   185    37     1    23     8    21     2    214    42      1    30     9
##  8   298    73     0    24    24     7     3    509   108      0    41    37
##  9   323    81     6    26    32     8     2    341    86      6    32    34
## 10   401    92    17    49    66    65    13   5206  1332    253   784   890
## # ... with 312 more rows, and 8 more variables: CWalks <int>, League <fct>,
## #   Division <fct>, PutOuts <int>, Assists <int>, Errors <int>, Salary <dbl>,
## #   NewLeague <fct>
glimpse(df.hitters)
## Rows: 322
## Columns: 20
## $ AtBat     <int> 293, 315, 479, 496, 321, 594, 185, 298, 323, 401, 574, 202, ~
## $ Hits      <int> 66, 81, 130, 141, 87, 169, 37, 73, 81, 92, 159, 53, 113, 60,~
## $ HmRun     <int> 1, 7, 18, 20, 10, 4, 1, 0, 6, 17, 21, 4, 13, 0, 7, 3, 20, 2,~
## $ Runs      <int> 30, 24, 66, 65, 39, 74, 23, 24, 26, 49, 107, 31, 48, 30, 29,~
## $ RBI       <int> 29, 38, 72, 78, 42, 51, 8, 24, 32, 66, 75, 26, 61, 11, 27, 1~
## $ Walks     <int> 14, 39, 76, 37, 30, 35, 21, 7, 8, 65, 59, 27, 47, 22, 30, 11~
## $ Years     <int> 1, 14, 3, 11, 2, 11, 2, 3, 2, 13, 10, 9, 4, 6, 13, 3, 15, 5,~
## $ CAtBat    <int> 293, 3449, 1624, 5628, 396, 4408, 214, 509, 341, 5206, 4631,~
## $ CHits     <int> 66, 835, 457, 1575, 101, 1133, 42, 108, 86, 1332, 1300, 467,~
## $ CHmRun    <int> 1, 69, 63, 225, 12, 19, 1, 0, 6, 253, 90, 15, 41, 4, 36, 3, ~
## $ CRuns     <int> 30, 321, 224, 828, 48, 501, 30, 41, 32, 784, 702, 192, 205, ~
## $ CRBI      <int> 29, 414, 266, 838, 46, 336, 9, 37, 34, 890, 504, 186, 204, 1~
## $ CWalks    <int> 14, 375, 263, 354, 33, 194, 24, 12, 8, 866, 488, 161, 203, 2~
## $ League    <fct> A, N, A, N, N, A, N, A, N, A, A, N, N, A, N, A, N, A, A, N, ~
## $ Division  <fct> E, W, W, E, E, W, E, W, W, E, E, W, E, E, E, W, W, W, W, W, ~
## $ PutOuts   <int> 446, 632, 880, 200, 805, 282, 76, 121, 143, 0, 238, 304, 211~
## $ Assists   <int> 33, 43, 82, 11, 40, 421, 127, 283, 290, 0, 445, 45, 11, 151,~
## $ Errors    <int> 20, 10, 14, 3, 4, 25, 7, 9, 19, 0, 22, 11, 7, 6, 8, 0, 10, 1~
## $ Salary    <dbl> NA, 475.000, 480.000, 500.000, 91.500, 750.000, 70.000, 100.~
## $ NewLeague <fct> A, N, A, N, N, A, A, A, N, A, A, N, N, A, N, A, N, A, A, N, ~

Eksplorasi Data

plot_intro(data = df.hitters,
           geom_label_args = list(size=2.5))

skim_without_charts(data = df.hitters)
Data summary
Name df.hitters
Number of rows 322
Number of columns 20
_______________________
Column type frequency:
factor 3
numeric 17
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
League 0 1 FALSE 2 A: 175, N: 147
Division 0 1 FALSE 2 W: 165, E: 157
NewLeague 0 1 FALSE 2 A: 176, N: 146

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100
AtBat 0 1.00 380.93 153.40 16.0 255.25 379.5 512.00 687
Hits 0 1.00 101.02 46.45 1.0 64.00 96.0 137.00 238
HmRun 0 1.00 10.77 8.71 0.0 4.00 8.0 16.00 40
Runs 0 1.00 50.91 26.02 0.0 30.25 48.0 69.00 130
RBI 0 1.00 48.03 26.17 0.0 28.00 44.0 64.75 121
Walks 0 1.00 38.74 21.64 0.0 22.00 35.0 53.00 105
Years 0 1.00 7.44 4.93 1.0 4.00 6.0 11.00 24
CAtBat 0 1.00 2648.68 2324.21 19.0 816.75 1928.0 3924.25 14053
CHits 0 1.00 717.57 654.47 4.0 209.00 508.0 1059.25 4256
CHmRun 0 1.00 69.49 86.27 0.0 14.00 37.5 90.00 548
CRuns 0 1.00 358.80 334.11 1.0 100.25 247.0 526.25 2165
CRBI 0 1.00 330.12 333.22 0.0 88.75 220.5 426.25 1659
CWalks 0 1.00 260.24 267.06 0.0 67.25 170.5 339.25 1566
PutOuts 0 1.00 288.94 280.70 0.0 109.25 212.0 325.00 1378
Assists 0 1.00 106.91 136.85 0.0 7.00 39.5 166.00 492
Errors 0 1.00 8.04 6.37 0.0 3.00 6.0 11.00 32
Salary 59 0.82 535.93 451.12 67.5 190.00 425.0 750.00 2460

Dari 20 peubah, terdapat 17 peubah bertipe numerik dan sisanya bertipe faktor. Pada data yang digunakan, terdapat 59 missing values yang seluruhnya berada pada peubah salary. Selain itu, terdapat informasi mengenai rata-rata, standar deviasi, dan persentil masing-masing peubah.

Menangani Missing Values

df.hitters1 <- na.omit(df.hitters)
tibble(df.hitters1)
## # A tibble: 263 x 20
##    AtBat  Hits HmRun  Runs   RBI Walks Years CAtBat CHits CHmRun CRuns  CRBI
##    <int> <int> <int> <int> <int> <int> <int>  <int> <int>  <int> <int> <int>
##  1   315    81     7    24    38    39    14   3449   835     69   321   414
##  2   479   130    18    66    72    76     3   1624   457     63   224   266
##  3   496   141    20    65    78    37    11   5628  1575    225   828   838
##  4   321    87    10    39    42    30     2    396   101     12    48    46
##  5   594   169     4    74    51    35    11   4408  1133     19   501   336
##  6   185    37     1    23     8    21     2    214    42      1    30     9
##  7   298    73     0    24    24     7     3    509   108      0    41    37
##  8   323    81     6    26    32     8     2    341    86      6    32    34
##  9   401    92    17    49    66    65    13   5206  1332    253   784   890
## 10   574   159    21   107    75    59    10   4631  1300     90   702   504
## # ... with 253 more rows, and 8 more variables: CWalks <int>, League <fct>,
## #   Division <fct>, PutOuts <int>, Assists <int>, Errors <int>, Salary <dbl>,
## #   NewLeague <fct>

Dilakukan penghapusan terhadap baris dengan missing values sehingga didapatkan data bersih sebanyak 263 amatan.

plot_intro(df.hitters1)

Dilihat lagi metrik gambaran umum dari data bersih yang sudah tidak memiliki missing value, didapatkan hasil seperti pada plot di atas. Memori yang terpakai lebih rendah daripada penggunaan memori pada data yang belum ditangani missing values-nya. Hal ini disebabkan oleh baris data yang berkurang setelah dilakukan penghapusan. Terlihat pula seluruh baris memiliki amatan yang lengkap.

skim_without_charts(df.hitters1)
Data summary
Name df.hitters1
Number of rows 263
Number of columns 20
_______________________
Column type frequency:
factor 3
numeric 17
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
League 0 1 FALSE 2 A: 139, N: 124
Division 0 1 FALSE 2 W: 134, E: 129
NewLeague 0 1 FALSE 2 A: 141, N: 122

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100
AtBat 0 1 403.64 147.31 19.0 282.5 413 526.0 687
Hits 0 1 107.83 45.13 1.0 71.5 103 141.5 238
HmRun 0 1 11.62 8.76 0.0 5.0 9 18.0 40
Runs 0 1 54.75 25.54 0.0 33.5 52 73.0 130
RBI 0 1 51.49 25.88 0.0 30.0 47 71.0 121
Walks 0 1 41.11 21.72 0.0 23.0 37 57.0 105
Years 0 1 7.31 4.79 1.0 4.0 6 10.0 24
CAtBat 0 1 2657.54 2286.58 19.0 842.5 1931 3890.5 14053
CHits 0 1 722.19 648.20 4.0 212.0 516 1054.0 4256
CHmRun 0 1 69.24 82.20 0.0 15.0 40 92.5 548
CRuns 0 1 361.22 331.20 2.0 105.5 250 497.5 2165
CRBI 0 1 330.42 323.37 3.0 95.0 230 424.5 1659
CWalks 0 1 260.27 264.06 1.0 71.0 174 328.5 1566
PutOuts 0 1 290.71 279.93 0.0 113.5 224 322.5 1377
Assists 0 1 118.76 145.08 0.0 8.0 45 192.0 492
Errors 0 1 8.59 6.61 0.0 3.0 7 13.0 32
Salary 0 1 535.93 451.12 67.5 190.0 425 750.0 2460

Berdasarkan output di atas, seluruh peubah kategorik memiliki lebih dari satu kategori sehingga tidak perlu penghapusan peubah kategorik. Selain itu, terdapat perubahan pada informasi peubah pada rata-rata, standar deviasi, dan persentil setelah dilakukan penanganan missing values pada data.

Memeriksa Sebaran Data

plot_histogram(data = df.hitters1, nrow=3, ncol = 3,
               geom_histogram_args = list(fill="maroon"))

Histogram di atas menggambarkan sebaran dari 17 peubah numerik. Terlihat sebaran sebagian besar peubah cenderung menjulur ke kanan. Artinya, frekuensi terbesar dari peubah yang digunakan berada pada rentang nilai yang rendah.

plot_bar(df.hitters1)

Diagram batang di atas merupakan besaran dari masing-masing peubah kategorik, yaitu league, division, dan new league. Frekuensi kategori dari masing-masing peubah tidak berbeda signifikan atau hampir balance.

Periksa korelasi peubah yang digunakan

cor_mat <- cor(df.hitters1%>%
                   select_if(is.numeric),method = "spearman")
cor_mat[upper.tri(cor_mat,diag = TRUE)] <- NA 
cor_df <- cor_mat   %>%
    as.data.frame() %>% 
    rownames_to_column(var = "Var1") %>%
  pivot_longer(names_to = "Var2",
               values_to = "corr",
               -Var1) %>% na.omit
cor_df %>% filter(abs(corr)>0.6) %>% arrange(desc(abs(corr)))
## # A tibble: 42 x 3
##    Var1   Var2    corr
##    <chr>  <chr>  <dbl>
##  1 CHits  CAtBat 0.997
##  2 CRuns  CHits  0.990
##  3 CRuns  CAtBat 0.989
##  4 Hits   AtBat  0.972
##  5 CRBI   CHits  0.968
##  6 CRBI   CAtBat 0.968
##  7 CRBI   CRuns  0.962
##  8 CWalks CRuns  0.950
##  9 CWalks CAtBat 0.944
## 10 CWalks CHits  0.940
## # ... with 32 more rows

Dilakukan pemeriksaan terhadap korelasi antarpeubah. Nilai korelasi di atas difilter sehingga hanya mencetak output berupa peubah dengan korelasi yang lebih besar dari 0.6.

cor_df %>% filter(abs(corr)<=0.6)  
## # A tibble: 94 x 3
##    Var1   Var2    corr
##    <chr>  <chr>  <dbl>
##  1 HmRun  AtBat 0.534 
##  2 HmRun  Hits  0.529 
##  3 Walks  HmRun 0.448 
##  4 Years  AtBat 0.0526
##  5 Years  Hits  0.0814
##  6 Years  HmRun 0.170 
##  7 Years  Runs  0.0427
##  8 Years  RBI   0.166 
##  9 Years  Walks 0.154 
## 10 CAtBat AtBat 0.320 
## # ... with 84 more rows

Nilai korelasi di atas difilter sehingga hanya mencetak output berupa peubah dengan korelasi yang lebih kecil dari 0.6.

Seleksi Peubah

1. Regresi Linear

regresi1 <- lm(formula = Salary~.,data = df.hitters1)
broom::tidy(regresi1) %>% mutate(across(where(is.numeric),                                ~ format(round(.x,3),                                             big.mark=",",                                             scientific=F)))
## # A tibble: 20 x 5
##    term        estimate std.error statistic  p.value
##    <chr>          <dbl>     <dbl>     <dbl>    <dbl>
##  1 (Intercept)  163.      90.8        1.80  0.0736  
##  2 AtBat         -1.98     0.634     -3.12  0.00201 
##  3 Hits           7.50     2.38       3.15  0.00181 
##  4 HmRun          4.33     6.20       0.698 0.486   
##  5 Runs          -2.38     2.98      -0.797 0.426   
##  6 RBI           -1.04     2.60      -0.402 0.688   
##  7 Walks          6.23     1.83       3.41  0.000766
##  8 Years         -3.49    12.4       -0.281 0.779   
##  9 CAtBat        -0.171    0.135     -1.27  0.206   
## 10 CHits          0.134    0.675      0.199 0.843   
## 11 CHmRun        -0.173    1.62      -0.107 0.915   
## 12 CRuns          1.45     0.750      1.94  0.0538  
## 13 CRBI           0.808    0.693      1.17  0.245   
## 14 CWalks        -0.812    0.328     -2.47  0.0141  
## 15 LeagueN       62.6     79.3        0.790 0.430   
## 16 DivisionW   -117.      40.4       -2.89  0.00414 
## 17 PutOuts        0.282    0.0774     3.64  0.000333
## 18 Assists        0.371    0.221      1.68  0.0947  
## 19 Errors        -3.36     4.39      -0.765 0.445   
## 20 NewLeagueN   -24.8     79.0       -0.313 0.754

Output di atas menghasilkan koefisien regresi dari masing-masing peubah yang selanjutnya digunakan untuk membuat persamaan model.

2. Forward Selection

best_forward <- regsubsets(Salary~.,data = df.hitters1, method = "forward", nvmax = 19)
summ_forward <- summary(best_forward)
result_gof <- data.frame(
  Adj.R2 = c(which.max(summ_forward$adjr2),max(summ_forward$adjr2)),
  CP = c(which.min(summ_forward$cp),min(summ_forward$cp)),
  BIC = c(which.min(summ_forward$bic),min(summ_forward$bic))
)
result_gof
##       Adj.R2        CP       BIC
## 1 11.0000000 10.000000    6.0000
## 2  0.5225706  5.009317 -147.9169
coef_forward <- coef(best_forward, result_gof$BIC[1])
enframe(coef_forward) %>% mutate(across(where(is.numeric),                                ~ format(round(.x,3),                                             big.mark=",",                                             scientific=F)))
## # A tibble: 7 x 2
##   name           value
##   <chr>          <dbl>
## 1 (Intercept)   91.5  
## 2 AtBat         -1.87 
## 3 Hits           7.60 
## 4 Walks          3.70 
## 5 CRBI           0.643
## 6 DivisionW   -123.   
## 7 PutOuts        0.264
plot_forward <- data.frame(subset=seq_along(summ_forward$bic),
                           BIC = summ_forward$bic)
ggplot(plot_forward, aes(subset, BIC)) +
  geom_line(size = 1, color = "darkgrey") +
  geom_point(color = "steelblue", size = 2)+
  geom_vline(aes(xintercept = result_gof$BIC[1]),
             color = "steelblue", size = 1)+
  theme_bw()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## i Please use `linewidth` instead.

Berdasarkan hasil seleksi peubah dengan metode forward selection, subset terbaik dengan nilai BIC terendah adalah -147.9169, yang terjadi saat subset = 6. Peubah penjelas yang masuk ke dalam model adalah AtBat, Hits, Walks, CRBI, DivisionW, dan PutOuts.

3. Backward Selection

best_backward <- regsubsets(Salary~., data = df.hitters1, method = "backward", nvmax = 19)
summ_backward <- summary(best_backward)
result_gof2 <- data.frame(
  Adj.R2 = c(which.max(summ_backward$adjr2),max(summ_backward$adjr2)),
  CP = c(which.min(summ_backward$cp),min(summ_backward$cp)),
  BIC = c(which.min(summ_backward$bic),min(summ_backward$bic))
)
result_gof2
##       Adj.R2        CP      BIC
## 1 11.0000000 10.000000    8.000
## 2  0.5225706  5.009317 -147.382
coef_backward <-  coef(best_backward,result_gof2$BIC[1])
enframe(coef_backward) %>% mutate(across(where(is.numeric),                                ~ format(round(.x,3),                                             big.mark=",",                                             scientific=F)))
## # A tibble: 9 x 2
##   name           value
##   <chr>          <dbl>
## 1 (Intercept)  117.   
## 2 AtBat         -2.03 
## 3 Hits           6.85 
## 4 Walks          6.44 
## 5 CRuns          0.705
## 6 CRBI           0.527
## 7 CWalks        -0.807
## 8 DivisionW   -124.   
## 9 PutOuts        0.275
plot_backward <- data.frame(subset = seq_along(summ_backward$bic),
                           BIC = summ_backward$bic)
ggplot(plot_backward, aes(subset, BIC))+
  geom_line(size=1, color = "darkgrey")+
  geom_point(color="steelblue", size=2)+
  geom_vline(aes(xintercept = result_gof$BIC[1]),
             color = "steelblue", size = 1.2)+
  theme_bw()

Berdasarkan hasil seleksi peubah dengan metode forward selection, subset terbaik dengan nilai BIC terendah adalah -147.382, yang terjadi saat subset = 6. Peubah penjelas yang masuk ke dalam model adalah AtBat, Hits, Walks, CRuns, CRBI, CWalks, DivisionW, dan PutOuts.