TUJUAN

Analisis dalam LBB C2 ini bertujuan untuk memprediksi (mendiagnosa) penyakit kanker payudara yang dialami seorang pasien berdasarkan data yang telah dilaporkan oleh Dr. William H. Wolberg yang mana datasetnya bisa didapatkan di https://archive.ics.uci.edu/ml/datasets/breast+cancer+wisconsin+(original).

Analisis akan menggunakan pendekatan metode Supervised Learning (Classfication Machine Learning) yaitu dengan Klasifikasi Naive Bayes dan Decision Tree.

READ DATA

bc <-  read.csv("breast-cancer-wisconsin.data")

names(bc) <-  c("Id_number",
                "Clump_thickness",
                "Uniformity_of_cell_size",
                "Uniformity_of_cell_shape",
                "Marginal_adhesion",
                "Single_epithelial_cell_Sz",
                "Bare_nuclei",
                "Bland_chromatin",
                "Normal_nucleoli",
                "Mitoses",
                "Class")

head(bc)
#>   Id_number Clump_thickness Uniformity_of_cell_size Uniformity_of_cell_shape
#> 1   1002945               5                       4                        4
#> 2   1015425               3                       1                        1
#> 3   1016277               6                       8                        8
#> 4   1017023               4                       1                        1
#> 5   1017122               8                      10                       10
#> 6   1018099               1                       1                        1
#>   Marginal_adhesion Single_epithelial_cell_Sz Bare_nuclei Bland_chromatin
#> 1                 5                         7          10               3
#> 2                 1                         2           2               3
#> 3                 1                         3           4               3
#> 4                 3                         2           1               3
#> 5                 8                         7          10               9
#> 6                 1                         2          10               3
#>   Normal_nucleoli Mitoses Class
#> 1               2       1     2
#> 2               1       1     2
#> 3               7       1     2
#> 4               1       1     2
#> 5               7       1     4
#> 6               1       1     2

Keterangan:

  1. Sample code number: id number
  2. Clump Thickness: 1 - 10
  3. Uniformity of Cell Size: 1 - 10
  4. Uniformity of Cell Shape: 1 - 10
  5. Marginal Adhesion: 1 - 10
  6. Single Epithelial Cell Size: 1 - 10
  7. Bare Nuclei: 1 - 10
  8. Bland Chromatin: 1 - 10
  9. Normal Nucleoli: 1 - 10
  10. Mitoses: 1 - 10
  11. Class: (2 for benign, 4 for malignant)

EDA

Kolom Id_number tidak dipakai sehingga dihilangkan. Kolom lain yang tipe datanya belum sesuai, kita ubah di sini.

library(dplyr)

bc_clean <- bc %>% 
  select(-c(Id_number)) %>% 
  mutate ( Bare_nuclei =as.numeric(Bare_nuclei),
           Class = as.factor(Class)
                    )
str(bc_clean)
#> 'data.frame':    698 obs. of  10 variables:
#>  $ Clump_thickness          : int  5 3 6 4 8 1 2 2 4 1 ...
#>  $ Uniformity_of_cell_size  : int  4 1 8 1 10 1 1 1 2 1 ...
#>  $ Uniformity_of_cell_shape : int  4 1 8 1 10 1 2 1 1 1 ...
#>  $ Marginal_adhesion        : int  5 1 1 3 8 1 1 1 1 1 ...
#>  $ Single_epithelial_cell_Sz: int  7 2 3 2 7 2 2 2 2 1 ...
#>  $ Bare_nuclei              : num  10 2 4 1 10 10 1 1 1 1 ...
#>  $ Bland_chromatin          : int  3 3 3 3 9 3 3 1 2 3 ...
#>  $ Normal_nucleoli          : int  2 1 7 1 7 1 1 1 1 1 ...
#>  $ Mitoses                  : int  1 1 1 1 1 1 1 5 1 1 ...
#>  $ Class                    : Factor w/ 2 levels "2","4": 1 1 1 1 2 1 1 1 1 1 ...

