rf_breastcancer_shiny

1 Kelompok 1:

  1. Angelia Yuflih Pambayun (23031030002)
  2. Septiana Nabila Dwi Halisa (23031030010)
  3. Nariza Rahmadani P.M (23031030012)
  4. Yoga Adi Saputra (23031030027)
  5. Nayli Kurnia ILahi (23031030045)

2 Package

library(readr)
## Warning: package 'readr' was built under R version 4.3.3
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.3.3
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(palmerpenguins)
## Warning: package 'palmerpenguins' was built under R version 4.3.3
library(ranger)
## Warning: package 'ranger' was built under R version 4.3.3
library(tidymodels)
## Warning: package 'tidymodels' was built under R version 4.3.3
## ── Attaching packages ────────────────────────────────────── tidymodels 1.3.0 ──
## ✔ broom        1.0.10     ✔ rsample      1.3.1 
## ✔ dials        1.4.2      ✔ tibble       3.2.1 
## ✔ ggplot2      3.5.2      ✔ tidyr        1.3.1 
## ✔ infer        1.0.9      ✔ tune         1.3.0 
## ✔ modeldata    1.5.1      ✔ workflows    1.3.0 
## ✔ parsnip      1.3.3      ✔ workflowsets 1.1.0 
## ✔ purrr        1.0.4      ✔ yardstick    1.3.2 
## ✔ recipes      1.3.1
## Warning: package 'scales' was built under R version 4.3.2
## Warning: package 'purrr' was built under R version 4.3.3
## Warning: package 'tibble' was built under R version 4.3.2
## Warning: package 'tidyr' was built under R version 4.3.3
## Warning: package 'tune' was built under R version 4.3.3
## Warning: package 'workflowsets' was built under R version 4.3.3
## Warning: package 'yardstick' was built under R version 4.3.3
## ── Conflicts ───────────────────────────────────────── tidymodels_conflicts() ──
## ✖ purrr::discard()  masks scales::discard()
## ✖ dplyr::filter()   masks stats::filter()
## ✖ dplyr::lag()      masks stats::lag()
## ✖ yardstick::spec() masks readr::spec()
## ✖ recipes::step()   masks stats::step()
library(shiny)
## Warning: package 'shiny' was built under R version 4.3.3
## 
## Attaching package: 'shiny'
## The following object is masked from 'package:infer':
## 
##     observe
library(glue)
library(DT)
## Warning: package 'DT' was built under R version 4.3.3
## 
## Attaching package: 'DT'
## The following objects are masked from 'package:shiny':
## 
##     dataTableOutput, renderDataTable
library(purrr)

set.seed(123)

3 Data

path <- "C:\\Users\\ASUS\\Downloads\\breast+cancer+wisconsin+diagnostic\\wdbc.data"
bc <- read.csv(path, header = FALSE)

colnames(bc) <- c(
  "id", "diagnosis",
  "radius_mean", "texture_mean", "perimeter_mean", "area_mean", "smoothness_mean",
  "compactness_mean", "concavity_mean", "concave_points_mean", "symmetry_mean",
  "fractal_dimension_mean",
  
  "radius_se", "texture_se", "perimeter_se", "area_se", "smoothness_se",
  "compactness_se", "concavity_se", "concave_points_se", "symmetry_se",
  "fractal_dimension_se",
  
  "radius_worst", "texture_worst", "perimeter_worst", "area_worst", "smoothness_worst",
  "compactness_worst", "concavity_worst", "concave_points_worst", "symmetry_worst",
  "fractal_dimension_worst"
)

4 Train & Save Model

4.1 Data & target — kolom fitur yang akan dipakai

bc <- bc %>%
  select(
    diagnosis,
    radius_mean,
    texture_mean,
    area_mean,
    smoothness_mean,
    compactness_mean,
    concavity_mean
  )
bc$diagnosis <- factor(bc$diagnosis, levels = c("B", "M"))

4.2 Split data

split <- initial_split(bc, prop = 0.8, strata = diagnosis)
train <- training(split)
test  <- testing(split)

4.3 Recipe preprocessing

rec <- recipe(diagnosis ~ ., data = train) %>%
  step_string2factor(all_nominal_predictors()) %>% 
  step_impute_median(all_numeric_predictors()) %>% 
  step_impute_mode(all_nominal_predictors()) %>% 
  step_novel(all_nominal_predictors()) %>% 
  step_zv(all_predictors()) %>%
  step_dummy(all_nominal_predictors())

4.4 Model Random Forest (ranger)

rf_spec <- rand_forest(
  trees = 500,
  mtry  = NULL,
  min_n = 5
) %>%
  set_engine("ranger", importance = "impurity", probability = TRUE) %>%
  set_mode("classification")

4.5 Workflow = recipe + Model

wf <- workflow() %>%
  add_model(rf_spec) %>%
  add_recipe(rec)
rf_fit <- fit(wf, data = train)

4.6 Simpan Workflow

saveRDS(rf_fit, "D:/SEMESTER 5/MESIN STATISTIK/r/rf_breastcancer_workflow.rds")

#Shiny APp

# 1) LOAD MODEL RANDOM FOREST (WORKFLOW)
rf_wf <- readRDS("D:/SEMESTER 5/MESIN STATISTIK/r/rf_breastcancer_workflow.rds")


# 2) LOAD DATA ASLI UNTUK RANGE INPUT DEFAULT

