Chapter 3: Classification using Nearest Neighbors

Example: Classifying Cancer Samples

Step 2: Exploring and preparing the data

# import the CSV file
wbcd <- read.csv("wisc_bc_data.csv", stringsAsFactors = FALSE)

# examine the structure of the wbcd data frame
str(wbcd)
## 'data.frame':    569 obs. of  32 variables:
##  $ id               : int  87139402 8910251 905520 868871 9012568 906539 925291 87880 862989 89827 ...
##  $ diagnosis        : chr  "B" "B" "B" "B" ...
##  $ radius_mean      : num  12.3 10.6 11 11.3 15.2 ...
##  $ texture_mean     : num  12.4 18.9 16.8 13.4 13.2 ...
##  $ perimeter_mean   : num  78.8 69.3 70.9 73 97.7 ...
##  $ area_mean        : num  464 346 373 385 712 ...
##  $ smoothness_mean  : num  0.1028 0.0969 0.1077 0.1164 0.0796 ...
##  $ compactness_mean : num  0.0698 0.1147 0.078 0.1136 0.0693 ...
##  $ concavity_mean   : num  0.0399 0.0639 0.0305 0.0464 0.0339 ...
##  $ points_mean      : num  0.037 0.0264 0.0248 0.048 0.0266 ...
##  $ symmetry_mean    : num  0.196 0.192 0.171 0.177 0.172 ...
##  $ dimension_mean   : num  0.0595 0.0649 0.0634 0.0607 0.0554 ...
##  $ radius_se        : num  0.236 0.451 0.197 0.338 0.178 ...
##  $ texture_se       : num  0.666 1.197 1.387 1.343 0.412 ...
##  $ perimeter_se     : num  1.67 3.43 1.34 1.85 1.34 ...
##  $ area_se          : num  17.4 27.1 13.5 26.3 17.7 ...
##  $ smoothness_se    : num  0.00805 0.00747 0.00516 0.01127 0.00501 ...
##  $ compactness_se   : num  0.0118 0.03581 0.00936 0.03498 0.01485 ...
##  $ concavity_se     : num  0.0168 0.0335 0.0106 0.0219 0.0155 ...
##  $ points_se        : num  0.01241 0.01365 0.00748 0.01965 0.00915 ...
##  $ symmetry_se      : num  0.0192 0.035 0.0172 0.0158 0.0165 ...
##  $ dimension_se     : num  0.00225 0.00332 0.0022 0.00344 0.00177 ...
##  $ radius_worst     : num  13.5 11.9 12.4 11.9 16.2 ...
##  $ texture_worst    : num  15.6 22.9 26.4 15.8 15.7 ...
##  $ perimeter_worst  : num  87 78.3 79.9 76.5 104.5 ...
##  $ area_worst       : num  549 425 471 434 819 ...
##  $ smoothness_worst : num  0.139 0.121 0.137 0.137 0.113 ...
##  $ compactness_worst: num  0.127 0.252 0.148 0.182 0.174 ...
##  $ concavity_worst  : num  0.1242 0.1916 0.1067 0.0867 0.1362 ...
##  $ points_worst     : num  0.0939 0.0793 0.0743 0.0861 0.0818 ...
##  $ symmetry_worst   : num  0.283 0.294 0.3 0.21 0.249 ...
##  $ dimension_worst  : num  0.0677 0.0759 0.0788 0.0678 0.0677 ...
# drop the id feature
wbcd <- wbcd[-1]

#see the changes
wbcd
# table of diagnosis
table(wbcd$diagnosis)
## 
##   B   M 
## 357 212

#this refers to the nature of the autopsy: B=Benign, M=Malignant

# recode diagnosis as a factor
wbcd$diagnosis <- factor(wbcd$diagnosis, levels = c("B", "M"),
                         labels = c("Benign", "Malignant"))

#confirm the changes
str(wbcd$diagnosis)
##  Factor w/ 2 levels "Benign","Malignant": 1 1 1 1 1 1 1 2 1 1 ...
# table or proportions with more informative labels
round(prop.table(table(wbcd$diagnosis)) * 100, digits = 1)
## 
##    Benign Malignant 
##      62.7      37.3

#62.7% of the outcomes of the biopsies were found to be benign tumours. The other 37.3 were cancerous tumours

