Pengatar Data Sains

~ Tugas 3 ~


Kontak : \(\downarrow\)
Email
Instagram https://www.instagram.com/diasary_nm/
RPubs https://rpubs.com/diyasarya/

Import Data

library(tidyverse)

print(getwd())
## [1] "C:/Users/diyas/OneDrive/Documents/Semester 2 Matana University/DataSains/Tugas"
Data_Cancer <- read.csv("wdbc.txt")
Unscaled_Cancer <- read.csv("unscaled_wdbc.txt")
Data_Cancer
Unscaled_Cancer

Describe Variables in Data Set

Untuk melihat isi dari Data Frame kita gunakan glimpse, function ini berguna untuk melihat berapa kolom dan baris dari data tersebut serta melihat type data per kolom.

glimpse(Data_Cancer)
## Rows: 569
## Columns: 12
## $ ID                <int> 842302, 842517, 84300903, 84348301, 84358402, 843786~
## $ Class             <chr> "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M~
## $ Radius            <dbl> 1.0960995, 1.8282120, 1.5784992, -0.7682333, 1.74875~
## $ Texture           <dbl> -2.0715123, -0.3533215, 0.4557859, 0.2535091, -1.150~
## $ Perimeter         <dbl> 1.26881726, 1.68447255, 1.56512598, -0.59216612, 1.7~
## $ Area              <dbl> 0.98350952, 1.90703027, 1.55751319, -0.76379174, 1.8~
## $ Smoothness        <dbl> 1.56708746, -0.82623545, 0.94138212, 3.28066684, 0.2~
## $ Compactness       <dbl> 3.28062806, -0.48664348, 1.05199990, 3.39991742, 0.5~
## $ Concavity         <dbl> 2.65054179, -0.02382489, 1.36227979, 1.91421287, 1.3~
## $ Concave_Points    <dbl> 2.53024886, 0.54766227, 2.03543978, 1.45043113, 1.42~
## $ Symmetry          <dbl> 2.215565542, 0.001391139, 0.938858720, 2.864862154, ~
## $ Fractal_Dimension <dbl> 2.25376381, -0.86788881, -0.39765801, 4.90660199, -0~

Dari output di atas didapat kolom Class memiliki type data <chr>. Karena kita akan menggunakan Class sebagai variable kategori statistical, kita harus mengubah type datanya menjadi factor dengan function as_factor.

Data_Cancer <- Data_Cancer %>%
  mutate(Class = as.factor(Class))
glimpse(Data_Cancer)
## Rows: 569
## Columns: 12
## $ ID                <int> 842302, 842517, 84300903, 84348301, 84358402, 843786~
## $ Class             <fct> M, M, M, M, M, M, M, M, M, M, M, M, M, M, M, M, M, M~
## $ Radius            <dbl> 1.0960995, 1.8282120, 1.5784992, -0.7682333, 1.74875~
## $ Texture           <dbl> -2.0715123, -0.3533215, 0.4557859, 0.2535091, -1.150~
## $ Perimeter         <dbl> 1.26881726, 1.68447255, 1.56512598, -0.59216612, 1.7~
## $ Area              <dbl> 0.98350952, 1.90703027, 1.55751319, -0.76379174, 1.8~
## $ Smoothness        <dbl> 1.56708746, -0.82623545, 0.94138212, 3.28066684, 0.2~
## $ Compactness       <dbl> 3.28062806, -0.48664348, 1.05199990, 3.39991742, 0.5~
## $ Concavity         <dbl> 2.65054179, -0.02382489, 1.36227979, 1.91421287, 1.3~
## $ Concave_Points    <dbl> 2.53024886, 0.54766227, 2.03543978, 1.45043113, 1.42~
## $ Symmetry          <dbl> 2.215565542, 0.001391139, 0.938858720, 2.864862154, ~
## $ Fractal_Dimension <dbl> 2.25376381, -0.86788881, -0.39765801, 4.90660199, -0~

Setelah kita ubah menjadi type factor, kita kategorikan kolom Class dengan menggunakan function levels. Karena kolom Class hanya memiliki 2 nilai yang berbeda maka levels dari Class juga memiliki 2 nilai yaitu B for Benign dan M for Malignant. Untuk melakukan seperti itu, langkah pertama kita gunakan pull sebagai function untuk mengekstrak satu kolom (Class) setelah itu, gunakan levels untuk melihat kategori dalam kolom Class.

