Pada kesempatan kali ini, saya akan mencoba melakukan prediksi terhadap pasien yang terkena kanker prostat, akan diprediksi apakah kankernya ganas atau jinak kategori dari beberapa variabel penunjangnya. Algoritma yang akan saya gunakan yaitu menggunakan logistik regression dan k-nearest neighbor yang termasuk dalam supervised learning.
Tujuan dalam analisis kali ini adalah akan memprediksi apakah kanker tersebut ganas atau jinak, dan kita ingin membuat model yang memfokuskan untuk mendeteksi kanker prostat yang ganas (Malignant).
library(dplyr)
library(GGally)
library(caret)
library(class)
library(rsample)Dataset yang akan saya gunakan yaitu data mengenai pasien yang terkena kanker prostat berdasarkan beberapa karakteristik yang menyertai yang dapat Anda unduh langsung pada Kaggle https://www.kaggle.com/datasets/sajidsaifi/prostate-cancer.
df <- read.csv("Prostate_Cancer.csv")
dfDari data yang ada, terdapat 10 kolom dan 100 baris data. Untuk kebutuhan analisa kita, kita akan membuang kolom yang dirasa tidak perlu untuk masuk ke analisis kita.
df <- df %>%
select(-id) %>%
mutate(diagnosis_result = factor(diagnosis_result,
levels = c("B", "M"),
labels = c("Benign", "Malignant")))
dfKita membuang kolom id, dan mengubah isi data di kolom diagnosis_result agar lebih informatif.
Pada tahap ini, kita akan mengeksplorasi data.
is.na(df) %>% colSums()## diagnosis_result radius texture perimeter
## 0 0 0 0
## area smoothness compactness symmetry
## 0 0 0 0
## fractal_dimension
## 0
glimpse(df)## Rows: 100
## Columns: 9
## $ diagnosis_result <fct> Malignant, Benign, Malignant, Malignant, Malignant, …
## $ radius <int> 23, 9, 21, 14, 9, 25, 16, 15, 19, 25, 24, 17, 14, 12…
## $ texture <int> 12, 13, 27, 16, 19, 25, 26, 18, 24, 11, 21, 15, 15, …
## $ perimeter <int> 151, 133, 130, 78, 135, 83, 120, 90, 88, 84, 103, 10…
## $ area <int> 954, 1326, 1203, 386, 1297, 477, 1040, 578, 520, 476…
## $ smoothness <dbl> 0.143, 0.143, 0.125, 0.070, 0.141, 0.128, 0.095, 0.1…
## $ compactness <dbl> 0.278, 0.079, 0.160, 0.284, 0.133, 0.170, 0.109, 0.1…
## $ symmetry <dbl> 0.242, 0.181, 0.207, 0.260, 0.181, 0.209, 0.179, 0.2…
## $ fractal_dimension <dbl> 0.079, 0.057, 0.060, 0.097, 0.059, 0.076, 0.057, 0.0…
Dapat dilihat, data yang kita gunakan sudah sesuai dengan tipe data yang seharusnya, dan tidak terdapat missing value.
Sebagai metode pertama, kita akan menggunakan metode Logistik Regression dalam memprediksi data kita.
Sebelum melakukan pemodelan, kita perlu melihat terlebih dahulu proporsi dari target variabel yang kita miliki pada kolom diagnosis_result.
prop.table(table(df$diagnosis_result))##
## Benign Malignant
## 0.38 0.62
table(df$diagnosis_result)##
## Benign Malignant
## 38 62
Kita melihat proporsi data yang ada, karena akan lebih baik jika data yang kita punya seimbang, dan data diatas bisa dibilang cukup seimbang untuk kita jadikan model.
Selanjutnya yaitu melakukan splitting data menjadi data train dan data test. Tujuannya yaitu pada data train akan kita gunakan untuk modeling/pelatihan, sedangkan data test akan kita gunakan sebagai penguji model yang sudah kita buat jika dihadapkan dengan unseen data (data baru). Selain itu hal ini dapat digunakan untuk melihat kemampuan model yang kita buat dalam menghadapi unseen data.
set.seed(100) # merujuk pada key untuk proses CV knn
index <- initial_split(data=df, # data awal sebelum split
prop = 0.7, #proporsi split 80:20
strata = diagnosis_result) #label kelas agar pembagian train dan test antara kelas positif dan negatif sama
df_train <- training(index)
df_test <- testing(index)prop.table(table(df_train$diagnosis_result))##
## Benign Malignant
## 0.3768116 0.6231884
prop.table(table(df_test$diagnosis_result))##
## Benign Malignant
## 0.3870968 0.6129032
Untuk tahap ini, kita melakukan pemodelan dengan menggunakan regresi logistik. Pemodelan menggunakan fungsi glm() dalam memodelkan menggunakan regresi logistik. Variabel yang digunakan adalah beberapa variabel yang kita anggap mempengaruhi target variabel, dimana variabel target menjadi variabel responnya.
ggcorr(data = df, hjust = 1, layout.exp = 3, label = T)## Warning in ggcorr(data = df, hjust = 1, layout.exp = 3, label = T): data in
## column(s) 'diagnosis_result' are not numeric and were ignored
# model_base <- glm(formula = diagnosis_result ~ perimeter+compactness+symmetry+fractal_dimension, family = "binomial",
# data = df_train)
# summary(model_base)model <- glm(formula = diagnosis_result ~., family = "binomial",
data = df_train)
summary(model)##
## Call:
## glm(formula = diagnosis_result ~ ., family = "binomial", data = df_train)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -3.2556 -0.4329 0.1513 0.5666 1.5915
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 5.045e+00 1.155e+01 0.437 0.662
## radius -9.467e-03 7.935e-02 -0.119 0.905
## texture 8.604e-02 7.817e-02 1.101 0.271
## perimeter -7.358e-02 1.425e-01 -0.516 0.606
## area 1.042e-02 1.119e-02 0.931 0.352
## smoothness -1.436e+00 2.869e+01 -0.050 0.960
## compactness 3.872e+01 2.444e+01 1.585 0.113
## symmetry -9.697e+00 2.309e+01 -0.420 0.675
## fractal_dimension -1.276e+02 1.286e+02 -0.993 0.321
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 91.422 on 68 degrees of freedom
## Residual deviance: 48.940 on 60 degrees of freedom
## AIC: 66.94
##
## Number of Fisher Scoring iterations: 6
Jika dilihat dari model yang sudah dibuat, terlihat bahwa prediktornya tidak ada yang signifikan terhadap target. Maka dari itu kita akan mencoba menggunakan Step-Wise Regression.
step(model, direction = "backward", trace = FALSE)##
## Call: glm(formula = diagnosis_result ~ area + compactness, family = "binomial",
## data = df_train)
##
## Coefficients:
## (Intercept) area compactness
## -6.325084 0.008187 15.659154
##
## Degrees of Freedom: 68 Total (i.e. Null); 66 Residual
## Null Deviance: 91.42
## Residual Deviance: 51.6 AIC: 57.6
model2 <- glm(formula = diagnosis_result ~ area + compactness, family = "binomial",
data = df_train)
summary(model2)##
## Call:
## glm(formula = diagnosis_result ~ area + compactness, family = "binomial",
## data = df_train)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -3.6668 -0.5203 0.1332 0.5924 1.7901
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -6.325084 1.659989 -3.810 0.000139 ***
## area 0.008187 0.002393 3.421 0.000625 ***
## compactness 15.659154 7.034049 2.226 0.026001 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 91.422 on 68 degrees of freedom
## Residual deviance: 51.597 on 66 degrees of freedom
## AIC: 57.597
##
## Number of Fisher Scoring iterations: 6
Dengan menggunakan step “backward”, kita mendapatkan model yang lebih baik dari sebelumnya. Terlihat bahwa terdapat 2 prediktor yang signifikan, yaitu area dan compactness.
Pada tahap ini kita akan melakukan prediksi pada data df_test/unseen data, menggunakan model yang sudah kita training sebelumnya.
df_test$pred_diagnosis <- predict(object = model2, newdata = df_test , type = "response")
df_testdf_test$pred_label <- ifelse(df_test$pred_diagnosis>0.5 , "Malignant" , "Benign") %>% as.factor()
df_testdf_test %>% select(diagnosis_result,pred_diagnosis,pred_label)Setelah membuat model, kita tidak akan langsung menggunakannya, akan tetapi kita evaluasi model tersebut terlebih dahulu, untuk mengukur apakah model tersebut layak kita gunakan atau tidak.
table(predicted = df_test$pred_label, actual = df_test$diagnosis_result)## actual
## predicted Benign Malignant
## Benign 11 3
## Malignant 1 16
confusionMatrix(data = df_test$pred_label, reference = df_test$diagnosis_result, positive="Malignant")## Confusion Matrix and Statistics
##
## Reference
## Prediction Benign Malignant
## Benign 11 3
## Malignant 1 16
##
## Accuracy : 0.871
## 95% CI : (0.7017, 0.9637)
## No Information Rate : 0.6129
## P-Value [Acc > NIR] : 0.001628
##
## Kappa : 0.7362
##
## Mcnemar's Test P-Value : 0.617075
##
## Sensitivity : 0.8421
## Specificity : 0.9167
## Pos Pred Value : 0.9412
## Neg Pred Value : 0.7857
## Prevalence : 0.6129
## Detection Rate : 0.5161
## Detection Prevalence : 0.5484
## Balanced Accuracy : 0.8794
##
## 'Positive' Class : Malignant
##
Selanjutnya, kita akan menggunakan metode K-NN (K-Nearest Neighbour) dalam memprediksi data kita.
Dalam tahap ini, kita langsung saja membagi data menjadi data train dan data testing, karena Data Cleaning sudah dilakukan diatas.
set.seed(100) # merujuk pada key untuk proses CV knn
index <- initial_split(data=df, # data awal sebelum split
prop = 0.7, #proporsi split 80:20
strata = diagnosis_result) #label kelas agar pembagian train dan test antara kelas positif dan negatif sama
train <- training(index)
test <- testing(index)prop.table(table(train$diagnosis_result))##
## Benign Malignant
## 0.3768116 0.6231884
prop.table(table(test$diagnosis_result))##
## Benign Malignant
## 0.3870968 0.6129032
Proporsi data terlihat cukup seimbang, dan dapat kita gunakan untuk modelling.
# prediktor data train
train_x <- train %>% select_if(is.numeric) # dipilih semua kolom yang numerik karena akan discaling
# target data train
train_y <- train %>% select(diagnosis_result) # dipisahkan khusus untuk kelas target
# prediktor data test
test_x <- test %>% select_if(is.numeric)
# target data test
test_y <- test %>% select(diagnosis_result)# code ini hanya boleh dirun 1 kali
train_x <- scale(train_x)
test_x <- scale(test_x,
center=attr(train_x, "scaled:center"), #nilai rata-rata train
scale=attr(train_x, "scaled:scale")) # nilai sd trainSebagai tambahan, dalam tahap diatas, kita melakukan scaling terhadap data kita, atau bisa dibilang, kita menyamakan ukuran dari data kita, sehingga model yang dihasilkan oleh K-NN bisa maksimal.
Untuk memilih K-nya, kita melihat dari akar dari jumlah baris dari data kita.
sqrt(nrow(train_x))## [1] 8.306624
Angka yang didapatkan kemudian kita bulatkan jadi 9, karena data kita genap (100 baris), maka K-nya harus ganjil, agar model K-NN dapat menitik beratkan di salah satu data kita (Malignant atau Benign) sehingga data baru / unseen data, dapat diklasifikasikan ke data yang ada di titik beratnya. (tidak boleh seimbang)
df_pred <- knn(train = train_x, #prediktor data train
test = test_x, #prediktor data test
cl = train_y$diagnosis_result, #target data train
k=9) # jumlah k yang digunakan untuk klasifikasi# K-NN
confusionMatrix(data=df_pred, reference=test_y$diagnosis_result, positive="Malignant")## Confusion Matrix and Statistics
##
## Reference
## Prediction Benign Malignant
## Benign 10 1
## Malignant 2 18
##
## Accuracy : 0.9032
## 95% CI : (0.7425, 0.9796)
## No Information Rate : 0.6129
## P-Value [Acc > NIR] : 0.0003434
##
## Kappa : 0.7929
##
## Mcnemar's Test P-Value : 1.0000000
##
## Sensitivity : 0.9474
## Specificity : 0.8333
## Pos Pred Value : 0.9000
## Neg Pred Value : 0.9091
## Prevalence : 0.6129
## Detection Rate : 0.5806
## Detection Prevalence : 0.6452
## Balanced Accuracy : 0.8904
##
## 'Positive' Class : Malignant
##
# LOGISTIC REGRESSION
confusionMatrix(data = df_test$pred_label, reference = df_test$diagnosis_result, positive="Malignant")## Confusion Matrix and Statistics
##
## Reference
## Prediction Benign Malignant
## Benign 11 3
## Malignant 1 16
##
## Accuracy : 0.871
## 95% CI : (0.7017, 0.9637)
## No Information Rate : 0.6129
## P-Value [Acc > NIR] : 0.001628
##
## Kappa : 0.7362
##
## Mcnemar's Test P-Value : 0.617075
##
## Sensitivity : 0.8421
## Specificity : 0.9167
## Pos Pred Value : 0.9412
## Neg Pred Value : 0.7857
## Prevalence : 0.6129
## Detection Rate : 0.5161
## Detection Prevalence : 0.5484
## Balanced Accuracy : 0.8794
##
## 'Positive' Class : Malignant
##
Berdasarkan 2 model yang sudah kita buat, kurang lebih model hampir sama baiknya. Akan tetapi dalam pemilihan model machine learning, kita juga harus menyesuaikan dengan kebutuhan/tujuan kita di awal.
Tujuan kita yaitu ingin memfokuskan untuk mendeteksi kanker prostat yang ganas (Malignant).
Setelah dilakukan prediksi menggunakan model, masih ada saja prediksi yang salah. Pada klasifikasi, kita mengevaluasi model berdasarkan confusion matrix:
knitr::include_graphics("img/tnfp.PNG")positifnegatifKita ingin mengurangi prediksi seseorang terkena kanker prostat Jinak (prediksi kelas Benign), namun aktualnya orang tersebut terkena kanker prostat yang Ganas (aktual kelas Malignant).
FN : ketika kita prediksi seseorang kankernya jinak, namun aslinya kankernya ganas
FP : ketika kita prediksi seseorang kankernya ganas, namun aslinya kankernya jinak
recall: ketika kita ingin meminimalisir FN Precision : ketika kita ingin meminimalisir FP
Untuk kasus ini kita akan mengurangi FN atau FP, agar pengembangan model kedepannya lebih baik juga. Kita ingin mengurangi kesalahan model dalam memprediksi kanker ganas, maka dari itu, kita akan mengurangi FN, agar dapat dilakukan pemeriksaan lanjutan, dan juga guna untuk mengurangi resiko orang tersebut kankernya semakin parah.
Maka dari itu, kita dapat memilih model dengan metode K-NN, karena Sensitivity/ Recall lebih baik dibandingkan dengan metode Logistic Regression.