1 Overview

Dataset yang akan digunakan adalah data klasifikasi Jamur berdasarkan jenis tidak beracun (edible) atau beracun (poisonous) dari 23 species gilled mushrooms dalam family Agaricus and Lepiota yang didapatkan dari The Audubon Society Field Guide to North American Mushrooms (1981).

Pada kesempatan kali ini, menggunakan informasi yang terdapat pada data mushroom akan dilakukan prediksi jenis jamur menggunakan pemodelan naive bayes, desicion tree dan random forest.

2 Load Data

jamur <- read.csv("mushrooms.csv", stringsAsFactors = T)
head(jamur)

Pada data jamur diatas terdapat 1 kolom target (class) dan 22 kolom prediktor yang merupakan informasi karakteristik dari jamur dengan informasi sebagai berikut :

class : class jamur edible =e, poisonous = p

cap.shape : bentuk cap jamur bell=b,conical=c,convex=x,flat=f, knobbed=k,sunken=s

cap.surface : permukaan cap fibrous=f,grooves=g,scaly=y,smooth=s

cap.color : warna cap brown=n,buff=b,cinnamon=c,gray=g,green=r,pink=p,purple=u,red=e,white=w,yellow=y

bruises : bruises=t,no=f

odor : almond=a,anise=l,creosote=c,fishy=y,foul=f,musty=m,none=n,pungent=p,spicy=s

gill.attachment : penempatan gill attached=a, descending=d, free=f, notched=n

gill.spacing : jarak gill close=c,crowded=w,distant=d

gill.size : ukuran gill broad=b,narrow=n

gill.color : warna gill black=k,brown=n,buff=b,chocolate=h,gray=g, green=r,orange=o,pink=p,purple=u,red=e,white=w,yellow=y

stalk.shape : bentuk stalk enlarging=e,tapering=t

stalk.root : akar stalk bulbous=b,club=c,cup=u,equal=e,rhizomorphs=z,rooted=r,missing=?

stalk.surface.above.ring : fibrous=f,scaly=y,silky=k,smooth=s

stalk.surface.below.ring : fibrous=f,scaly=y,silky=k,smooth=s

stalk.color.above.ring : brown=n,buff=b,cinnamon=c,gray=g,orange=o,pink=p,red=e,white=w,yellow=y

stalk-color-below-ring: brown=n,buff=b,cinnamon=c,gray=g,orange=o,pink=p,red=e,white=w,yellow=y

veil-type: partial=p,universal=u

veil-color: brown=n,orange=o,white=w,yellow=y

ring-number: none=n,one=o,two=t

ring-type: cobwebby=c,evanescent=e,flaring=f,large=l,none=n,pendant=p,sheathing=s,zone=z

spore-print-color: black=k,brown=n,buff=b,chocolate=h,green=r,orange=o,purple=u,white=w,yellow=y

population: abundant=a,clustered=c,numerous=n,scattered=s,several=v,solitary=y

habitat: grasses=g,leaves=l,meadows=m,paths=p,urban=u,waste=w,woods=d

3 Data Wrangling and Analysis

3.1 Cek Type Data

Cek type data yang masih belum sesuai

