---
title: "Lecture 5: Classification"
output: html_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

## Load Libraries

```{r}
library(tidyverse)
library(shiny)
library(class)
```

## Data Preparation

### Read in CORIS data

```{r}
coris = as_tibble(read.csv("~/Downloads/coris.csv")) %>% 
  select(-row.names) %>%
  mutate(across(-chd, ~ as.numeric(scale(.))), chd = (chd != 0))
chd = coris$chd
```

### Train/Test Separation

```{r}
set.seed(12345)
train.ind = sample(1:nrow(coris), nrow(coris) * 0.5)
coris.train = coris[train.ind,]
coris.test = coris[-train.ind,]
```

## KNN Model

### Ranging over k and L, run KNN

```{r}
knn.err <- map_dfr(c(1,2,3,4,seq(5, nrow(coris.train), 5)), function(K) {
  # Run KNN 
  knn.raw <- knn(train = coris.train,
                 test = coris.test,
                 cl = coris.train$chd,
                 k = K, l = 0,
                 use.all = TRUE,
                 prob = TRUE)
  
  # Get the vote shares that the majority possessed
  knn.probs <- map_dbl(1:length(knn.raw), function(i) {
    if (knn.raw[i] == TRUE) {
      return(attributes(knn.raw)$prob[i])
    } else {
      return(1 - attributes(knn.raw)$prob[i])
    }
  })
  
  # Compute the TP, FP, FN, TN from the model on the test data
  confusion <- map_dfr(seq(0, 1.05, 0.01), function(thresh) {
    L <- thresh * K / 100
    knn.out <- as.numeric(knn.probs >= thresh)
    TP <- sum(coris.test$chd * knn.out)
    FP <- sum((1 - coris.test$chd) * knn.out)
    FN <- sum(coris.test$chd * (1 - knn.out))
    TN <- sum((1 - coris.test$chd) * (1 - knn.out))
    tibble(K, L, TP, FP, TN, FN, thresh)
  })
  
  return(confusion)
})
```

### Compute accuracy, TPR, FPR

```{r}
knn.err$accuracy = (knn.err$TP + knn.err$TN)/nrow(coris.test)
knn.err$TPR = (knn.err$TP)/(knn.err$TP + knn.err$FN)
knn.err$FPR = (knn.err$FP)/(knn.err$FP + knn.err$TN)
```

### Plot the ROC curve for K = 20

```{r}
knn.err.ksubset = subset(knn.err, K == 20)
ggplot(data = knn.err.ksubset, aes(x = FPR, y = TPR)) + 
  geom_line(size = 1.5, alpha = 0.5) + geom_point(aes(size = thresh))
```

## R Shiny App to Explore Role of Threshold

### User Interface (UI)

```{r}
ui <- fluidPage(
  titlePanel("Average 0-1 Loss on Test Set with Varying Threshold"),
  mainPanel(
    sliderInput("thresh_slider", 
                  "Threshold:",
                  min = 0,
                  max = 1.05,
                  value = 0.35,
                  step = 0.01,
                  ticks = FALSE),
    plotOutput("loss_plot")
  )
)
```

### Server Logic

```{r}
server <- function(input, output) {
  
  # This renders the plot based on user input
  output$loss_plot <- renderPlot({
    req(input$thresh_slider) # Make sure threshold value is available
    
    knn.err.subset <- knn.err %>%
      filter(abs(thresh - input$thresh_slider) < 1e-9)
    
    ggplot(data = knn.err.subset, aes(x = K, y = 1- accuracy)) + 
      geom_line() +
      ylim(0,0.75) + 
      ylab("Average 0-1 Loss on Test Set")
  })
}
```

### Run the shiny app

```{r, eval=FALSE}
shinyApp(ui = ui, server = server)
```

Note: The Shiny App block is marked as `eval=FALSE` to prevent execution during knitting. If you want to run the app, you'll need to execute this block manually in RStudio.