# summarize three numeric features
summary(wbcd[c("radius_mean", "area_mean", "smoothness_mean")])
##   radius_mean       area_mean      smoothness_mean  
##  Min.   : 6.981   Min.   : 143.5   Min.   :0.05263  
##  1st Qu.:11.700   1st Qu.: 420.3   1st Qu.:0.08637  
##  Median :13.370   Median : 551.1   Median :0.09587  
##  Mean   :14.127   Mean   : 654.9   Mean   :0.09636  
##  3rd Qu.:15.780   3rd Qu.: 782.7   3rd Qu.:0.10530  
##  Max.   :28.110   Max.   :2501.0   Max.   :0.16340
#The results from above represent the different features from each biopsy.
#The radius of the largest biopsy was 28.11, but in average, the samples had an area of 654.9. we will assume that these metrics are in cm.

#Based on my limited domain knowledge, the less smooth the appearance of a sample, the higher are the chances of a the biopsy to reveal cancerous tumor.
# create normalization function
normalize <- function(x) {
  return ((x - min(x)) / (max(x) - min(x)))
}

#the idea here is to put all of the observations in a range from 0-1 to be able to manage any outliers. 
# test normalization function - result should be identical
normalize(c(1, 2, 3, 4, 5))
## [1] 0.00 0.25 0.50 0.75 1.00
normalize(c(10, 20, 30, 40, 50)) # this indicates that these two are in the same scale
## [1] 0.00 0.25 0.50 0.75 1.00
# normalize the wbcd data
#here we are normalizing all of the independent variables for our analysis, and we are leaving the target column or "diagnosis" as the dependent variable of these 31 features.
wbcd_n <- as.data.frame(lapply(wbcd[2:31], normalize))
wbcd_n #these are all now in a range from 0-1 on each category
# confirm that normalization worked
summary(wbcd_n$area_mean)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.0000  0.1174  0.1729  0.2169  0.2711  1.0000
#here we can see that the average size of a biopsy is 21.69 mm
# create training and test data
wbcd_train <- wbcd_n[1:469, ] # this is the range for us to train the data. This is about 82% of the data.
wbcd_test <- wbcd_n[470:569, ] # the data will be tested in 18% of the data
# create labels for training and test data