str(jamur)
#> 'data.frame':    8124 obs. of  23 variables:
#>  $ class                   : Factor w/ 2 levels "e","p": 2 1 1 2 1 1 1 1 2 1 ...
#>  $ cap.shape               : Factor w/ 6 levels "b","c","f","k",..: 6 6 1 6 6 6 1 1 6 1 ...
#>  $ cap.surface             : Factor w/ 4 levels "f","g","s","y": 3 3 3 4 3 4 3 4 4 3 ...
#>  $ cap.color               : Factor w/ 10 levels "b","c","e","g",..: 5 10 9 9 4 10 9 9 9 10 ...
#>  $ bruises                 : Factor w/ 2 levels "f","t": 2 2 2 2 1 2 2 2 2 2 ...
#>  $ odor                    : Factor w/ 9 levels "a","c","f","l",..: 7 1 4 7 6 1 1 4 7 1 ...
#>  $ gill.attachment         : Factor w/ 2 levels "a","f": 2 2 2 2 2 2 2 2 2 2 ...
#>  $ gill.spacing            : Factor w/ 2 levels "c","w": 1 1 1 1 2 1 1 1 1 1 ...
#>  $ gill.size               : Factor w/ 2 levels "b","n": 2 1 1 2 1 1 1 1 2 1 ...
#>  $ gill.color              : Factor w/ 12 levels "b","e","g","h",..: 5 5 6 6 5 6 3 6 8 3 ...
#>  $ stalk.shape             : Factor w/ 2 levels "e","t": 1 1 1 1 2 1 1 1 1 1 ...
#>  $ stalk.root              : Factor w/ 5 levels "?","b","c","e",..: 4 3 3 4 4 3 3 3 4 3 ...
#>  $ stalk.surface.above.ring: Factor w/ 4 levels "f","k","s","y": 3 3 3 3 3 3 3 3 3 3 ...
#>  $ stalk.surface.below.ring: Factor w/ 4 levels "f","k","s","y": 3 3 3 3 3 3 3 3 3 3 ...
#>  $ stalk.color.above.ring  : Factor w/ 9 levels "b","c","e","g",..: 8 8 8 8 8 8 8 8 8 8 ...
#>  $ stalk.color.below.ring  : Factor w/ 9 levels "b","c","e","g",..: 8 8 8 8 8 8 8 8 8 8 ...
#>  $ veil.type               : Factor w/ 1 level "p": 1 1 1 1 1 1 1 1 1 1 ...
#>  $ veil.color              : Factor w/ 4 levels "n","o","w","y": 3 3 3 3 3 3 3 3 3 3 ...
#>  $ ring.number             : Factor w/ 3 levels "n","o","t": 2 2 2 2 2 2 2 2 2 2 ...
#>  $ ring.type               : Factor w/ 5 levels "e","f","l","n",..: 5 5 5 5 1 5 5 5 5 5 ...
#>  $ spore.print.color       : Factor w/ 9 levels "b","h","k","n",..: 3 4 4 3 4 3 3 4 3 3 ...
#>  $ population              : Factor w/ 6 levels "a","c","n","s",..: 4 3 3 4 1 3 3 4 5 4 ...
#>  $ habitat                 : Factor w/ 7 levels "d","g","l","m",..: 6 2 4 6 2 2 4 4 2 4 ...

Semua type data sudah sesuai yaitu factor dikarenakan sudah dilakukan perubahan pada saat read data sebelumnya menggunakan formula stringAsFactors

3.2 Cek Missing Value

colSums(is.na(jamur))
#>                    class                cap.shape              cap.surface 
#>                        0                        0                        0 
#>                cap.color                  bruises                     odor 
#>                        0                        0                        0 
#>          gill.attachment             gill.spacing                gill.size 
#>                        0                        0                        0 
#>               gill.color              stalk.shape               stalk.root 
#>                        0                        0                        0 
#> stalk.surface.above.ring stalk.surface.below.ring   stalk.color.above.ring 
#>                        0                        0                        0 
#>   stalk.color.below.ring                veil.type               veil.color 
#>                        0                        0                        0 
#>              ring.number                ring.type        spore.print.color 
#>                        0                        0                        0 
#>               population                  habitat 
#>                        0                        0

Tidak terdapat missing value pada setiap kolom

3.3 Cek Informasi pada data

summary(jamur)
#>  class    cap.shape cap.surface   cap.color    bruises       odor     
#>  e:4208   b: 452    f:2320      n      :2284   f:4748   n      :3528  
#>  p:3916   c:   4    g:   4      g      :1840   t:3376   f      :2160  
#>           f:3152    s:2556      e      :1500            s      : 576  
#>           k: 828    y:3244      y      :1072            y      : 576  
#>           s:  32                w      :1040            a      : 400  
#>           x:3656                b      : 168            l      : 400  
#>                                 (Other): 220            (Other): 484  
#>  gill.attachment gill.spacing gill.size   gill.color   stalk.shape stalk.root
#>  a: 210          c:6812       b:5612    b      :1728   e:3516      ?:2480    
#>  f:7914          w:1312       n:2512    p      :1492   t:4608      b:3776    
#>                                         w      :1202               c: 556    
#>                                         n      :1048               e:1120    
#>                                         g      : 752               r: 192    
#>                                         h      : 732                         
#>                                         (Other):1170                         
#>  stalk.surface.above.ring stalk.surface.below.ring stalk.color.above.ring
#>  f: 552                   f: 600                   w      :4464          
#>  k:2372                   k:2304                   p      :1872          
#>  s:5176                   s:4936                   g      : 576          
#>  y:  24                   y: 284                   n      : 448          
#>                                                    b      : 432          
#>                                                    o      : 192          
#>                                                    (Other): 140          
#>  stalk.color.below.ring veil.type veil.color ring.number ring.type
#>  w      :4384           p:8124    n:  96     n:  36      e:2776   
#>  p      :1872                     o:  96     o:7488      f:  48   
#>  g      : 576                     w:7924     t: 600      l:1296   
#>  n      : 512                     y:   8                 n:  36   
#>  b      : 432                                            p:3968   
#>  o      : 192                                                     
#>  (Other): 156                                                     
#>  spore.print.color population habitat 
#>  w      :2388      a: 384     d:3148  
#>  n      :1968      c: 340     g:2148  
#>  k      :1872      n: 400     l: 832  
#>  h      :1632      s:1248     m: 292  
#>  r      :  72      v:4040     p:1144  
#>  b      :  48      y:1712     u: 368  
#>  (Other): 144                 w: 192