Cek missing values.

is.na(bc_clean) %>% colSums()
#>           Clump_thickness   Uniformity_of_cell_size  Uniformity_of_cell_shape 
#>                         0                         0                         0 
#>         Marginal_adhesion Single_epithelial_cell_Sz               Bare_nuclei 
#>                         0                         0                        16 
#>           Bland_chromatin           Normal_nucleoli                   Mitoses 
#>                         0                         0                         0 
#>                     Class 
#>                         0

Terdapat missing values (NA) pada kolom Bare-nuclei maka dilakukan langkah berikut ini:

bc_clean <- bc_clean %>% 
  filter(complete.cases(.)) # complete cases utk filter data-data yang barisnya tidak ada NA

is.na(bc_clean) %>% colSums()
#>           Clump_thickness   Uniformity_of_cell_size  Uniformity_of_cell_shape 
#>                         0                         0                         0 
#>         Marginal_adhesion Single_epithelial_cell_Sz               Bare_nuclei 
#>                         0                         0                         0 
#>           Bland_chromatin           Normal_nucleoli                   Mitoses 
#>                         0                         0                         0 
#>                     Class 
#>                         0

Cek proporsi data pada kelas target.

prop.table(table(bc_clean$Class))
#> 
#>         2         4 
#> 0.6495601 0.3504399

Proporsi data 64,95% dan 35,50% kurang balance, sehingga di langkah berikutnya kita akan melakukan balancing data.

Cross-Validation

Split data bc_clean menjadi data training dan data testing, dengan 75% data akan digunakan sebagai data training.

RNGkind(sample.kind = "Rounding")
set.seed(100)
library(rsample)

# train-test splitting
index <- sample(nrow(bc_clean), nrow(bc_clean)*0.75)

bc_clean_train <- bc_clean[index, ]
bc_clean_test <- bc_clean[-index, ]

Cek kembali proporsi kelas target.

# cek proporsi votes dengan data votes_train
prop.table(table(bc_clean_train$Class))
#> 
#>         2         4 
#> 0.6536204 0.3463796

Kita coba melakukan split data menggunakan Stratified Random Sampling seperti berikut.

RNGkind(sample.kind = "Rounding")
set.seed(100)
library(rsample)

# Stratified Random Sampling

index_n <- initial_split(data = bc_clean, # data yang digunakan untuk splitting
                         prop = 0.75,  # proporsi data train
                         strata = Class) # variabel target

bc_train_n <- training(index_n) # fungsi training bawaan dari library rsample
bc_test_n <- testing(index_n)
# cek porporsi votes_train_n
prop.table(table(bc_train_n$Class))
#> 
#>         2         4 
#> 0.6497065 0.3502935

Ternyata proporsi data kelas target masih belum balance. Mari lakukan treatment pada data train yang imbalance menggunakan upSample. Alasan digunakan metode upsample adalah karena datanya sedikit yaitu 511 observasi pada data-train.

RNGkind(sample.kind = "Rounding")
set.seed(100)
library(caret)

bc_train_balance <- upSample (x=bc_clean_train %>% select(-Class), #hanya select variabel prediktor saja
                         y=bc_clean_train$Class, # hanya selesct variabel target saja
                         yname="Class") # nama kolom target
prop.table(table(bc_train_balance$Class))
#> 
#>   2   4 
#> 0.5 0.5

Sekarang proporsi data sudah balance. Data sudah siap maka selanjutnya kita buat pemodelan.

PEMODELAN

Naive Bayes

Menggunakan argumen naiveBayes(formula, data)

Di sini digunakan metode Laplace Smoothing untuk dapat memastikan model tidak terlalu ekstrim dalam mengklasifikasikan observasi serta tetap dapat mempertimbangkan nilai peluang dari prediktor lainnya.