wbcd_train_labels <- wbcd[1:469, 1]
wbcd_test_labels <- wbcd[470:569, 1]
#see the labels for the training data
wbcd_train_labels 
##   [1] Benign    Benign    Benign    Benign    Benign    Benign    Benign   
##   [8] Malignant Benign    Benign    Malignant Benign    Benign    Benign   
##  [15] Malignant Benign    Benign    Benign    Malignant Benign    Benign   
##  [22] Benign    Benign    Benign    Benign    Malignant Benign    Malignant
##  [29] Benign    Benign    Benign    Malignant Malignant Benign    Benign   
##  [36] Benign    Malignant Benign    Malignant Malignant Malignant Malignant
##  [43] Malignant Benign    Benign    Malignant Benign    Malignant Benign   
##  [50] Benign    Malignant Benign    Benign    Benign    Malignant Benign   
##  [57] Benign    Benign    Malignant Malignant Malignant Malignant Malignant
##  [64] Malignant Malignant Benign    Benign    Benign    Benign    Benign   
##  [71] Malignant Benign    Benign    Benign    Benign    Benign    Malignant
##  [78] Benign    Benign    Malignant Benign    Benign    Benign    Benign   
##  [85] Benign    Benign    Benign    Benign    Malignant Benign    Benign   
##  [92] Benign    Malignant Benign    Malignant Malignant Benign    Benign   
##  [99] Benign    Malignant Benign    Benign    Benign    Malignant Benign   
## [106] Benign    Benign    Benign    Benign    Benign    Benign    Benign   
## [113] Malignant Malignant Benign    Malignant Malignant Malignant Malignant
## [120] Benign    Benign    Malignant Benign    Malignant Benign    Benign   
## [127] Malignant Malignant Malignant Malignant Malignant Benign    Benign   
## [134] Malignant Benign    Malignant Benign    Benign    Malignant Benign   
## [141] Malignant Malignant Malignant Malignant Malignant Benign    Malignant
## [148] Benign    Benign    Benign    Benign    Benign    Benign    Benign   
## [155] Benign    Benign    Benign    Malignant Benign    Benign    Benign   
## [162] Benign    Benign    Benign    Malignant Malignant Benign    Malignant
## [169] Benign    Benign    Malignant Malignant Malignant Benign    Malignant
## [176] Benign    Malignant Benign    Malignant Benign    Benign    Malignant
## [183] Benign    Malignant Benign    Benign    Malignant Malignant Malignant
## [190] Benign    Malignant Benign    Benign    Malignant Malignant Malignant
## [197] Malignant Benign    Benign    Benign    Malignant Benign    Benign   
## [204] Benign    Benign    Malignant Benign    Malignant Benign    Malignant
## [211] Benign    Malignant Malignant Benign    Benign    Benign    Benign   
## [218] Benign    Malignant Malignant Benign    Benign    Malignant Benign   
## [225] Benign    Benign    Benign    Malignant Malignant Benign    Benign   
## [232] Benign    Benign    Malignant Benign    Benign    Malignant Benign   
## [239] Benign    Benign    Malignant Benign    Benign    Malignant Malignant
## [246] Benign    Benign    Benign    Benign    Benign    Benign    Malignant
## [253] Benign    Malignant Benign    Benign    Benign    Benign    Benign   
## [260] Benign    Benign    Benign    Malignant Benign    Malignant Malignant
## [267] Benign    Benign    Benign    Malignant Benign    Malignant Benign   
## [274] Benign    Malignant Malignant Benign    Malignant Benign    Benign   
## [281] Malignant Benign    Benign    Benign    Malignant Benign    Malignant
## [288] Malignant Malignant Benign    Benign    Benign    Malignant Benign   
## [295] Malignant Malignant Malignant Malignant Benign    Malignant Malignant
## [302] Benign    Benign    Malignant Malignant Malignant Malignant Benign   
## [309] Benign    Benign    Malignant Malignant Benign    Benign    Malignant
## [316] Benign    Malignant Malignant Benign    Benign    Benign    Malignant
## [323] Malignant Benign    Benign    Malignant Benign    Malignant Benign   
## [330] Malignant Benign    Benign    Benign    Benign    Malignant Benign   
## [337] Benign    Benign    Malignant Benign    Benign    Malignant Malignant
## [344] Benign    Malignant Benign    Benign    Benign    Malignant Benign   
## [351] Benign    Malignant Benign    Benign    Malignant Benign    Benign   
## [358] Benign    Malignant Benign    Malignant Malignant Benign    Benign   
## [365] Benign    Benign    Malignant Benign    Benign    Malignant Benign   
## [372] Benign    Benign    Benign    Benign    Benign    Benign    Benign   
## [379] Benign    Benign    Benign    Benign    Malignant Malignant Benign   
## [386] Benign    Malignant Benign    Malignant Benign    Malignant Benign   
## [393] Benign    Malignant Benign    Benign    Benign    Malignant Malignant
## [400] Benign    Benign    Benign    Malignant Malignant Benign    Malignant
## [407] Benign    Benign    Malignant Benign    Benign    Benign    Benign   
## [414] Benign    Benign    Malignant Benign    Benign    Malignant Malignant
## [421] Benign    Malignant Benign    Benign    Benign    Malignant Benign   
## [428] Benign    Benign    Malignant Malignant Malignant Benign    Malignant
## [435] Benign    Benign    Malignant Benign    Benign    Benign    Benign   
## [442] Benign    Malignant Benign    Malignant Benign    Benign    Benign   
## [449] Benign    Malignant Benign    Malignant Malignant Benign    Benign   
## [456] Benign    Benign    Malignant Benign    Benign    Malignant Malignant
## [463] Benign    Benign    Malignant Benign    Benign    Malignant Benign   
## Levels: Benign Malignant
#this will allow the model to be trained and to run tests on the trained data

Step 3: Training a model on the data

# load the "class" library
library(class)
# here we are assigning the K-Nearest Neighbor algorithm which will use the euclidean distance to group the data and create predictions using the labels from the trained data.

