Introduction:

In this homework, you will apply logistic regression to a real-world dataset: the Pima Indians Diabetes Database. This dataset contains medical records from 768 women of Pima Indian heritage, aged 21 or older, and is used to predict the onset of diabetes (binary outcome: 0 = no diabetes, 1 = diabetes) based on physiological measurements.

The data is publicly available from the UCI Machine Learning Repository and can be imported directly.

Dataset URL: https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv

Columns (no header in the CSV, so we need to assign them manually):

  1. Pregnancies: Number of times pregnant
  2. Glucose: Plasma glucose concentration (2-hour test)
  3. BloodPressure: Diastolic blood pressure (mm Hg)
  4. SkinThickness: Triceps skin fold thickness (mm)
  5. Insulin: 2-hour serum insulin (mu U/ml)
  6. BMI: Body mass index (weight in kg/(height in m)^2)
  7. DiabetesPedigreeFunction: Diabetes pedigree function (a function scoring genetic risk)
  8. Age: Age in years
  9. Outcome: Class variable (0 = no diabetes, 1 = diabetes)

Task Overview: You will load the data, build a logistic regression model to predict diabetes onset using a subset of predictors (Glucose, BMI, Age), interpret the model, evaluate it with a confusion matrix and metrics, and analyze the ROC curve and AUC.

Cleaning the dataset Don’t change the following code

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.1     
## ── 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(ggplot2)
library(cowplot)
## 
## Attaching package: 'cowplot'
## 
## The following object is masked from 'package:lubridate':
## 
##     stamp
url <- "https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv"

data <- read.csv(url, header = FALSE)

colnames(data) <- c("Pregnancies", "Glucose", "BloodPressure", "SkinThickness", "Insulin", "BMI", "DiabetesPedigreeFunction", "Age", "Outcome")

data$Outcome <- as.factor(data$Outcome)

# Handle missing values (replace 0s with NA because 0 makes no sense here)
data$Glucose[data$Glucose == 0] <- NA
data$BloodPressure[data$BloodPressure == 0] <- NA
data$BMI[data$BMI == 0] <- NA


colSums(is.na(data))
##              Pregnancies                  Glucose            BloodPressure 
##                        0                        5                       35 
##            SkinThickness                  Insulin                      BMI 
##                        0                        0                       11 
## DiabetesPedigreeFunction                      Age                  Outcome 
##                        0                        0                        0

Question 1: Create and Interpret a Logistic Regression Model - Fit a logistic regression model to predict Outcome using Glucose, BMI, and Age.

## Enter your code here

Model <- glm(Glucose ~ BMI + Age, data = data)

summary(Model)
## 
## Call:
## glm(formula = Glucose ~ BMI + Age, data = data)
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  67.0644     5.7889  11.585  < 2e-16 ***
## BMI           1.0029     0.1514   6.623 6.70e-11 ***
## Age           0.6702     0.0896   7.480 2.09e-13 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for gaussian family taken to be 826.3349)
## 
##     Null deviance: 703261  on 751  degrees of freedom
## Residual deviance: 618925  on 749  degrees of freedom
##   (16 observations deleted due to missingness)
## AIC: 7190.3
## 
## Number of Fisher Scoring iterations: 2
r_square <- 1- (Model$deviance / Model$null.deviance)

r_square
## [1] 0.1199221

What does the intercept represent (log-odds of diabetes when predictors are zero)? It represents the log odds of someone having diabetes.

For each predictor (Glucose, BMI, Age), does a one-unit increase raise or lower the odds of diabetes? Are they significant (p-value < 0.05)? a one unit increase in the predictors raises the odds of having diabetes. They are significant at the .05 level.

##R2: This model explains about 12 percent of the variations in diabetes onset.

Question 2: Confusion Matrix and Important Metric

Calculate and report the metrics:

Accuracy: (TP + TN) / Total Sensitivity (Recall): TP / (TP + FN) Specificity: TN / (TN + FP) Precision: TP / (TP + FP)

Use the following starter code

# Keep only rows with no missing values in Glucose, BMI, or Age
data_subset <- data[complete.cases(data[, c("Glucose", "BMI", "Age")]), ]

#Create a numeric version of the outcome (0 = no diabetes, 1 = diabetes).This is required for calculating confusion matrices.
data_subset$Outcome_num <- ifelse(data_subset$Outcome == "1", 1, 0)


# Predicted probabilities
Prob.Prediction <- Model$fitted.values


# Predicted classes
Class.Prediction <- ifelse(Prob.Prediction > 0.5, 1, 0)


# Confusion matrix
Conf_Matrix <- table(
  Predicted = factor(Class.Prediction, levels = c(0, 1)),
  Actual = factor(data_subset$Outcome_num, levels = c(0, 1))
)
  
Conf_Matrix
##          Actual
## Predicted   0   1
##         0   0   0
##         1 488 264
#Extract Values:
TN <-0
FP <-488 
FN <-0 
TP <-264 

#Metrics    
accuracy <- (TP + TN) / (TP + TN + FP + FN)
sensitivity <- TP / (TP + FN) 
specificity <- TN / (TN + FP)
precision <- TP / (TP + FP)

cat("Accuracy:", round(accuracy, 3), "\nSensitivity:", round(sensitivity, 3), "\nSpecificity:", round(specificity, 3), "\nPrecision:", round(precision, 3))
## Accuracy: 0.351 
## Sensitivity: 1 
## Specificity: 0 
## Precision: 0.351

Interpret: How well does the model perform? Is it better at detecting diabetes (sensitivity) or non-diabetes (specificity)? Why might this matter for medical diagnosis? This model does not perform well, It catches 100% of diabetic cases but this is because it doesn’t classify anyone as non diabetic. This matters because it is over predicting the number of people with diabetes which could lead to incorrect diagnosis and wasted time and resources on the part of doctors figuring out who is truly diabetic and who isn’t.

Question 3: ROC Curve, AUC, and Interpretation

#Enter your code here
library(pROC)
## Type 'citation("pROC")' for a citation.
## 
## Attaching package: 'pROC'
## The following objects are masked from 'package:stats':
## 
##     cov, smooth, var
ROC_CRV <- roc(response = data_subset$Outcome_num,
               predictor = Model$fitted.values)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
ROC_CRV
## 
## Call:
## roc.default(response = data_subset$Outcome_num, predictor = Model$fitted.values)
## 
## Data: Model$fitted.values in 488 controls (data_subset$Outcome_num 0) < 264 cases (data_subset$Outcome_num 1).
## Area under the curve: 0.7332
AUC_VAL <- auc(ROC_CRV); AUC_VAL
## Area under the curve: 0.7332
plot.roc(ROC_CRV, print.auc = TRUE, legacy.axes = TRUE,
         xlab = "False positive rate",
         ylab = "True Positive Rate")

What does AUC indicate (0.5 = random, 1.0 = perfect)?

The AUC is 0.7332 which means the model has some predicting power.

For diabetes diagnosis, prioritize sensitivity (catching cases) or specificity (avoiding false positives)? Suggest a threshold and explain.

It is better to prioritize sensitivity because there is more down side to missing a case than there is to falsely classifying someone as a diabetic. If some one is falsely classified as a diabetic the doctors can do some tests to figure out whether they truly are or are not diabetic. While if a person who is diabetic is not properly classified as such their condition can worsen over time before they correctly diagnose this person.