dapat dilihat pada colom veil.type hanya terdapat satu kategori saja, maka sebaiknya kolom tersebut di keluarkan dari data set yang ada

jamur <- jamur %>% select(-veil.type)

3.4 Cek Class Imbalance

table(jamur$class) %>% prop.table()
#> 
#>         e         p 
#> 0.5179714 0.4820286

Kelas target dari data dapat dikatakan seimbang antara edible dan poisonous

4 Cross Validation

Dilakukan pemisahan data train dengan test untuk mengevaluasi model dan melihat kemampuannya memprediksi data baru

RNGkind(sample.kind = "Rounding")
set.seed(123)

# index sampling 
# ---- mengambil 80 persen index dari jumlah data -----
index <- sample(x = nrow(jamur),size = 0.8*nrow(jamur)) 


# splitting data 
train <- jamur[index,] #ambil data 80%
test  <- jamur[-index,]  #ambil data 20%
# re-check class imbalance
table(train$class) %>% prop.table() %>% round(digits = 2)
#> 
#>    e    p 
#> 0.52 0.48

5 Model Naive Bayes

model_bayes <- naiveBayes(class~., data = jamur, laplace = 1)

predict_bayes <- predict(model_bayes, test)

confusionMatrix(predict_bayes, 
                test$class, 
                positive = "p")
#> Confusion Matrix and Statistics
#> 
#>           Reference
#> Prediction   e   p
#>          e 832  58
#>          p   7 728
#>                                                
#>                Accuracy : 0.96                 
#>                  95% CI : (0.9493, 0.969)      
#>     No Information Rate : 0.5163               
#>     P-Value [Acc > NIR] : < 0.00000000000000022
#>                                                
#>                   Kappa : 0.9198               
#>                                                
#>  Mcnemar's Test P-Value : 0.0000000005584      
#>                                                
#>             Sensitivity : 0.9262               
#>             Specificity : 0.9917               
#>          Pos Pred Value : 0.9905               
#>          Neg Pred Value : 0.9348               
#>              Prevalence : 0.4837               
#>          Detection Rate : 0.4480               
#>    Detection Prevalence : 0.4523               
#>       Balanced Accuracy : 0.9589               
#>                                                
#>        'Positive' Class : p                    
#> 

Hasil confusionmatrix menunjukkan bahwa klasifikasi Naive Bayes memperkirakan 832 jamur tidak beracun (edible) dengan benar dan 7 prediksi salah, sedangkan model memprediksi 728 jamur beracun (poisonous) dengan benar dan 58 prediksi salah. Tingkat akurasi dari model ini adalah sebesar 96% dan sensitivity (recall) sebesar 93%.

6 Decision Tree

Model selanjutnya yang akan dibuat adalah decision tree

model_dt <- ctree(formula = class ~ .,
                   data = jamur)

plot(model_dt, type = "simple")

Kita bisa melihat tingkat pertama adalah odor yang merupakan prediktor yang paling mempengaruhi klasifikasi dari jamur

# prediksi kelas di data test
pred_dt <- predict(object = model_dt, 
                          newdata = test) 
# confusion matrix
confusionMatrix(data = pred_dt,
                reference = test$class,
                positive = "p")
#> Confusion Matrix and Statistics
#> 
#>           Reference
#> Prediction   e   p
#>          e 839   0
#>          p   0 786
#>                                                
#>                Accuracy : 1                    
#>                  95% CI : (0.9977, 1)          
#>     No Information Rate : 0.5163               
#>     P-Value [Acc > NIR] : < 0.00000000000000022
#>                                                
#>                   Kappa : 1                    
#>                                                
#>  Mcnemar's Test P-Value : NA                   
#>                                                
#>             Sensitivity : 1.0000               
#>             Specificity : 1.0000               
#>          Pos Pred Value : 1.0000               
#>          Neg Pred Value : 1.0000               
#>              Prevalence : 0.4837               
#>          Detection Rate : 0.4837               
#>    Detection Prevalence : 0.4837               
#>       Balanced Accuracy : 1.0000               
#>                                                
#>        'Positive' Class : p                    
#> 

Hasil model decision tree memiliki sensitivity dan akurasi sebesar 100%, dimana tidak terdapat error atas semua prediksi yang dilakukan terhadap data test.

7 Random Forest

Tahap awal dalam membuat model random forest kita akan mereduksi prediktor yang memiliki nilai seragam (low variance)

n0_var <- nearZeroVar(jamur)
  
jamur_var <- jamur[,-n0_var] 

