library(tidyverse)── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.2.1 ✔ readr 2.2.0
✔ forcats 1.0.1 ✔ stringr 1.6.0
✔ ggplot2 4.0.3 ✔ tibble 3.3.1
✔ lubridate 1.9.5 ✔ tidyr 1.3.2
✔ purrr 1.2.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(caret)Loading required package: lattice
Attaching package: 'caret'
The following object is masked from 'package:purrr':
lift
library(knitr)
# 1. Load Data
train_raw <- read.csv("C:/Users/aidan/Downloads/insurance-training-data2.csv")
test_raw <- read.csv("C:/Users/aidan/Downloads/insurance-testing-data2.csv")
# 2. Basic Data Cleaning & Imputation
clean_data <- function(df) {
df <- df %>%
mutate(
INCOME = as.numeric(gsub("[\\$,]", "", INCOME)),
HOME_VAL = as.numeric(gsub("[\\$,]", "", HOME_VAL)),
BLUEBOOK = as.numeric(gsub("[\\$,]", "", BLUEBOOK)),
OLDCLAIM = as.numeric(gsub("[\\$,]", "", OLDCLAIM)),
REVOKED = ifelse(REVOKED == "Yes", 1, 0),
URBANICITY = ifelse(grepl("Highly Urban", URBANICITY), 1, 0)
)
num_cols <- c("AGE", "YOJ", "INCOME", "HOME_VAL", "BLUEBOOK", "CAR_AGE")
for (col in num_cols) {
if (col %in% colnames(df)) {
df[[col]][is.na(df[[col]])] <- median(df[[col]], na.rm = TRUE)
}
}
return(df)
}
train <- clean_data(train_raw)
test <- clean_data(test_raw)
# 3. Fit Logistic Model on Training Data
logit_model <- glm(TARGET_FLAG ~ AGE + KIDSDRIV + MVR_PTS + REVOKED +
TIF + TRAVTIME + URBANICITY + BLUEBOOK + INCOME,
data = train, family = binomial(link = "logit"))
# 4. Internal Validation Split (to generate Confusion Matrix & F1)
set.seed(42)
train_idx <- createDataPartition(train$TARGET_FLAG, p = 0.8, list = FALSE)
val_train <- train[train_idx, ]
val_test <- train[-train_idx, ]
best_logit <- glm(formula(logit_model), data = val_train, family = binomial)
val_probs <- predict(best_logit, newdata = val_test, type = "response")
val_preds <- factor(ifelse(val_probs > 0.5, 1, 0), levels = c(0, 1))
val_actual <- factor(val_test$TARGET_FLAG, levels = c(0, 1))
# 5. Create Confusion Matrix & Report F1 Statistic
cm <- confusionMatrix(val_preds, val_actual, positive = "1")
print(cm)Confusion Matrix and Statistics
Reference
Prediction 0 1
0 884 262
1 64 95
Accuracy : 0.7502
95% CI : (0.7258, 0.7735)
No Information Rate : 0.7264
P-Value [Acc > NIR] : 0.02828
Kappa : 0.2401
Mcnemar's Test P-Value : < 2e-16
Sensitivity : 0.2661
Specificity : 0.9325
Pos Pred Value : 0.5975
Neg Pred Value : 0.7714
Prevalence : 0.2736
Detection Rate : 0.0728
Detection Prevalence : 0.1218
Balanced Accuracy : 0.5993
'Positive' Class : 1
f1_score <- cm$byClass["F1"]
cat("\nF1 Score for Best Model:", round(f1_score, 4), "\n")
F1 Score for Best Model: 0.3682
# 6. Predict onto Test Data & Save Results
test$P_TARGET_FLAG <- predict(logit_model, newdata = test, type = "response")
test$TARGET_FLAG <- ifelse(test$P_TARGET_FLAG > 0.5, 1, 0)
# Save CSV File
predictions_summary <- test %>% select(INDEX, P_TARGET_FLAG, TARGET_FLAG)
write.csv(predictions_summary, "Evaluation_Dataset_Predictions.csv", row.names = FALSE)
# Display Table in Rendered Output
kable(head(predictions_summary, 10), caption = "First 10 Predictions")| INDEX | P_TARGET_FLAG | TARGET_FLAG |
|---|---|---|
| 5 | 0.1835118 | 0 |
| 8 | 0.3084215 | 0 |
| 26 | 0.4148967 | 0 |
| 40 | 0.3020215 | 0 |
| 45 | 0.0917245 | 0 |
| 55 | 0.4297917 | 0 |
| 61 | 0.8782225 | 1 |
| 66 | 0.1269640 | 0 |
| 67 | 0.4491373 | 0 |
| 71 | 0.3036976 | 0 |
# 1. Convert Confusion Matrix stats into a readable table
metrics_df <- data.frame(
Metric = c("Accuracy", "Sensitivity (Recall)", "Specificity", "F1 Score", "Balanced Accuracy"),
Value = c(
round(cm$overall["Accuracy"], 4),
round(cm$byClass["Sensitivity"], 4),
round(cm$byClass["Specificity"], 4),
round(cm$byClass["F1"], 4),
round(cm$byClass["Balanced Accuracy"], 4)
)
)
# Display Metrics Table
kable(metrics_df, caption = "Model Classification Performance Metrics")| Metric | Value | |
|---|---|---|
| Accuracy | Accuracy | 0.7502 |
| Sensitivity | Sensitivity (Recall) | 0.2661 |
| Specificity | Specificity | 0.9325 |
| F1 | F1 Score | 0.3682 |
| Balanced Accuracy | Balanced Accuracy | 0.5993 |
# 2. Display Predictions Table
kable(head(predictions_summary, 10), caption = "First 10 Evaluation Predictions")| INDEX | P_TARGET_FLAG | TARGET_FLAG |
|---|---|---|
| 5 | 0.1835118 | 0 |
| 8 | 0.3084215 | 0 |
| 26 | 0.4148967 | 0 |
| 40 | 0.3020215 | 0 |
| 45 | 0.0917245 | 0 |
| 55 | 0.4297917 | 0 |
| 61 | 0.8782225 | 1 |
| 66 | 0.1269640 | 0 |
| 67 | 0.4491373 | 0 |
| 71 | 0.3036976 | 0 |