1. Brain tumor dataset

This dataset contains simulated data for brain tumor diagnosis, treatment, and patient details. It consists of 19 columns and 20,000 rows, providing information such as patient demographics, tumor characteristics, symptoms, treatment details, and follow-up requirements. The dataset is designed for machine learning projects focused on predicting the type and severity of brain tumors, as well as understanding various treatment methods and patient outcomes.

Columns:

2. Goal of the project

The goal of the project is to amake predictions about the tumor diagnosis -benign or malignant.

Class of the diagnosis:

Benign tumor: It does not invade nearby tissue or spread to other parts of the body the way cancer can.

Malignant tumor: It is a group of diseased cells defined by one of three characteristics: uncontrolled growth, invasion and damage of healthy cells, or metastasizing (spreading) to other organs of the body.

#packages
#Tidyverse for map_int()
if(!require(tidyverse)) install.packages("tidyverse", repos = "http://cran.us.r-project.org")
#corrplot package for corrplot()
if(!require(corrplot)) install.packages("corrplot", repos = "http://cran.us.r-project.org")
#caret package for find.Correlation()
if(!require(caret)) install.packages("caret", repos = "http://cran.us.r-project.org")
#randomForest package for randomForest()
if(!require(randomForest)) install.packages("randomForest", repos = "http://cran.us.r-project.org")
#kernlab package for SVM
if(!require(kernlab)) install.packages("kernlab", repos = "http://cran.us.r-project.org")

3. Analysis of the Dataset

bc<-read.csv("brain_tumor_dataset.csv", header=T, sep=",")
str(bc)
## 'data.frame':    20000 obs. of  19 variables:
##  $ Patient_ID         : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Age                : int  73 26 31 29 54 27 72 61 50 54 ...
##  $ Gender             : chr  "Male" "Male" "Male" "Male" ...
##  $ Tumor_Type         : chr  "Malignant" "Benign" "Benign" "Malignant" ...
##  $ Tumor_Size         : num  5.38 4.85 5.59 1.44 2.42 ...
##  $ Location           : chr  "Temporal" "Parietal" "Parietal" "Temporal" ...
##  $ Histology          : chr  "Astrocytoma" "Glioblastoma" "Meningioma" "Medulloblastoma" ...
##  $ Stage              : chr  "III" "II" "I" "IV" ...
##  $ Symptom_1          : chr  "Vision Issues" "Headache" "Vision Issues" "Vision Issues" ...
##  $ Symptom_2          : chr  "Seizures" "Headache" "Headache" "Seizures" ...
##  $ Symptom_3          : chr  "Seizures" "Nausea" "Seizures" "Headache" ...
##  $ Radiation_Treatment: chr  "No" "Yes" "No" "Yes" ...
##  $ Surgery_Performed  : chr  "No" "Yes" "No" "No" ...
##  $ Chemotherapy       : chr  "No" "Yes" "No" "Yes" ...
##  $ Survival_Rate      : num  51.3 46.4 47.1 51.9 54.7 ...
##  $ Tumor_Growth_Rate  : num  0.112 2.166 1.884 1.283 2.069 ...
##  $ Family_History     : chr  "No" "Yes" "No" "Yes" ...
##  $ MRI_Result         : chr  "Positive" "Positive" "Negative" "Negative" ...
##  $ Follow_Up_Required : chr  "Yes" "Yes" "No" "No" ...

We have 20000 observations of 19 variables. So we can understand by our bc dataset that we have 20000 patients and 19 characteristics/features per patient. So, we can imagine a matrix of 20000 rows and 19 columns. This matrix has 380000 elements.

Let’s check if these id numbers are unique.

ifelse (n_distinct(bc$Patient_ID) == 20000, "Id numbers are unique.", "Id numbers are not unique.")
## [1] "Id numbers are unique."

The id numbers are unique, so there are no double id features in our data set.

Let’s check if we have missing values in our data set.

map_int(bc, function(.x) sum(is.na(.x)))
##          Patient_ID                 Age              Gender          Tumor_Type 
##                   0                   0                   0                   0 
##          Tumor_Size            Location           Histology               Stage 
##                   0                   0                   0                   0 
##           Symptom_1           Symptom_2           Symptom_3 Radiation_Treatment 
##                   0                   0                   0                   0 
##   Surgery_Performed        Chemotherapy       Survival_Rate   Tumor_Growth_Rate 
##                   0                   0                   0                   0 
##      Family_History          MRI_Result  Follow_Up_Required 
##                   0                   0                   0

There are no missing values. So, we do not need to transform our data set.

3.1 Class Imbalance

Let’s see if our classification data set is imbalanced. Classes that make up small proportions of the data set are called minority classes. Let’s calculate our proportions of our classes.

round(prop.table(table(bc$Tumor_Type)), 2)
## 
##    Benign Malignant 
##       0.5       0.5

The proportion of negative and positive labels are equal, so there is no impalanced dataset.

3.2 Multicollinearity

Let’s check if there is multicollinearity on our data set. Multicollinearity exists when two or more of the predictors in a regression model are moderately or highly correlated with one another. Then, we will remove the highly correlated predictors (if any).