wbcd_test_pred <- knn(train = wbcd_train, test = wbcd_test,
                      cl = wbcd_train_labels, k = 21) #we have to specify the training and testing portions for the algorithm to be instantiated and assigned to the test_pred variable. Finally, the model will use at least 21 neighbors to assign each new data point to either a benign or malignant label.

#display the predictions created by knn
wbcd_test_pred
##   [1] Benign    Benign    Benign    Benign    Malignant Benign    Malignant
##   [8] Benign    Malignant Benign    Malignant Benign    Malignant Malignant
##  [15] Benign    Benign    Malignant Benign    Malignant Benign    Malignant
##  [22] Malignant Malignant Malignant Benign    Benign    Benign    Benign   
##  [29] Malignant Malignant Malignant Benign    Malignant Malignant Benign   
##  [36] Benign    Benign    Benign    Benign    Malignant Malignant Benign   
##  [43] Malignant Malignant Benign    Malignant Malignant Malignant Malignant
##  [50] Malignant Benign    Benign    Benign    Benign    Benign    Benign   
##  [57] Benign    Benign    Malignant Benign    Benign    Benign    Benign   
##  [64] Benign    Malignant Malignant Benign    Benign    Benign    Benign   
##  [71] Benign    Malignant Benign    Benign    Malignant Malignant Benign   
##  [78] Benign    Benign    Benign    Benign    Benign    Benign    Malignant
##  [85] Benign    Benign    Malignant Benign    Benign    Benign    Benign   
##  [92] Malignant Benign    Benign    Benign    Benign    Benign    Malignant
##  [99] Benign    Malignant
## Levels: Benign Malignant

Step 4: Evaluating model performance

# load the "gmodels" library
library(gmodels)

# Create the cross tabulation of predicted vs. actual
CrossTable(x = wbcd_test_labels, y = wbcd_test_pred,
           prop.chisq = FALSE)
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |           N / Row Total |
## |           N / Col Total |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  100 
## 
##  
##                  | wbcd_test_pred 
## wbcd_test_labels |    Benign | Malignant | Row Total | 
## -----------------|-----------|-----------|-----------|
##           Benign |        61 |         0 |        61 | 
##                  |     1.000 |     0.000 |     0.610 | 
##                  |     0.968 |     0.000 |           | 
##                  |     0.610 |     0.000 |           | 
## -----------------|-----------|-----------|-----------|
##        Malignant |         2 |        37 |        39 | 
##                  |     0.051 |     0.949 |     0.390 | 
##                  |     0.032 |     1.000 |           | 
##                  |     0.020 |     0.370 |           | 
## -----------------|-----------|-----------|-----------|
##     Column Total |        63 |        37 |       100 | 
##                  |     0.630 |     0.370 |           | 
## -----------------|-----------|-----------|-----------|
## 
## 

#our model predicted from the trained data that 61 of the 100 samples were benign correctly.The model also predicted that 2 of the biopsies were benign when in reality these were malignant, and it predicted accurately that 37 of the remaining samples were malignant tumors.

## Step 5: Improving model performance

# use the scale() function to z-score standardize a data frame
wbcd_z <- as.data.frame(scale(wbcd[-1])) #here we need to exclude the target variable from the scaling

# confirm that the transformation was applied correctly
summary(wbcd_z$area_mean)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## -1.4532 -0.6666 -0.2949  0.0000  0.3632  5.2459
# RECREATE labels for training and test data

wbcd_train_labels <- wbcd[1:512, 1]
wbcd_test_labels <- wbcd[513:569, 1]
# create training and test data sets
wbcd_train <- wbcd_z[1:512, ] #80% training size
wbcd_test <- wbcd_z[513:569, ] #20% test size

# re-classify test cases
wbcd_test_pred <- knn(train = wbcd_train, test = wbcd_test,
                      cl = wbcd_train_labels, k = 15) #here we will be using 15 neighbors to classify the results of the biopsies