Data_Cancer %>%
  pull(Class) %>%
  levels()
## [1] "B" "M"

Exploring Data Set

Dalam mengeksplor data, untuk mengetahui berapa sih jumlah atau presentasi dari kolom Class berdasarkan kategori levelnya. Maka, kita akan menggunakan function ini group_by, summarize, dan n.

jumlah_baris <- nrow(Data_Cancer)
Data_Cancer %>%
  group_by(Class) %>%
  summarize(
    Jumlah = n(),
    Perentasi = n()/jumlah_baris * 100
  )

Berdasarkan output di atas bahwa ada 357(63%) Benign dan 212(37%) Malignant.

Setelah itu, kita buat scatter plot untuk mengetahui hubungan antara variable Perimeter dan Concavity, kita coba untuk menvisualisasikannya dengan ggplot. Agar lebih mudah dilihat kita gunakan perbedaan warna pada variable B dan M dengan menggunakan function scale_color_manual.

perim_concav <- Data_Cancer %>%
  ggplot(aes(x = Perimeter, y = Concavity, color = Class)) +
  geom_point(alpha = 0.6) +
  labs(x = "Perimeter (standardized)", 
       y = "Concavity (standardized)",
       color = "Diagnosis") +
  scale_color_manual(labels = c("Malignant", "Benign"), 
                     values = c("orange2", "steelblue2")) +
  theme(text = element_text(size = 12))
perim_concav

Dari scatter di atas dapat disimpulkan bahwa Benign memiliki nilai concavity dan perimeter yang rendah atau bisa dilihat karena Benign berada pada sebaran di bawah kiri. Sedangkan, Malignant memiliki nilai concavity dan perimeter yang tinggi karena berada pada sebaran di atas kanan.

Classification with KNN

Untuk mencari KNN dalam penelitian, kita dapat menghitung jarak dari penelitian tersebut. Biasanya kita memilih nilai K terkecil untuk menemukan nilai jarak yang efektif. Contohnya pada coding di bawah ini kita menggunakan K = 5 dengan pridictor variablenya adalah Perimeter = 0 dan Concavity = 3.5.

new_obs_Perimeter <- 0
new_obs_Concavity <- 3.5
Data_Cancer %>%
  select(ID, Perimeter, Concavity, Class) |>
  mutate(dist_from_new = sqrt((Perimeter - new_obs_Perimeter)^2 + 
                              (Concavity - new_obs_Concavity)^2)) %>%
  arrange(dist_from_new) %>%
  slice(1:5) # take the first 5 rows

Hasil di atas menunjukkan bahwa jarak terdekat dari 5 data di atas ada 3 data yang Classnya Malignant (M).

Koding di atas untuk menentukan jarak antara dua variable. Sedangkan, untuk menentukan jarak antara lebih dari dua variable dapat menggunakan koding di bawah ini.

new_obs_Perimeter <- 0
new_obs_Concavity <- 3.5
new_obs_Symmetry <- 1

Data_Cancer %>%
  select(ID, Perimeter, Concavity, Symmetry, Class) %>%
  mutate(dist_from_new = sqrt((Perimeter - new_obs_Perimeter)^2 + 
                              (Concavity - new_obs_Concavity)^2 +
                                (Symmetry - new_obs_Symmetry)^2)) %>%
  arrange(dist_from_new) %>%
  slice(1:5) # take the first 5 rows

Hasil di atas menunjukkan bahwa jarak terdekat dari 5 data di atas ada 4 data yang Classnya Malignant (M).

KNN with tidymodels

Agar proses KNN kita lebih efisien, kita gunakan select untuk memilih kolom data mana saja yang akan kita fokuskan untuk proses KNN.

library(tidymodels)
library(kknn)

cancer_train <- Data_Cancer %>%
  select(Class, Perimeter, Concavity)
cancer_train

Koding di bawah ini menjelaskan tentang proses pemodelan KNN, langkah pertama kita gunakan function nearest_neighbor dengan nilai K = 5. Untuk menentukan jenis metode/model yang akan digunakan, kita pakai function set_engine. Sedangkan, untuk menetukan klasifikasi KNN kita gunakan set_mode.

knn_spec <- nearest_neighbor(weight_func = "rectangular", neighbors = 5) %>%
  set_engine("kknn") %>%
  set_mode("classification")