bc_corr <- cor(bc%>%select(Age, Tumor_Size, Survival_Rate,Tumor_Growth_Rate))
bc_corr
##                            Age   Tumor_Size Survival_Rate Tumor_Growth_Rate
## Age                1.000000000 -0.014385485  -0.014825024      -0.009852592
## Tumor_Size        -0.014385485  1.000000000   0.003294837       0.004346803
## Survival_Rate     -0.014825024  0.003294837   1.000000000      -0.006824939
## Tumor_Growth_Rate -0.009852592  0.004346803  -0.006824939       1.000000000
corrplot::corrplot(bc_corr)

There is no multicollinearity in our dataset.

3.3 Transform our data set - Create training and testing set

Let’s transform our data set and create a training and a testing set. Our testing set will be 20% of our data set.

bc1<-bc%>%select(Age, Tumor_Size, Survival_Rate,Tumor_Growth_Rate)
bc2<-bc1%>%cbind(diagnosis=as.factor(bc$Tumor_Type))
set.seed(1570)
bc_index <- createDataPartition(bc2$diagnosis, times = 1, p = 0.8, list = FALSE)
bc_training <- bc2[bc_index, ]
bc_testing <-  bc2[-bc_index, ]
bc_control <- trainControl(method="cv",
                           number = 10,
                           classProbs = TRUE,
                           summaryFunction = twoClassSummary)

4. Models

4.1 Model 1 - Logistic Regression

model1_lr <- train(diagnosis ~., data = bc_training, method = "glm", 
                         metric = "ROC", preProcess = c("scale", "center"), 
                         trControl = bc_control)
prediction_lr <- predict(model1_lr, bc_testing)
con_mat_lr <- confusionMatrix(prediction_lr, bc_testing$diagnosis, positive = "Malignant")
con_mat_lr
## Confusion Matrix and Statistics
## 
##            Reference
## Prediction  Benign Malignant
##   Benign       873       871
##   Malignant   1121      1135
##                                           
##                Accuracy : 0.502           
##                  95% CI : (0.4864, 0.5176)
##     No Information Rate : 0.5015          
##     P-Value [Acc > NIR] : 0.4811          
##                                           
##                   Kappa : 0.0036          
##                                           
##  Mcnemar's Test P-Value : 2.419e-08       
##                                           
##             Sensitivity : 0.5658          
##             Specificity : 0.4378          
##          Pos Pred Value : 0.5031          
##          Neg Pred Value : 0.5006          
##              Prevalence : 0.5015          
##          Detection Rate : 0.2838          
##    Detection Prevalence : 0.5640          
##       Balanced Accuracy : 0.5018          
##                                           
##        'Positive' Class : Malignant       
## 

4.2 Model 2 - Random Forest

model2_rf <- train(diagnosis~.,
                  bc_training,
                  method="rf",
                  metric="ROC",
                  trControl=bc_control)
pred_rf <- predict(model2_rf, bc_testing)
con_mat_rf <- confusionMatrix(pred_rf, bc_testing$diagnosis, positive = "Malignant")
con_mat_rf
## Confusion Matrix and Statistics
## 
##            Reference
## Prediction  Benign Malignant
##   Benign       983       969
##   Malignant   1011      1037
##                                           
##                Accuracy : 0.505           
##                  95% CI : (0.4894, 0.5206)
##     No Information Rate : 0.5015          
##     P-Value [Acc > NIR] : 0.3347          
##                                           
##                   Kappa : 0.0099          
##                                           
##  Mcnemar's Test P-Value : 0.3568          
##                                           
##             Sensitivity : 0.5169          
##             Specificity : 0.4930          
##          Pos Pred Value : 0.5063          
##          Neg Pred Value : 0.5036          
##              Prevalence : 0.5015          
##          Detection Rate : 0.2592          
##    Detection Prevalence : 0.5120          
##       Balanced Accuracy : 0.5050          
##                                           
##        'Positive' Class : Malignant       
## 

Let’s make plots and see our model results.

plot(model2_rf)

randomForest::varImpPlot(model2_rf$finalModel, sort = TRUE, 
           n.var = 4, main = "The top 4 predictive features")

4.3 K-Nearest Neighbor (KNN)

model3_knn<- train(diagnosis ~., data = bc_training, 
                      method = "knn", 
                      metric = "ROC", 
                      preProcess = c("scale", "center"), 
                      trControl = bc_control, 
                      tuneLength =31)

Let’s make a plot of our model.

plot(model3_knn)

predictions_knn <- predict(model3_knn, bc_testing)
con_mat_knn <- confusionMatrix(predictions_knn, bc_testing$diagnosis, positive = "Malignant")
con_mat_knn
## Confusion Matrix and Statistics
## 
##            Reference
## Prediction  Benign Malignant
##   Benign      1009       997
##   Malignant    985      1009
##                                           
##                Accuracy : 0.5045          
##                  95% CI : (0.4889, 0.5201)
##     No Information Rate : 0.5015          
##     P-Value [Acc > NIR] : 0.3581          
##                                           
##                   Kappa : 0.009           
##                                           
##  Mcnemar's Test P-Value : 0.8048          
##                                           
##             Sensitivity : 0.5030          
##             Specificity : 0.5060          
##          Pos Pred Value : 0.5060          
##          Neg Pred Value : 0.5030          
##              Prevalence : 0.5015          
##          Detection Rate : 0.2522          
##    Detection Prevalence : 0.4985          
##       Balanced Accuracy : 0.5045          
##                                           
##        'Positive' Class : Malignant       
## 