# Create the cross tabulation of predicted vs. actual
CrossTable(x = wbcd_test_labels, y = wbcd_test_pred,
           prop.chisq = FALSE)
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |           N / Row Total |
## |           N / Col Total |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  57 
## 
##  
##                  | wbcd_test_pred 
## wbcd_test_labels |    Benign | Malignant | Row Total | 
## -----------------|-----------|-----------|-----------|
##           Benign |        38 |         1 |        39 | 
##                  |     0.974 |     0.026 |     0.684 | 
##                  |     0.974 |     0.056 |           | 
##                  |     0.667 |     0.018 |           | 
## -----------------|-----------|-----------|-----------|
##        Malignant |         1 |        17 |        18 | 
##                  |     0.056 |     0.944 |     0.316 | 
##                  |     0.026 |     0.944 |           | 
##                  |     0.018 |     0.298 |           | 
## -----------------|-----------|-----------|-----------|
##     Column Total |        39 |        18 |        57 | 
##                  |     0.684 |     0.316 |           | 
## -----------------|-----------|-----------|-----------|
## 
## 

#using a sample of 57 biopsies, the model only had one false positive and one false negative

# try several different values of k

# RECREATE labels for training and test data

wbcd_train_labels <- wbcd[1:500, 1]
wbcd_test_labels <- wbcd[501:569, 1]

# try several different values of k
wbcd_train <- wbcd_n[1:500, ]
wbcd_test <- wbcd_n[501:569, ]
#K=1
wbcd_test_pred <- knn(train = wbcd_train, test = wbcd_test, cl = wbcd_train_labels, k=1)
CrossTable(x = wbcd_test_labels, y = wbcd_test_pred, prop.chisq=FALSE)
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |           N / Row Total |
## |           N / Col Total |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  69 
## 
##  
##                  | wbcd_test_pred 
## wbcd_test_labels |    Benign | Malignant | Row Total | 
## -----------------|-----------|-----------|-----------|
##           Benign |        43 |         2 |        45 | 
##                  |     0.956 |     0.044 |     0.652 | 
##                  |     0.977 |     0.080 |           | 
##                  |     0.623 |     0.029 |           | 
## -----------------|-----------|-----------|-----------|
##        Malignant |         1 |        23 |        24 | 
##                  |     0.042 |     0.958 |     0.348 | 
##                  |     0.023 |     0.920 |           | 
##                  |     0.014 |     0.333 |           | 
## -----------------|-----------|-----------|-----------|
##     Column Total |        44 |        25 |        69 | 
##                  |     0.638 |     0.362 |           | 
## -----------------|-----------|-----------|-----------|
## 
## 

#when k=1 the model predicts 2 false negatives and one false positive. The quality of our model decreased but not significantly

#k=5
wbcd_test_pred <- knn(train = wbcd_train, test = wbcd_test, cl = wbcd_train_labels, k=5)
CrossTable(x = wbcd_test_labels, y = wbcd_test_pred, prop.chisq=FALSE)
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |           N / Row Total |
## |           N / Col Total |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  69 
## 
##  
##                  | wbcd_test_pred 
## wbcd_test_labels |    Benign | Malignant | Row Total | 
## -----------------|-----------|-----------|-----------|
##           Benign |        45 |         0 |        45 | 
##                  |     1.000 |     0.000 |     0.652 | 
##                  |     0.978 |     0.000 |           | 
##                  |     0.652 |     0.000 |           | 
## -----------------|-----------|-----------|-----------|
##        Malignant |         1 |        23 |        24 | 
##                  |     0.042 |     0.958 |     0.348 | 
##                  |     0.022 |     1.000 |           | 
##                  |     0.014 |     0.333 |           | 
## -----------------|-----------|-----------|-----------|
##     Column Total |        46 |        23 |        69 | 
##                  |     0.667 |     0.333 |           | 
## -----------------|-----------|-----------|-----------|
## 
## 

#Here the model produced only 1 false positive which is an improvement compared to K=1

wbcd_test_pred <- knn(train = wbcd_train, test = wbcd_test, cl = wbcd_train_labels, k=11)
CrossTable(x = wbcd_test_labels, y = wbcd_test_pred, prop.chisq=FALSE) 
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |           N / Row Total |
## |           N / Col Total |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  69 
## 
##  
##                  | wbcd_test_pred 
## wbcd_test_labels |    Benign | Malignant | Row Total | 
## -----------------|-----------|-----------|-----------|
##           Benign |        45 |         0 |        45 | 
##                  |     1.000 |     0.000 |     0.652 | 
##                  |     0.938 |     0.000 |           | 
##                  |     0.652 |     0.000 |           | 
## -----------------|-----------|-----------|-----------|
##        Malignant |         3 |        21 |        24 | 
##                  |     0.125 |     0.875 |     0.348 | 
##                  |     0.062 |     1.000 |           | 
##                  |     0.043 |     0.304 |           | 
## -----------------|-----------|-----------|-----------|
##     Column Total |        48 |        21 |        69 | 
##                  |     0.696 |     0.304 |           | 
## -----------------|-----------|-----------|-----------|
## 
## 

