Type 2 diabetes is a major public health concern, and identifying the clinical factors most strongly associated with it is a routine but essential exercise in medical statistics. This report analyzes the Pima Indians Diabetes Dataset (768 female patients, 9 variables) to identify which clinical measurements are independently associated with a diabetes diagnosis.
Research question: Which clinical variables (glucose, BMI, age, blood pressure, insulin) are independently associated with diabetes status in this population?
mydata <- read.csv("diabetes.csv")
str(mydata)
## 'data.frame': 768 obs. of 9 variables:
## $ Pregnancies : int 6 1 8 1 0 5 3 10 2 8 ...
## $ Glucose : int 148 85 183 89 137 116 78 115 197 125 ...
## $ BloodPressure : int 72 66 64 66 40 74 50 0 70 96 ...
## $ SkinThickness : int 35 29 0 23 35 0 32 0 45 0 ...
## $ Insulin : int 0 0 0 94 168 0 88 0 543 0 ...
## $ BMI : num 33.6 26.6 23.3 28.1 43.1 25.6 31 35.3 30.5 0 ...
## $ DiabetesPedigreeFunction: num 0.627 0.351 0.672 0.167 2.288 ...
## $ Age : int 50 31 32 21 33 30 26 29 53 54 ...
## $ Outcome : int 1 0 1 0 1 0 1 0 1 1 ...
The dataset contains 768 observations and 9 variables, all numeric.
The outcome variable, Outcome, is binary (1 = diabetic, 0 =
non-diabetic).
Several variables (Glucose, BloodPressure,
SkinThickness, Insulin, BMI)
contained biologically implausible zero values, which almost certainly
represent missing data recorded as 0 rather than NA. These
were recoded as missing prior to analysis.
mydata$Glucose[mydata$Glucose == 0] <- NA
mydata$BloodPressure[mydata$BloodPressure == 0] <- NA
mydata$SkinThickness[mydata$SkinThickness == 0] <- NA
mydata$Insulin[mydata$Insulin == 0] <- NA
mydata$BMI[mydata$BMI == 0] <- NA
summary(mydata)
## Pregnancies Glucose BloodPressure SkinThickness
## Min. : 0.000 Min. : 44.0 Min. : 24.00 Min. : 7.00
## 1st Qu.: 1.000 1st Qu.: 99.0 1st Qu.: 64.00 1st Qu.:22.00
## Median : 3.000 Median :117.0 Median : 72.00 Median :29.00
## Mean : 3.845 Mean :121.7 Mean : 72.41 Mean :29.15
## 3rd Qu.: 6.000 3rd Qu.:141.0 3rd Qu.: 80.00 3rd Qu.:36.00
## Max. :17.000 Max. :199.0 Max. :122.00 Max. :99.00
## NAs :5 NAs :35 NAs :227
## Insulin BMI DiabetesPedigreeFunction Age
## Min. : 14.00 Min. :18.20 Min. :0.0780 Min. :21.00
## 1st Qu.: 76.25 1st Qu.:27.50 1st Qu.:0.2437 1st Qu.:24.00
## Median :125.00 Median :32.30 Median :0.3725 Median :29.00
## Mean :155.55 Mean :32.46 Mean :0.4719 Mean :33.24
## 3rd Qu.:190.00 3rd Qu.:36.60 3rd Qu.:0.6262 3rd Qu.:41.00
## Max. :846.00 Max. :67.10 Max. :2.4200 Max. :81.00
## NAs :374 NAs :11
## Outcome
## Min. :0.000
## 1st Qu.:0.000
## Median :0.000
## Mean :0.349
## 3rd Qu.:1.000
## Max. :1.000
##
Note that Insulin retained a very high proportion of
missing values (374 of 768, ~49%) even after cleaning, which informed a
modeling decision made later in this report.
Before selecting statistical tests, the Shapiro-Wilk test was used to assess normality for all numeric variables.
numeric_cols <- c("Pregnancies", "Glucose", "BloodPressure", "SkinThickness",
"Insulin", "BMI", "DiabetesPedigreeFunction", "Age")
for (col in numeric_cols) {
result <- shapiro.test(mydata[[col]])
cat(col, ": p-value =", format(result$p.value, scientific = TRUE), "\n")
}
## Pregnancies : p-value = 1.609257e-21
## Glucose : p-value = 1.720326e-11
## BloodPressure : p-value = 9.45138e-05
## SkinThickness : p-value = 1.775691e-09
## Insulin : p-value = 1.698218e-21
## BMI : p-value = 8.557785e-09
## DiabetesPedigreeFunction : p-value = 2.477506e-27
## Age : p-value = 2.402274e-24
All variables significantly deviated from a normal distribution (p < 0.05 in every case). Consequently, non-parametric tests (Mann-Whitney U) and median (rather than mean) summaries were used throughout this analysis.
Each candidate predictor was first compared between diabetic and non-diabetic patients using the Mann-Whitney U test.
wilcox.test(Glucose ~ Outcome, data = mydata)
##
## Wilcoxon rank sum test with continuity correction
##
## data: Glucose by Outcome
## W = 27394, p-value < 2.2e-16
## alternative hypothesis: true location shift is not equal to 0
tapply(mydata$Glucose, mydata$Outcome, median, na.rm = TRUE)
## 0 1
## 107 140
wilcox.test(BMI ~ Outcome, data = mydata)
##
## Wilcoxon rank sum test with continuity correction
##
## data: BMI by Outcome
## W = 40875, p-value < 2.2e-16
## alternative hypothesis: true location shift is not equal to 0
tapply(mydata$BMI, mydata$Outcome, median, na.rm = TRUE)
## 0 1
## 30.1 34.3
wilcox.test(Age ~ Outcome, data = mydata)
##
## Wilcoxon rank sum test with continuity correction
##
## data: Age by Outcome
## W = 41950, p-value < 2.2e-16
## alternative hypothesis: true location shift is not equal to 0
tapply(mydata$Age, mydata$Outcome, median, na.rm = TRUE)
## 0 1
## 27 36
wilcox.test(BloodPressure ~ Outcome, data = mydata)
##
## Wilcoxon rank sum test with continuity correction
##
## data: BloodPressure by Outcome
## W = 47567, p-value = 1.629e-06
## alternative hypothesis: true location shift is not equal to 0
tapply(mydata$BloodPressure, mydata$Outcome, median, na.rm = TRUE)
## 0 1
## 70.0 74.5
wilcox.test(Insulin ~ Outcome, data = mydata)
##
## Wilcoxon rank sum test with continuity correction
##
## data: Insulin by Outcome
## W = 9210.5, p-value = 7.477e-14
## alternative hypothesis: true location shift is not equal to 0
tapply(mydata$Insulin, mydata$Outcome, median, na.rm = TRUE)
## 0 1
## 102.5 169.5
| Variable | Median (Non-diabetic) | Median (Diabetic) | p-value |
|---|---|---|---|
| Glucose | 107 | 140 | < 0.001 |
| BMI | 30.1 | 34.3 | < 0.001 |
| Age | 27 | 36 | < 0.001 |
| Blood Pressure | 70.0 | 74.5 | < 0.001 |
| Insulin | 102.5 | 169.5 | < 0.001 |
All five variables were significantly higher in the diabetic group at the univariate level.
To identify which variables remained independently associated with diabetes after adjusting for one another, a multivariable logistic regression model was built.
model <- glm(Outcome ~ Glucose + BMI + Age + BloodPressure + Insulin,
data = mydata, family = binomial)
summary(model)
##
## Call:
## glm(formula = Outcome ~ Glucose + BMI + Age + BloodPressure +
## Insulin, family = binomial, data = mydata)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -9.7208551 1.1674617 -8.326 < 2e-16 ***
## Glucose 0.0379725 0.0057177 6.641 3.11e-11 ***
## BMI 0.0812731 0.0212529 3.824 0.000131 ***
## Age 0.0550397 0.0138266 3.981 6.87e-05 ***
## BloodPressure -0.0027008 0.0114185 -0.237 0.813025
## Insulin -0.0007491 0.0012984 -0.577 0.563979
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 498.10 on 391 degrees of freedom
## Residual deviance: 353.99 on 386 degrees of freedom
## (376 observations deleted due to missingness)
## AIC: 365.99
##
## Number of Fisher Scoring iterations: 5
This model retained only 392 of 768 observations, because
Insulin alone carried 374 missing values, and
glm() excludes any row with a missing value in any
included predictor (listwise deletion). In this model,
Insulin and BloodPressure were not
statistically significant.
Since Insulin was not significant and was responsible
for the majority of missing data, it was removed to preserve sample
size.
model2 <- glm(Outcome ~ Glucose + BMI + Age + BloodPressure,
data = mydata, family = binomial)
summary(model2)
##
## Call:
## glm(formula = Outcome ~ Glucose + BMI + Age + BloodPressure,
## family = binomial, data = mydata)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -8.673701 0.795070 -10.909 < 2e-16 ***
## Glucose 0.034909 0.003523 9.910 < 2e-16 ***
## BMI 0.092529 0.015315 6.042 1.52e-09 ***
## Age 0.034352 0.008418 4.081 4.49e-05 ***
## BloodPressure -0.008317 0.008422 -0.988 0.323
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 931.94 on 723 degrees of freedom
## Residual deviance: 695.17 on 719 degrees of freedom
## (44 observations deleted due to missingness)
## AIC: 705.17
##
## Number of Fisher Scoring iterations: 4
Removing Insulin restored the usable sample to 724
observations. BloodPressure remained non-significant (p =
0.323) even without Insulin in the model, indicating its
univariate association was likely confounded by glucose, BMI, and age
rather than reflecting an independent effect.
BloodPressure was subsequently removed, yielding a
final, parsimonious three-predictor model.
model_final <- glm(Outcome ~ Glucose + BMI + Age,
data = mydata, family = binomial)
summary(model_final)
##
## Call:
## glm(formula = Outcome ~ Glucose + BMI + Age, family = binomial,
## data = mydata)
##
## 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
exp(coef(model_final))
## (Intercept) Glucose BMI Age
## 0.0001194781 1.0361873320 1.0939041273 1.0291149212
exp(confint(model_final))
## 2.5 % 97.5 %
## (Intercept) 2.820409e-05 0.0004596464
## Glucose 1.029326e+00 1.0434890533
## BMI 1.064055e+00 1.1258354595
## Age 1.013524e+00 1.0450856242
This final model used 752 of 768 observations (98%).
| Variable | OR | 95% CI | p-value |
|---|---|---|---|
| Glucose | 1.036 | 1.029 – 1.043 | < 0.001 |
| BMI | 1.094 | 1.064 – 1.126 | < 0.001 |
| Age | 1.029 | 1.014 – 1.045 | < 0.001 |
or_data <- data.frame(
Variable = c("Glucose", "BMI", "Age"),
OR = c(1.036, 1.094, 1.029),
Lower = c(1.029, 1.064, 1.014),
Upper = c(1.043, 1.126, 1.045)
)
ggplot(or_data, aes(x = OR, y = Variable)) +
geom_point(size = 3, color = "steelblue") +
geom_errorbar(aes(xmin = Lower, xmax = Upper), width = 0.2, color = "steelblue") +
geom_vline(xintercept = 1, linetype = "dashed", color = "red") +
labs(title = "Odds Ratios for Diabetes Risk Factors",
x = "Odds Ratio (95% CI)", y = "") +
theme_minimal()
In this analysis of 768 female patients from the Pima Indians Diabetes Dataset, glucose, BMI, and age were each independently and significantly associated with increased odds of diabetes, even after adjusting for one another. Blood pressure and insulin, despite showing significant univariate associations, did not remain independent predictors once glucose, BMI, and age were accounted for — illustrating the difference between a crude and an adjusted association.
In the final multivariable logistic regression model (n = 752), glucose (OR = 1.04, 95% CI: 1.03–1.04), BMI (OR = 1.09, 95% CI: 1.06–1.13), and age (OR = 1.03, 95% CI: 1.01–1.05) were all independently and significantly associated with increased odds of diabetes (all p < 0.001).
Analysis conducted in R (base R + ggplot2). Dataset: Pima Indians Diabetes Database, publicly available via Kaggle.