This analysis explores the predictors of depressive symptoms among older adults using machine learning techniques, including Random Forest and Multiple Regression. (Update from mmanuscript)
# Install required packages
#install.packages("haven")
#install.packages("psych")
#install.packages("ggplot2")
#install.packages("reshape2")
#install.packages("corrplot")
#install.packages("randomForest")
#install.packages("nnet")
#install.packages("pROC")
#install.packages("patchwork")
#install.packages("iml")
#install.packages("grid")
#install.packages("gridExtra")
#install.packages("NeuralNetTools")
#install.packages("png")
#install.packages("themis")
#install.packages("smotefamily")
# Load libraries
library(dplyr)
library(smotefamily)
library(themis)
library(haven)
library(psych)
library(ggplot2)
library(reshape2)
library(corrplot)
library(randomForest)
library(nnet)
library(pROC)
library(grid)
library(gridExtra)
library(NeuralNetTools)
library(png)
library(caret)
The data is imported, cleaned, and variables are transformed or categorized for analysis.
#Load the data
data_ESC <- read_sav("C:/Users/nhshannoniv/Downloads/Choi_10-20-24_EastSouthCentral Respondents only_deleteSpouseVar_2012-2020LBQ+randhrs1992_2018v1 (1).sav")
#Start Data Cleaning
data_ESC <- data.frame(
depresv_symp = data_ESC$R14CESD, # Number of depressive symptoms
diff_day_actt = data_ESC$R14ADLA, # Difficulty in daily activities
age506580 = data_ESC$r14Age506580, # Age categorized into 50, 65, and 80 years
self_rt_help = data_ESC$R14SRHpf, # Self-rated health perception
num_comorbidity = data_ESC$r14comorbidityNopsych, # Number of comorbid conditions excluding psychological disorders
cog_status = data_ESC$r13CogStat3, # Cognitive status (3 levels)
female = data_ESC$female, # Gender indicator (1 = female, 0 = male)
marital_stat = data_ESC$r14MS, # Marital status
num_ppl_in_house = data_ESC$H14HHRES, # Number of people living in the household
livealone = data_ESC$h14Livealone, # Indicator for living alone
yrs_of_edu = data_ESC$RAEDYRS, # Years of education completed
employment_type = data_ESC$r14employment, # Employment type (e.g., full-time, part-time)
non_house_wealth_log = data_ESC$LognohsngW, # Log-transformed non-housing wealth
house_wealth_log = data_ESC$LoghousingW, # Log-transformed housing wealth
income_log = data_ESC$Loghhincome, # Log-transformed household income
insurance = data_ESC$HealthInsu # Health insurance coverage
)
#Additional cleaning
data_ESC$age506580[data_ESC$age506580 == 0] <- NA
data_ESC$num_comorbidity[data_ESC$num_comorbidity %in% c(5, 6, 7)] <- 4
# Transform variables
data_ESC$depresv_symp <- as.numeric(data_ESC$depresv_symp)
data_ESC$depresv_symp <- cut(data_ESC$depresv_symp, breaks = c(-Inf, 2, Inf),
labels = c("0-2 Symptoms", "3+ Symptoms"))
# Convert variables to factors
categorical_vars <- c("depresv_symp", "age506580", "cog_status", "female", "marital_stat",
"livealone", "employment_type", "insurance")
data_ESC[categorical_vars] <- lapply(data_ESC[categorical_vars], as.factor)
#changing the reference categories
data_ESC$marital_stat <- relevel(data_ESC$marital_stat, ref = 2) #Single
data_ESC$employment_type <- relevel(data_ESC$employment_type, ref = 1) #Full time
data_ESC$insurance <- relevel(data_ESC$insurance, ref = 2) #Employer
# Handling missing data
# Pairwise deletion is applied here to exclude rows with any missing values. While this simplifies the analysis,
# it may introduce bias if the excluded data is not missing completely at random. Future work might consider
# using imputation techniques to preserve sample size and reduce potential biases.
data_ESC <- na.omit(data_ESC)
#Splt the data into Training and Test dataset
# Train-Test Split
set.seed(1989)
sample_index <- sample(1:nrow(data_ESC), size = 0.7 * nrow(data_ESC))
train_data <- data_ESC[sample_index, ]
test_data <- data_ESC[-sample_index, ]
# Compute and visualize correlations for numerical predictors
numeric_data <- train_data[, c("yrs_of_edu", "non_house_wealth_log", "house_wealth_log",
"income_log", "num_comorbidity",
"num_ppl_in_house")]
correlation_matrix <- cor(numeric_data, use = "complete.obs")
corrplot(correlation_matrix, method = "color", col = colorRampPalette(c("blue", "white", "red"))(200),
type = "upper", addCoef.col = "black", tl.col = "black", tl.srt = 45,
title = "Correlation Matrix for Training Data")
correlation_matrix
## yrs_of_edu non_house_wealth_log house_wealth_log
## yrs_of_edu 1.0000000 0.29461099 0.21452628
## non_house_wealth_log 0.2946110 1.00000000 0.33065844
## house_wealth_log 0.2145263 0.33065844 1.00000000
## income_log 0.3508956 0.33876979 0.29939443
## num_comorbidity -0.1928991 -0.08071173 -0.06263273
## num_ppl_in_house -0.1309180 -0.06397789 0.01963504
## income_log num_comorbidity num_ppl_in_house
## yrs_of_edu 0.35089557 -0.192899144 -0.130917977
## non_house_wealth_log 0.33876979 -0.080711730 -0.063977887
## house_wealth_log 0.29939443 -0.062632729 0.019635043
## income_log 1.00000000 -0.120470815 -0.022499988
## num_comorbidity -0.12047082 1.000000000 -0.005705693
## num_ppl_in_house -0.02249999 -0.005705693 1.000000000
A Random Forest model is used to predict depressive symptom categories.
# Train Random Forest on training data
rf_model <- randomForest(depresv_symp ~ ., data = train_data, importance = TRUE, ntree = 500)
rf_model
##
## Call:
## randomForest(formula = depresv_symp ~ ., data = train_data, importance = TRUE, ntree = 500)
## Type of random forest: classification
## Number of trees: 500
## No. of variables tried at each split: 3
##
## OOB estimate of error rate: 22.79%
## Confusion matrix:
## 0-2 Symptoms 3+ Symptoms class.error
## 0-2 Symptoms 500 46 0.08424908
## 3+ Symptoms 124 76 0.62000000
# Evaluate performance on test data
rf_predictions <- predict(rf_model, newdata = test_data)
confusion_matrix_rf <- table(Predicted = rf_predictions, Actual = test_data$depresv_symp)
confusion_matrix_rf
## Actual
## Predicted 0-2 Symptoms 3+ Symptoms
## 0-2 Symptoms 232 49
## 3+ Symptoms 14 26
Variable Importance Plot
# Display model summary and plot variable importance
varImpPlot(rf_model, main = "Variable Importance in Predicting Depressive Symptoms (depresv_symp)")
# Interpretation
# The variable importance plot highlights which predictors contribute the most to the model's performance. For example, variables with high importance scores like `yrs_of_edu` or `income_log` indicate a stronger relationship with depressive symptoms. This insight helps to prioritize factors for potential interventions or further study.
varImp(rf_model)
## 0-2 Symptoms 3+ Symptoms
## diff_day_actt 18.52687980 18.52687980
## age506580 5.89468221 5.89468221
## self_rt_help 19.91811770 19.91811770
## num_comorbidity 6.45406990 6.45406990
## cog_status -0.41670847 -0.41670847
## female 0.28213990 0.28213990
## marital_stat 6.36843836 6.36843836
## num_ppl_in_house 0.03640394 0.03640394
## livealone 1.41967920 1.41967920
## yrs_of_edu -0.81597597 -0.81597597
## employment_type 3.61980679 3.61980679
## non_house_wealth_log 11.37233905 11.37233905
## house_wealth_log 7.78917495 7.78917495
## income_log 3.89733283 3.89733283
## insurance 3.89741615 3.89741615
# Compute predicted probabilities for the test data
rf_probabilities <- predict(rf_model, newdata = test_data, type = "prob")[, 2]
# Convert the actual outcome to a numeric binary variable (if necessary)
# Assuming "3+ Symptoms" is the positive class and "0-2 Symptoms" is the negative class
test_data$depresv_symp_numeric <- ifelse(test_data$depresv_symp == "3+ Symptoms", 1, 0)
# Generate the ROC curve and compute AUC
roc_rf <- roc(test_data$depresv_symp_numeric, rf_probabilities)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
# Explain ROC and AUC
# ROC (Receiver Operating Characteristic) curves illustrate the diagnostic ability of a binary classifier system as its discrimination threshold is varied.
# AUC (Area Under the Curve) provides a single scalar value to summarize the performance of the model, with higher values indicating better performance.
# Plot the ROC curve
plot.roc(roc_rf, main = "ROC Curve for Random Forest Model",
col = "blue", lwd = 2, print.auc = TRUE, auc.polygon = TRUE,
auc.polygon.col = "lightblue", grid = TRUE)
#Comparing RF to linear regression
# Train a Multiple Linear Regression model
# Convert depresv_symp to a factor if not already
train_data$depresv_symp <- as.factor(train_data$depresv_symp)
test_data$depresv_symp <- as.factor(test_data$depresv_symp)
# Fit the logistic regression model
logistic_model <- glm(depresv_symp ~ ., data = train_data, family = binomial)
# Display model summary
summary(logistic_model)
##
## Call:
## glm(formula = depresv_symp ~ ., family = binomial, data = train_data)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -3.525905 1.093076 -3.226 0.00126 **
## diff_day_actt 0.450734 0.101297 4.450 8.60e-06 ***
## age5065802 -0.784446 0.287453 -2.729 0.00635 **
## age5065803 -1.071947 0.384635 -2.787 0.00532 **
## self_rt_help 1.245741 0.219893 5.665 1.47e-08 ***
## num_comorbidity 0.176562 0.091821 1.923 0.05449 .
## cog_status1 0.027516 0.268488 0.102 0.91837
## cog_status2 0.351085 0.486109 0.722 0.47015
## female1 0.009433 0.211809 0.045 0.96448
## marital_stat1 -0.412535 0.395394 -1.043 0.29679
## marital_stat3 0.604520 0.456501 1.324 0.18542
## marital_stat4 0.983545 0.477457 2.060 0.03940 *
## marital_stat5 0.849041 0.509426 1.667 0.09558 .
## num_ppl_in_house 0.038308 0.122042 0.314 0.75360
## livealone1 -0.187347 0.350696 -0.534 0.59319
## yrs_of_edu 0.026245 0.042262 0.621 0.53460
## employment_type2 -0.355501 0.409028 -0.869 0.38477
## employment_type3 0.372871 0.331065 1.126 0.26005
## employment_type4 1.142393 0.429886 2.657 0.00787 **
## non_house_wealth_log -0.070654 0.033891 -2.085 0.03709 *
## house_wealth_log 0.044272 0.044753 0.989 0.32254
## income_log 0.225221 0.161399 1.395 0.16288
## insurance0 0.337294 0.392787 0.859 0.39050
## insurance2 -0.133175 0.375620 -0.355 0.72293
## insurance3 0.095173 0.464556 0.205 0.83767
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 867.38 on 745 degrees of freedom
## Residual deviance: 671.17 on 721 degrees of freedom
## AIC: 721.17
##
## Number of Fisher Scoring iterations: 5
# Predict on test data
lm_predictions <- predict(logistic_model, newdata = test_data)
# Evaluate the performance of the model
mse_lm <- mean((test_data$depresv_symp - lm_predictions)^2)
## Warning in Ops.factor(test_data$depresv_symp, lm_predictions): '-' not
## meaningful for factors
cat("Mean Squared Error (Linear Regression):", mse_lm, "\n")
## Mean Squared Error (Linear Regression): NA
# Generate the ROC curve and compute AUC for regression (using predicted probabilities)
roc_lm <- roc(test_data$depresv_symp, lm_predictions)
## Setting levels: control = 0-2 Symptoms, case = 3+ Symptoms
## Setting direction: controls < cases
# Plot the ROC curve
plot.roc(roc_lm, main = "ROC Curve for Linear Regression Model",
col = "red", lwd = 2, print.auc = TRUE, auc.polygon = TRUE,
auc.polygon.col = "pink", grid = TRUE)
# Define a threshold for classification
# Assuming a cutoff of 2.5 to separate "0-2 Symptoms" from "3+ Symptoms"
threshold <- 2.5
lm_predictions_class <- ifelse(lm_predictions >= threshold, "3+ Symptoms", "0-2 Symptoms")
# Create a confusion matrix
confusion_matrix_lm <- table(Predicted = lm_predictions_class, Actual = test_data$depresv_symp)
# Display the confusion matrix
cat("Confusion Matrix for Linear Regression Model:\n")
## Confusion Matrix for Linear Regression Model:
print(confusion_matrix_lm)
## Actual
## Predicted 0-2 Symptoms 3+ Symptoms
## 0-2 Symptoms 245 75
## 3+ Symptoms 1 0
# Compute overall accuracy
accuracy_lm <- sum(diag(confusion_matrix_lm)) / sum(confusion_matrix_lm)
cat("Accuracy (Linear Regression):", round(accuracy_lm * 100, 2), "%\n")
## Accuracy (Linear Regression): 76.32 %