The model increased the number of false positives to 3 from 1 when we changed k to 11 from 5

wbcd_test_pred <- knn(train = wbcd_train, test = wbcd_test, cl = wbcd_train_labels, k=15)
CrossTable(x = wbcd_test_labels, y = wbcd_test_pred, prop.chisq=FALSE)
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |           N / Row Total |
## |           N / Col Total |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  69 
## 
##  
##                  | wbcd_test_pred 
## wbcd_test_labels |    Benign | Malignant | Row Total | 
## -----------------|-----------|-----------|-----------|
##           Benign |        45 |         0 |        45 | 
##                  |     1.000 |     0.000 |     0.652 | 
##                  |     0.938 |     0.000 |           | 
##                  |     0.652 |     0.000 |           | 
## -----------------|-----------|-----------|-----------|
##        Malignant |         3 |        21 |        24 | 
##                  |     0.125 |     0.875 |     0.348 | 
##                  |     0.062 |     1.000 |           | 
##                  |     0.043 |     0.304 |           | 
## -----------------|-----------|-----------|-----------|
##     Column Total |        48 |        21 |        69 | 
##                  |     0.696 |     0.304 |           | 
## -----------------|-----------|-----------|-----------|
## 
## 

#similar results compared to the ones at k=11

wbcd_test_pred <- knn(train = wbcd_train, test = wbcd_test, cl = wbcd_train_labels, k=21)
CrossTable(x = wbcd_test_labels, y = wbcd_test_pred, prop.chisq=FALSE)
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |           N / Row Total |
## |           N / Col Total |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  69 
## 
##  
##                  | wbcd_test_pred 
## wbcd_test_labels |    Benign | Malignant | Row Total | 
## -----------------|-----------|-----------|-----------|
##           Benign |        45 |         0 |        45 | 
##                  |     1.000 |     0.000 |     0.652 | 
##                  |     0.938 |     0.000 |           | 
##                  |     0.652 |     0.000 |           | 
## -----------------|-----------|-----------|-----------|
##        Malignant |         3 |        21 |        24 | 
##                  |     0.125 |     0.875 |     0.348 | 
##                  |     0.062 |     1.000 |           | 
##                  |     0.043 |     0.304 |           | 
## -----------------|-----------|-----------|-----------|
##     Column Total |        48 |        21 |        69 | 
##                  |     0.696 |     0.304 |           | 
## -----------------|-----------|-----------|-----------|
## 
## 

the model seems to have no improvements as we start increasing k. So far, we start seeing diminishing returns at k=11

wbcd_test_pred <- knn(train = wbcd_train, test = wbcd_test, cl = wbcd_train_labels, k=27)
CrossTable(x = wbcd_test_labels, y = wbcd_test_pred, prop.chisq=FALSE)
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |           N / Row Total |
## |           N / Col Total |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  69 
## 
##  
##                  | wbcd_test_pred 
## wbcd_test_labels |    Benign | Malignant | Row Total | 
## -----------------|-----------|-----------|-----------|
##           Benign |        45 |         0 |        45 | 
##                  |     1.000 |     0.000 |     0.652 | 
##                  |     0.938 |     0.000 |           | 
##                  |     0.652 |     0.000 |           | 
## -----------------|-----------|-----------|-----------|
##        Malignant |         3 |        21 |        24 | 
##                  |     0.125 |     0.875 |     0.348 | 
##                  |     0.062 |     1.000 |           | 
##                  |     0.043 |     0.304 |           | 
## -----------------|-----------|-----------|-----------|
##     Column Total |        48 |        21 |        69 | 
##                  |     0.696 |     0.304 |           | 
## -----------------|-----------|-----------|-----------|
## 
## 

#based on all of the tests ran, the ideal number of neighbors for our model is k=5