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.0     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.2     ✔ 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
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.

model_diabetes <- glm(Outcome ~ Glucose + BMI + Age, 
                     data = data, 
                     family = "binomial")

# Provide the model summary
summary(model_diabetes)
## 
## Call:
## glm(formula = Outcome ~ Glucose + BMI + Age, family = "binomial", 
##     data = data)
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -9.032377   0.711037 -12.703  < 2e-16 ***
## Glucose      0.035548   0.003481  10.212  < 2e-16 ***
## BMI          0.089753   0.014377   6.243  4.3e-10 ***
## Age          0.028699   0.007809   3.675 0.000238 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 974.75  on 751  degrees of freedom
## Residual deviance: 724.96  on 748  degrees of freedom
##   (16 observations deleted due to missingness)
## AIC: 732.96
## 
## Number of Fisher Scoring iterations: 4
# Calculate and interpret R2
R2_log <- 1 - (model_diabetes$deviance / model_diabetes$null.deviance)
print(paste("Pseudo R-squared:", round(R2_log, 4)))
## [1] "Pseudo R-squared: 0.2563"
## Enter your code here

What does the intercept represent (log-odds of diabetes when predictors are zero)? The intercept represents the log-odds of having diabetes when Glucose, BMI, and Age are all zero. In practical terms, this is a baseline mathematical constant, as a person cannot have a BMI or Glucose level of zero.

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)? Glucose, BMI, and Age: For each of these predictors, a one-unit increase raises the log-odds of having diabetes because their coefficients are positive.Significance: All three predictors are highly significant (p-value < 0.05), meaning they are all strong statistical indicators for predicting diabetes in this dataset.

Question 2: Confusion Matrix and Important Metric

# 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)


# 1. Predict probabilities
probabilities <- predict(model_diabetes, newdata = data_subset, type = "response")

# 2. Create predicted classes (threshold 0.5)
predictions <- ifelse(probabilities > 0.5, 1, 0)

# 3. Build a confusion matrix
conf_matrix <- table(Predicted = predictions, Actual = data_subset$Outcome_num)
print(conf_matrix)
##          Actual
## Predicted   0   1
##         0 429 114
##         1  59 150
# 4. Extract values and calculate metrics
TN <- conf_matrix[1,1]
FP <- conf_matrix[1,2]
FN <- conf_matrix[2,1]
TP <- conf_matrix[2,2]

accuracy <- (TP + TN) / sum(conf_matrix)
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.77 
## Sensitivity: 0.718 
## Specificity: 0.79 
## Precision: 0.568

Calculate and report the metrics:

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

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? For medical diagnosis, Sensitivity is usually more important because it measures the model’s ability to correctly identify people with diabetes. A “False Negative” (missing a case of diabetes) is much more dangerous than a “False Positive” (an extra test for someone healthy).

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
# Generate ROC curve
roc_score <- roc(data_subset$Outcome_num, probabilities)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
# Plot the curve
plot(roc_score, main ="ROC Curve for Diabetes Prediction", col = "blue")

# Calculate AUC
auc_val <- auc(roc_score)
print(paste("AUC:", round(auc_val, 4)))
## [1] "AUC: 0.828"

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

The AUC (Area Under the Curve) indicates the model’s ability to distinguish between the two classes. An AUC of 0.5 means the model is guessing randomly, while 1.0 is perfect. The result likely between shows the model has good discriminatory power.

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

To prioritize Sensitivity (catching more cases), we should lower the threshold. This makes the model quicker to flag someone as having diabetes, ensuring fewer cases are missed, even if it slightly increases the number of false alarms.