proses cross validation

RNGkind(sample.kind = "Rounding")
set.seed(123)

index_var <- sample(nrow(jamur_var), nrow(jamur_var)*0.8)
var_train <- jamur_var[index_var, ]
var_test <- jamur_var[-index_var, ]
set.seed(123)

ctrl <- trainControl(method = "repeatedcv",
                      number = 5, # k-fold
                      repeats = 3) # repetisi
 
jamur_forest <- train(class ~ .,
                    data = var_train,
                    method = "rf", # random forest
                    trControl = ctrl)
 
saveRDS(jamur_forest, "jamur_forest.RDS") # simpan model
model_rf <- readRDS("jamur_forest.RDS")

Dari hasil RF, mtry paling bagus adalah mtry = 46, nilai akurasi yang didapatkan adalah sebesar 100%

7.1 Cek Out-of-Bag (OOB) Error

kita akan melakukan pengecekan OOB error yang merupakan persentase data OOB yang misklasifikasi

model_rf$finalModel
#> 
#> Call:
#>  randomForest(x = x, y = y, mtry = param$mtry) 
#>                Type of random forest: classification
#>                      Number of trees: 500
#> No. of variables tried at each split: 46
#> 
#>         OOB estimate of  error rate: 0%
#> Confusion matrix:
#>      e    p class.error
#> e 3369    0           0
#> p    0 3130           0

Nilai OOB Error pada model model_rf sebesar 0%. Dengan kata lain, akurasi model pada data OOB adalah 100%!

pred_rf <- predict(model_rf, 
                   var_test)
confusionMatrix(data = pred_rf,
                reference = var_test$class)
#> Confusion Matrix and Statistics
#> 
#>           Reference
#> Prediction   e   p
#>          e 839   0
#>          p   0 786
#>                                                
#>                Accuracy : 1                    
#>                  95% CI : (0.9977, 1)          
#>     No Information Rate : 0.5163               
#>     P-Value [Acc > NIR] : < 0.00000000000000022
#>                                                
#>                   Kappa : 1                    
#>                                                
#>  Mcnemar's Test P-Value : NA                   
#>                                                
#>             Sensitivity : 1.0000               
#>             Specificity : 1.0000               
#>          Pos Pred Value : 1.0000               
#>          Neg Pred Value : 1.0000               
#>              Prevalence : 0.5163               
#>          Detection Rate : 0.5163               
#>    Detection Prevalence : 0.5163               
#>       Balanced Accuracy : 1.0000               
#>                                                
#>        'Positive' Class : e                    
#> 

Performa model random forest yaitu memiliki sensitivity (recall) dan akurasi sebesar 100%!

Selanjutnya, mari kita lihat prediktor penting yang paling sering digunakan dalam model random forest

varImp(model_rf)
#> rf variable importance
#> 
#>   only 20 most important variables shown (out of 91)
#> 
#>                           Overall
#> odorn                     100.000
#> odorf                      32.620
#> gill.sizen                 30.678
#> stalk.rootc                16.846
#> stalk.surface.above.ringk   9.497
#> bruisest                    9.418
#> spore.print.colorr          7.741
#> stalk.surface.below.ringk   5.823
#> stalk.surface.below.ringy   5.193
#> stalk.rootr                 4.931
#> odorl                       4.723
#> spore.print.colorh          4.074
#> ring.typep                  3.530
#> gill.spacingw               3.478
#> spore.print.colorw          3.110
#> odorp                       2.350
#> cap.colory                  2.213
#> ring.numbert                2.207
#> populationv                 1.649
#> stalk.shapet                1.609
plot(varImp(model_rf))

Prediktor yang paling penting dalam menentukan klasifikasi jamur adalah odorn atau jamur yang tidak memiliki bau

8 Conclusion

  • Berdasarkan tingkat akurasi dan sensitivity masing-masing model mendapatkan nilai sebagai berikut :
  1. Model Naive Bayes : Sensitivity 93% dan Akurasi 96%

  2. Model Decision Tree : Sensitivity 100% dan Akurasi 100%

  3. Model Random Forest : Sensitivity 100% dan Akurasi 100%

  • Recall (sensitivity) lebih dititikberatkan karena ingin mengurangi prediksi false negatif dalam hal ini diprediksi `jamur tidak beracun namun sebenarnya jamur beracun

  • Model decision tree dan random forest memiliki performa paling baik pada data ini dimana menghasilkan tingkat akurasi 100%, namun hal tersebut sebagai catatan dalam pemodelan berisiko menjadi model yang overfitting.

  • Beberapa variable seperti odor (bau jamur), spore color (warna spora), stalk color (warna batang), cap surface sangat mempengaruhi klasifikasi jamur tersebut beracun atau tidak.