library(e1071)
model_NB <- naiveBayes(formula= Class~., data= bc_train_balance, laplace = 1)
model_NB
#> 
#> Naive Bayes Classifier for Discrete Predictors
#> 
#> Call:
#> naiveBayes.default(x = X, y = Y, laplace = laplace)
#> 
#> A-priori probabilities:
#> Y
#>   2   4 
#> 0.5 0.5 
#> 
#> Conditional probabilities:
#>    Clump_thickness
#> Y       [,1]     [,2]
#>   2 3.011976 1.708442
#>   4 7.550898 2.245205
#> 
#>    Uniformity_of_cell_size
#> Y       [,1]      [,2]
#>   2 1.272455 0.8168764
#>   4 6.562874 2.7268984
#> 
#>    Uniformity_of_cell_shape
#> Y       [,1]      [,2]
#>   2 1.428144 0.9519625
#>   4 6.431138 2.5970162
#> 
#>    Marginal_adhesion
#> Y       [,1]      [,2]
#>   2 1.347305 0.9518633
#>   4 5.502994 3.2648354
#> 
#>    Single_epithelial_cell_Sz
#> Y       [,1]      [,2]
#>   2 2.089820 0.8115262
#>   4 5.437126 2.5832561
#> 
#>    Bare_nuclei
#> Y       [,1]     [,2]
#>   2 1.341317 1.191927
#>   4 7.670659 3.122029
#> 
#>    Bland_chromatin
#> Y       [,1]     [,2]
#>   2 2.095808 1.108165
#>   4 5.997006 2.370361
#> 
#>    Normal_nucleoli
#> Y       [,1]      [,2]
#>   2 1.260479 0.9622582
#>   4 5.916168 3.3743658
#> 
#>    Mitoses
#> Y       [,1]      [,2]
#>   2 1.023952 0.1716234
#>   4 2.709581 2.5986999

Melalukan prediksi dengan function predict()

# type raw
pred_NB_raw <- predict(object = model_NB, newdata = bc_clean_test, type="raw") # menghasilkan peluang

Melakukan prediksi dengan parameter type = "class"

#type class
bc_clean_test$pred.Label <- predict(object = model_NB, newdata = bc_clean_test, type="class") #  (default threshold 0.5)

Evaluasi model dengan confusion matrix:

library(caret)
# confusion matrix
confusionMatrix(data = bc_clean_test$pred.Label, reference=bc_clean_test$Class)
#> Confusion Matrix and Statistics
#> 
#>           Reference
#> Prediction   2   4
#>          2 100   1
#>          4   9  61
#>                                               
#>                Accuracy : 0.9415              
#>                  95% CI : (0.8951, 0.9716)    
#>     No Information Rate : 0.6374              
#>     P-Value [Acc > NIR] : < 0.0000000000000002
#>                                               
#>                   Kappa : 0.8769              
#>                                               
#>  Mcnemar's Test P-Value : 0.02686             
#>                                               
#>             Sensitivity : 0.9174              
#>             Specificity : 0.9839              
#>          Pos Pred Value : 0.9901              
#>          Neg Pred Value : 0.8714              
#>              Prevalence : 0.6374              
#>          Detection Rate : 0.5848              
#>    Detection Prevalence : 0.5906              
#>       Balanced Accuracy : 0.9507              
#>                                               
#>        'Positive' Class : 2                   
#> 

Kelas positive dalam model ini adalah yang bernilai 2, yaitu benign.

Metrics mana yang akan dijadikan acuan? Karena berkaitan dengan kondisi medis seseorang, kita ingin menurunkan false positive (dianggap benign padahal sesungguhnya malignant).Malignant adalah tumor ganas, sehingga seseorang yang diprediksi masuk kelas malignant sebaiknya melakukan pemeriksaan lebih lanjut.

Jika dilihat dari nilai Pos Pred Value atau Precisionnya, model_NB ini mempunyai performa yang baik yaitu dengan nilai precision 99.11%.

catatan: ditetapkan nilai precision yang diharapkan adalah minimal 98%

Decission Tree

Selanjutnya mari kita coba membuat model dengan metode decission tree.

Pruning and Tree-size