path <- "C:/Users/ASUS/Downloads/breast+cancer+wisconsin+diagnostic/wdbc.data"

bc_raw <- read.csv(path, header = FALSE)
colnames(bc_raw) <- c(
  "id", "diagnosis",
  "radius_mean", "texture_mean", "perimeter_mean", "area_mean", "smoothness_mean",
  "compactness_mean", "concavity_mean", "concave_points_mean", "symmetry_mean", 
  "fractal_dimension_mean",
  "radius_se", "texture_se", "perimeter_se", "area_se", "smoothness_se",
  "compactness_se", "concavity_se", "concave_points_se", "symmetry_se",
  "fractal_dimension_se",
  "radius_worst", "texture_worst", "perimeter_worst", "area_worst",
  "smoothness_worst", "compactness_worst", "concavity_worst",
  "concave_points_worst", "symmetry_worst", "fractal_dimension_worst"
)

bc <- bc_raw %>% 
  select(
    diagnosis, radius_mean, texture_mean, area_mean,
    smoothness_mean, compactness_mean, concavity_mean
  )

num_summary <- bc %>% summarise(
  across(where(is.numeric), list(
    min = ~min(., na.rm = TRUE),
    max = ~max(., na.rm = TRUE),
    med = ~median(., na.rm = TRUE)
  ))
)

med <- function(nm) as.numeric(num_summary[[paste0(nm, "_med")]])

required_cols <- c(
  "radius_mean","texture_mean","area_mean",
  "smoothness_mean","compactness_mean","concavity_mean"
)

# 3) UI
ui <- fluidPage(
  titlePanel("Breast Cancer Diagnosis Prediction (Random Forest)"),
  tags$small("Model sudah dilatih & disimpan. Aplikasi ini hanya untuk prediksi."),
  br(),
  
  tabsetPanel(
    #  SINGLE PREDICTION 
    tabPanel(
      "Single Prediction",
      sidebarLayout(
        sidebarPanel(
          numericInput("radius_mean", "Radius Mean", value = med("radius_mean")),
          numericInput("texture_mean", "Texture Mean", value = med("texture_mean")),
          numericInput("area_mean", "Area Mean", value = med("area_mean")),
          numericInput("smoothness_mean", "Smoothness", value = med("smoothness_mean")),
          numericInput("compactness_mean", "Compactness", value = med("compactness_mean")),
          numericInput("concavity_mean", "Concavity", value = med("concavity_mean")),
          actionButton("predict_one", "Prediksi")
        ),
        
        mainPanel(
          h4("Hasil Prediksi Diagnosis"),
          verbatimTextOutput("single_pred"),
          DTOutput("single_proba")
        )
      )
    ),
    
    #BATCH PREDICTION 
    tabPanel(
      "Batch Prediction (CSV)",
      fileInput("csv_file", "Upload CSV tanpa kolom diagnosis", accept = ".csv"),
      br(),
      DTOutput("batch_table"),
      br(),
      downloadButton("download_preds", "Unduh Hasil Prediksi CSV")
    )
  )
)


# 4) SERVER
server <- function(input, output, session) {
  
  # SINGLE PREDICTION 
  observeEvent(input$predict_one, {
    
    newdat <- tibble(
      radius_mean     = input$radius_mean,
      texture_mean    = input$texture_mean,
      area_mean       = input$area_mean,
      smoothness_mean = input$smoothness_mean,
      compactness_mean = input$compactness_mean,
      concavity_mean  = input$concavity_mean
    )
    
    cls <- predict(rf_wf, new_data = newdat, type = "class")
    prb <- predict(rf_wf, new_data = newdat, type = "prob")
    
    pred_class <- cls$.pred_class[[1]]
    
    output$single_pred <- renderText({
      glue("Prediksi diagnosis: {pred_class}")
    })
    
    output$single_proba <- renderDT({
      datatable(prb, options = list(dom = "t"))
    })
  })
  
  #  BATCH PREDICTION 
  batch_data <- reactive({
    req(input$csv_file)
    
    df_raw <- readr::read_csv(input$csv_file$datapath, show_col_types = FALSE)
    
    # cek kolom wajib
    missing <- setdiff(required_cols, names(df_raw))
    validate(need(length(missing) == 0,
                  paste("Kolom wajib:", paste(missing, collapse = ", "))))
    
    df <- df_raw %>% mutate(across(all_of(required_cols), as.numeric))
    df
  })
  
  preds_df <- reactive({
    df  <- batch_data()
    cls <- predict(rf_wf, new_data = df, type = "class")
    prb <- predict(rf_wf, new_data = df, type = "prob")
    
    bind_cols(df, tibble(pred_diagnosis = cls$.pred_class), prb)
  })
  
  output$batch_table <- renderDT({
    req(preds_df())
    datatable(preds_df(), options = list(pageLength = 10))
  })
  
  output$download_preds <- downloadHandler(
    filename = function() paste0("prediksi_breastcancer_", format(Sys.time(), "%Y%m%d_%H%M%S"), ".csv"),
    content = function(file){
      write_csv(preds_df(), file)
    }
  )
}

# 5) AKTIFKAN SHINY
shinyApp(ui, server)
## PhantomJS not found. You can install it with webshot::install_phantomjs(). If it is installed, please make sure the phantomjs executable can be found via the PATH variable.
Shiny applications not supported in static R Markdown documents