knn_spec
## K-Nearest Neighbor Model Specification (classification)
## 
## Main Arguments:
##   neighbors = 5
##   weight_func = rectangular
## 
## Computational engine: kknn

Langkah selanjutnya kita tentukan Class sebagai target variable dan predictor variablenya adalah Perimeter dan Concavity dengan menggunakan function fit

knn_fit <- knn_spec %>%
  fit(Class ~ Perimeter + Concavity, data = cancer_train)
knn_fit <- knn_spec |>
  fit(Class ~ ., data = cancer_train)
knn_fit
## parsnip model object
## 
## Fit time:  21ms 
## 
## Call:
## kknn::train.kknn(formula = Class ~ ., data = data, ks = min_rows(5,     data, 5), kernel = ~"rectangular")
## 
## Type of response variable: nominal
## Minimal misclassification: 0.07557118
## Best kernel: rectangular
## Best k: 5

Untuk melakukan prediksi pada penelitian gunakan function predict dengan objek yang akan diprediksinya adalah knn_fit.

new_obs <- tibble(Perimeter = 0, Concavity = 3.5)
predict(knn_fit, new_obs)

Data Preprocessing with tidymodels

Dalam proses data dengan tidymodels kita lakukan sama seperti diawal yaitu dengan data Unscaled_Cancer lakukan pengubahan type data menjadi factor dan lakukan pemilihan data yang dibutuhkan seperti Class, Area, Smoothness dalam proses ini menggunakan function select.

Unscaled_Cancer <- Unscaled_Cancer %>%
  mutate(Class = as_factor(Class)) %>%
  select(Class, Area, Smoothness)
Unscaled_Cancer
uc_recipe <- recipe(Class ~ ., data = Unscaled_Cancer)
print(uc_recipe)
## Recipe
## 
## Inputs:
## 
##       role #variables
##    outcome          1
##  predictor          2

Langkah selanjutnya, untuk menentukan apa yang harus kita lakukan pada variable-variable yang sudah dipilih, kita gunakan all_prediction() atau all_outcomes() untuk menentukannya.

Atau untuk menentukan variable itu bentuknya kategori atau numerik kita dapat menggunakan function all_nominal() dan all_numeric().

uc_recipe <- uc_recipe %>%
  step_scale(all_predictors()) %>%
  step_center(all_predictors()) %>%
  prep()
uc_recipe
## Recipe
## 
## Inputs:
## 
##       role #variables
##    outcome          1
##  predictor          2
## 
## Training data contained 569 data points and no missing data.
## 
## Operations:
## 
## Scaling for Area, Smoothness [trained]
## Centering for Area, Smoothness [trained]

Selanjutnya, untuk lebih terorganisir datanya kita gunakan function bake().

scaled_cancer <- bake(uc_recipe, Unscaled_Cancer)
scaled_cancer

Permasalahan ketidakseimbangan data, contoh simpelnya seperti mengambil 3 data dari Malignant dan menyimpan semua data Benign dengan menggunakan function slice_head().

rare_cancer <- bind_rows(
      filter(Data_Cancer, Class == "B"),
      Data_Cancer %>% filter(Class == "M") %>% slice_head(n = 3)
    ) %>%
    select(Class, Perimeter, Concavity)

rare_plot <- rare_cancer %>%
  ggplot(aes(x = Perimeter, y = Concavity, color = Class)) +
  geom_point(alpha = 0.5) +
  labs(x = "Perimeter (standardized)", 
       y = "Concavity (standardized)",
       color = "Diagnosis") +
  scale_color_manual(labels = c("Malignant", "Benign"), 
                     values = c("orange2", "steelblue2")) +
  theme(text = element_text(size = 12))

rare_plot

Untuk menghindari permasalahan di atas, maka kita lakukan dengan cara statistika yaitu dengan menambahkan oversampling pada data ups_recipe dan gunakan function step_upsample() dari themis packages.

library(themis)

ups_recipe <- recipe(Class ~ ., data = rare_cancer) %>%
  step_upsample(Class, over_ratio = 1, skip = FALSE) %>%
  prep()

ups_recipe
## Recipe
## 
## Inputs:
## 
##       role #variables
##    outcome          1
##  predictor          2
## 
## Training data contained 360 data points and no missing data.
## 
## Operations:
## 
## Up-sampling based on Class [trained]