Kekurangan dari Decision Tree adalah kecenderungannya untuk overfitting. Hal ini terjadi karena Decision tree mampu membagi-bagi data hingga amat detail (bahkan hingga dalam leaf node hanya terdapat 1 observasi). Hal ini membuat decision tree justru menghafal pola di data train, dan membuat aturan yang terlalu kompleks, bukan mempelajari pola tersebut. Alhasil model menjadi kurang general untuk diaplikasikan ke data yang bukan data train, sehingga cenderung overfit.

Untuk mengatasinya, decision tree perlu tahu kapan ia berhenti membuat cabang sehingga pohon yang dihasilkan tidak terlalu kompleks. Pemotongan/pencegahan cabang pohon itu dinamakan Pruning, dimana kita mencegah Decision Tree untuk membuat cabang berdasarkan kriteria tertentu.

Parameter pruning:

  • mincriterion: Nilai 1-\(\alpha\). Saat mincriterion 0.95, P-value harus < 0.05 untuk suatu node dapat membuat cabang. (default: 0.95)

  • minsplit: Jumlah minimal observasi di tiap cabang setelah pemisahan. Bila tidak terpenuhi, tidak dilakukan percabangan. (default: 20)

  • minbucket: Jumlah minimal observasi di terminal node. Bila tidak terpenuhi, tidak dilakukan percabangan. (default: 7). Ketika nilai terminal masih diatas 7(secara default) tetapi jumlah observasi tidak memenuhi minimum angka yang dibutuhkan pada minsplit, maka percabangan akan dihentikan.

library(partykit)

model_DC <- ctree(formula = Class~., data = bc_train_balance,
                            control = ctree_control(mincriterion = 0.95, 
                                                    minsplit = 20,
                                                    minbucket = 7))
plot(model_DC, type='simple')

Evaluasi performa model_DC

# prediksi kelas di data test
pred_DC <- predict(model_DC, bc_clean_test, type="response")

# confusion matrix data test
confusionMatrix(pred_DC, bc_clean_test$Class, positive = "2")
#> Confusion Matrix and Statistics
#> 
#>           Reference
#> Prediction   2   4
#>          2 106   0
#>          4   3  62
#>                                              
#>                Accuracy : 0.9825             
#>                  95% CI : (0.9496, 0.9964)   
#>     No Information Rate : 0.6374             
#>     P-Value [Acc > NIR] : <0.0000000000000002
#>                                              
#>                   Kappa : 0.9624             
#>                                              
#>  Mcnemar's Test P-Value : 0.2482             
#>                                              
#>             Sensitivity : 0.9725             
#>             Specificity : 1.0000             
#>          Pos Pred Value : 1.0000             
#>          Neg Pred Value : 0.9538             
#>              Prevalence : 0.6374             
#>          Detection Rate : 0.6199             
#>    Detection Prevalence : 0.6199             
#>       Balanced Accuracy : 0.9862             
#>                                              
#>        'Positive' Class : 2                  
#> 

Nilai precision dari model ini sebesar 98.21%. Cukup baik dari target yang diharapkan.

Notes : Ketika model diujikan ke 2 data (train/test) dan menghasilkan performa yang masih kurang memuaskan, hal yang bisa dilakukan adalah: - Ketika data imbalance, bisa ditambahkan data agar proporsinya semakin seimbang dan model bisa belajar dengan lebih banyak data (sudah dilakukan) - Coba untuk melakukan tuning lain dengan metode pruning (mengatur mincriterion, minsplit, minbucket, atau parameter lain yang ada di DT)

KESIMPULAN

Performa kedua model yaitu model Naive Bayes dan model Decission Tree untuk memprediksi breast cancer cukup baik yaitu mempunyai nilai precision di atas 98%.

Model Naive Bayes mempunyai performa lebih tinggi yaitu dengan nilai precision 99.01%

Seandainya hasil performa model Naive Bayes dan Decission Tree dirasa masih kurang baik, bisa dipertimbangkan untuk menggunakan pendekatan lain dengan metode ensemble mehod, misalnya dengan Random Forest.