4.4 Support Vector Machine (SVM)

set.seed(1570)
model4_svm <- train(diagnosis ~., data = bc_training, method = "svmLinear", 
                      metric = "ROC", 
                      preProcess = c("scale", "center"), 
                      trace = FALSE, 
                      trControl = bc_control)
## line search fails 0.002909029 -0.006175341 -4.071908e-06 1.362345e-05 4.179166e-09 -3.876586e-09 -6.982964e-14
## Warning in method$predict(modelFit = modelFit, newdata = newdata, submodels =
## param): kernlab class prediction calculations failed; returning NAs
## Warning in method$prob(modelFit = modelFit, newdata = newdata, submodels =
## param): kernlab class probability calculations failed; returning NAs
## Warning in data.frame(..., check.names = FALSE): row names were found from a
## short variable and have been discarded
## Warning in nominalTrainWorkflow(x = x, y = y, wts = weights, info = trainInfo,
## : There were missing values in resampled performance measures.
prediction_svm <- predict(model4_svm, bc_testing)
con_mat_svm <- confusionMatrix(prediction_svm, bc_testing$diagnosis, positive = "Malignant")
con_mat_svm
## Confusion Matrix and Statistics
## 
##            Reference
## Prediction  Benign Malignant
##   Benign       146       143
##   Malignant   1848      1863
##                                           
##                Accuracy : 0.5022          
##                  95% CI : (0.4866, 0.5179)
##     No Information Rate : 0.5015          
##     P-Value [Acc > NIR] : 0.4685          
##                                           
##                   Kappa : 0.0019          
##                                           
##  Mcnemar's Test P-Value : <2e-16          
##                                           
##             Sensitivity : 0.92871         
##             Specificity : 0.07322         
##          Pos Pred Value : 0.50202         
##          Neg Pred Value : 0.50519         
##              Prevalence : 0.50150         
##          Detection Rate : 0.46575         
##    Detection Prevalence : 0.92775         
##       Balanced Accuracy : 0.50097         
##                                           
##        'Positive' Class : Malignant       
## 

5. Results

Models <- c("Logistic Regression", "Random Forest", "K-Nearest Neighbor", "Support Vector Machine")
Accuracy <- round(c(con_mat_lr$overall["Accuracy"], +
                      con_mat_rf$overall["Accuracy"],+
                      con_mat_knn$overall["Accuracy"], +
                      con_mat_svm$overall["Accuracy"]),2)   
table_of_Accuracy <- data.frame(Models, Accuracy)
table_of_Accuracy
##                   Models Accuracy
## 1    Logistic Regression      0.5
## 2          Random Forest      0.5
## 3     K-Nearest Neighbor      0.5
## 4 Support Vector Machine      0.5
model_list <- list(Logistic = model1_lr, rf = model2_rf, knn = model3_knn, svm = model4_svm)
results <- resamples(model_list)

summary(results)
## 
## Call:
## summary.resamples(object = results)
## 
## Models: Logistic, rf, knn, svm 
## Number of resamples: 10 
## 
## ROC 
##               Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
## Logistic 0.4844101 0.4954016 0.5024477 0.5027556 0.5113788 0.5265904    0
## rf       0.4730076 0.4855249 0.4914622 0.4941759 0.5004844 0.5211558    0
## knn      0.4828225 0.5023376 0.5093715 0.5072971 0.5149446 0.5200292    0
## svm      0.4800780 0.4924796 0.4935018 0.4976706 0.5054240 0.5171642    1
## 
## Sens 
##                Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
## Logistic 0.42534504 0.4335840 0.4422836 0.4443320 0.4556321 0.4661654    0
## rf       0.46741855 0.4763434 0.4956140 0.4913553 0.5001572 0.5219573    0
## knn      0.47678795 0.4863716 0.5040754 0.5008772 0.5090852 0.5263158    0
## svm      0.09273183 0.3621554 0.3984962 0.3594461 0.4341280 0.4624060    1
## 
## Spec 
##               Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
## Logistic 0.5280199 0.5434495 0.5617207 0.5601959 0.5767664 0.5853051    0
## rf       0.4788030 0.4890898 0.5034254 0.5029877 0.5104327 0.5379826    0
## knn      0.4769614 0.5045178 0.5074813 0.5054850 0.5132444 0.5224439    0
## svm      0.5516812 0.5635910 0.5748130 0.6384518 0.6288917 0.9178082    1
bwplot(results, metric = "ROC")

6. Conclusion

The knn model give us the best results. The accuracy of the model is 50% and ROC has a medium but slightly better predictive power. The main future goal is to make predictions on real health data to see the actual differences or improvements.