Untuk melihat apakah proses Balancing data sudah sukses kita dapat menggunakan metode dibawah ini.

upsampled_cancer <- bake(ups_recipe, rare_cancer)

upsampled_cancer %>%
  group_by(Class) %>%
  summarize(n = n())

Putting it Together in a workflow

Dalam tidymodels terdapat metode workflow yang dimana metode ini adalah menggabungkan setiap stepnya menjadi satu untuk menganalisa data. Pada percobaan kali ini, kita gunakan data set unscaled_wdbc.txt untuk step-stepnya sama seperti di awal bedanya workflow tidak menggunakan select untuk menentukan variablenya. Tapi kita menggunakan recipe() dengan formula Class ~ Area + Smoothness. Lalu, pada workflow kita tidak perlu menggunakan prep().

# load the unscaled cancer data 
# and make sure the target Class variable is a factor
Unscaled_Cancer <- Unscaled_Cancer %>%
  mutate(Class = as.factor(Class))

# create the KNN model
knn_spec <- nearest_neighbor(weight_func = "rectangular", neighbors = 7) %>%
  set_engine("kknn") %>%
  set_mode("classification")

# create the centering / scaling recipe
uc_recipe <- recipe(Class ~ Area + Smoothness, data = Unscaled_Cancer) %>%
  step_scale(all_predictors()) %>%
  step_center(all_predictors())

Nah, pada workflow untuk menentukan model atau metode yang harus digunakan kita pakai function add_recipe() dan add_model().

knn_fit <- workflow() %>%
  add_recipe(uc_recipe) %>%
  add_model(knn_spec) %>%
  fit(data = Unscaled_Cancer)

knn_fit
## == Workflow [trained] ==========================================================
## Preprocessor: Recipe
## Model: nearest_neighbor()
## 
## -- Preprocessor ----------------------------------------------------------------
## 2 Recipe Steps
## 
## * step_scale()
## * step_center()
## 
## -- Model -----------------------------------------------------------------------
## 
## Call:
## kknn::train.kknn(formula = ..y ~ ., data = data, ks = min_rows(7,     data, 5), kernel = ~"rectangular")
## 
## Type of response variable: nominal
## Minimal misclassification: 0.112478
## Best kernel: rectangular
## Best k: 7

Lalu, untuk melakukan prediksi pada penelitian kita gunakan predict() dengan objeknya adalah knn_fit. Sebagai contoh, kita akan memprediksi data Class dengan dua label penelitian yang berbeda yaitu dengan Area 1 = 500, Area 2 = 1500, Smoothness 1 = 0.075 dan Smoothness 2 = 0.1.

new_observation <- tibble(Area = c(500, 1500), Smoothness = c(0.075, 0.1))
prediction <- predict(knn_fit, new_observation)

prediction
# create the grid of area/smoothness vals, and arrange in a data frame
are_grid <- seq(min(Unscaled_Cancer$Area), 
                max(Unscaled_Cancer$Area), 
                length.out = 100)
smo_grid <- seq(min(Unscaled_Cancer$Smoothness), 
                max(Unscaled_Cancer$Smoothness), 
                length.out = 100)
asgrid <- as_tibble(expand.grid(Area = are_grid, 
                                Smoothness = smo_grid))

# use the fit workflow to make predictions at the grid points
knnPredGrid <- predict(knn_fit, asgrid)

# bind the predictions as a new column with the grid points
prediction_table <- bind_cols(knnPredGrid, asgrid) |> 
  rename(Class = .pred_class)

# plot:
# 1. the colored scatter of the original data
# 2. the faded colored scatter for the grid points
wkflw_plot <-
  ggplot() +
  geom_point(data = Unscaled_Cancer, 
             mapping = aes(x = Area, 
                           y = Smoothness, 
                           color = Class), 
             alpha = 0.75) +
  geom_point(data = prediction_table, 
             mapping = aes(x = Area, 
                           y = Smoothness, 
                           color = Class), 
             alpha = 0.02, 
             size = 5) +
  labs(color = "Diagnosis", 
       x = "Area (standardized)", 
       y = "Smoothness (standardized)") +
  scale_color_manual(labels = c("Malignant", "Benign"), 
                     values = c("orange2", "steelblue2")) +
  theme(text = element_text(size = 12))

wkflw_plot