source("data_preprocessing_backbone.R")
labor_data = read_csv("./data/CA_Labor.csv") %>%
janitor::clean_names() %>%
dplyr::select(-employment,-unemployment,-rank) %>%
rename(unemployment=unemployment_rate_per_cent)
population_data = read_csv("./data/CA_Land_Area.csv") %>%
janitor::clean_names() %>%
dplyr::select(-rank, -state) %>%
mutate(
location = ifelse(county %in% c("Los Angeles", "Orange", "San Diego", "Monterey", "San Benito", "San Luis Obispo", "Santa Barbara", "Santa Cruz", "Ventura", "Alameda", "Contra Costa", "Marin", "Napa", "San Francisco", "San Mateo", "Santa Clara", "Solano", "Sonoma", "Del Norte", "Humboldt", "Mendocino"), "costal", "inland"))
ca_nonmedical_data = left_join(population_data, labor_data, by = c("county")) %>%
mutate(labor_rate = labor_force/population*100) %>%
dplyr::select(-labor_force)
demo_covid = demo %>%
mutate(cumulative_deaths= as.numeric(population),
cumulative_cases = as.numeric(cumulative_cases),
prevalence=cumulative_cases/population*100,
test = cumulative_total_tests/population*100,
) %>%
dplyr::select(county_name, prevalence, test, population) %>%
rename(county=county_name) %>%
group_by(county) %>%
summarize(prevalence=max(prevalence),
test = max(test)) %>%
filter(!county %in% c("Out of state","Unknown"))
vaccine = read_csv("./data/CA_covid19vaccines.csv") %>%
janitor::clean_names() %>%
group_by(county) %>%
summarise(
fully_vaccinated = sum(fully_vaccinated)
# total number of fully vaccinated people
) %>%
arrange(county) %>%
drop_na() %>%
filter(!county %in% c("Unknown", "All CA Counties", "All CA and Non-CA Counties"))
ca_medical_data = left_join(demo_covid, vaccine, by =c("county"))
ca_premodel_data = left_join(ca_medical_data, ca_nonmedical_data, by = c("county")) %>%
mutate(
vaccination=fully_vaccinated/population*100) %>%
dplyr::select(-fully_vaccinated)
skimr::skim(ca_premodel_data)
| Name | ca_premodel_data |
| Number of rows | 58 |
| Number of columns | 9 |
| _______________________ | |
| Column type frequency: | |
| character | 2 |
| numeric | 7 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| county | 0 | 1 | 4 | 15 | 0 | 58 | 0 |
| location | 0 | 1 | 6 | 6 | 0 | 2 | 0 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| prevalence | 0 | 1 | 22.53 | 5.77 | 9.45 | 19.54 | 22.51 | 25.47 | 38.64 | ▂▆▇▂▁ |
| test | 0 | 1 | 367.42 | 171.84 | 104.51 | 277.33 | 312.06 | 402.06 | 983.95 | ▅▇▂▁▁ |
| land_area_sq_mi | 0 | 1 | 2685.85 | 3102.32 | 46.87 | 959.49 | 1535.34 | 3454.40 | 20056.92 | ▇▂▁▁▁ |
| population | 0 | 1 | 656326.21 | 1443528.73 | 1202.00 | 47277.50 | 179992.50 | 656486.00 | 9974203.00 | ▇▁▁▁▁ |
| unemployment | 0 | 1 | 7.33 | 2.12 | 4.42 | 5.97 | 6.96 | 8.22 | 17.37 | ▇▆▁▁▁ |
| labor_rate | 0 | 1 | 45.90 | 6.86 | 27.46 | 41.68 | 47.42 | 50.03 | 65.86 | ▁▃▇▂▁ |
| vaccination | 0 | 1 | 65.81 | 15.11 | 26.84 | 54.55 | 64.62 | 76.02 | 101.87 | ▁▆▇▆▃ |
left_join and divided by labor force by population as labor
force percentagepoint <- format_format(big.mark = " ", decimal.mark = ",", scientific = FALSE)
rawplot_prevalence = ca_premodel_data %>%
ggplot(aes(x = prevalence)) +
geom_density(fill = "#69b3a2", color = "#e9ecef", alpha = .8) +
scale_y_continuous(labels = point)
rawplot_unemployment = ca_premodel_data %>%
ggplot(aes(x = unemployment)) +
geom_density(fill = "#69b3a2", color = "#e9ecef", alpha = .8) +
scale_y_continuous(labels = point) +
scale_x_continuous(labels = point)
rawplot_labor = ca_premodel_data %>%
ggplot(aes(x = labor_rate)) +
geom_density(fill = "#69b3a2", color = "#e9ecef", alpha = .8) +
scale_y_continuous(labels = point) +
scale_x_continuous(labels = point)
rawplot_population = ca_premodel_data %>%
ggplot(aes(x = population)) +
geom_density(fill = "#69b3a2", color = "#e9ecef", alpha = .8) +
scale_y_continuous(labels = point) +
scale_x_continuous(labels = point)
plot_area = ca_premodel_data %>%
ggplot(aes(x = land_area_sq_mi)) +
geom_density(fill = "#69b3a2", color = "#e9ecef", alpha = .8)
plot_test = ca_premodel_data %>%
ggplot(aes(x = test)) +
geom_density(fill = "#69b3a2", color = "#e9ecef", alpha = .8)
ca_model_data = ca_premodel_data %>%
mutate(ln_unemployment = log(unemployment),
ln_area = log(land_area_sq_mi),
ln_population = log(population),
ln_test = log(test)) %>%
dplyr::select(-unemployment,-land_area_sq_mi,-population,-test,-county)
plot_ln_test = ca_model_data %>%
ggplot(aes(x = ln_test)) +
geom_density(fill = "#69b3a2", color = "#e9ecef", alpha = .8)
plot_ln_unemployment = ca_model_data %>%
ggplot(aes(x = ln_unemployment)) +
geom_density(fill = "#69b3a2", color = "#e9ecef", alpha = .8)
plot_ln_area = ca_model_data %>%
ggplot(aes(x = ln_area)) +
geom_density(fill = "#69b3a2", color = "#e9ecef", alpha = .8)
plot_ln_population = ca_model_data %>%
ggplot(aes(x = ln_population)) +
geom_density(fill = "#69b3a2", color = "#e9ecef", alpha = .8)
(rawplot_prevalence + rawplot_unemployment + plot_area + rawplot_population+plot_test)/(rawplot_labor + plot_ln_unemployment + plot_ln_area+plot_ln_population+plot_ln_test)

ca_model_data %>%
gtsummary::tbl_summary() %>%
gtsummary::bold_labels() %>%
as.tibble() %>%
knitr::kable()
| Characteristic | N = 58 |
|---|---|
| prevalence | 23 (20, 25) |
| location | NA |
| costal | 21 (36%) |
| inland | 37 (64%) |
| labor_rate | 47 (42, 50) |
| vaccination | 65 (55, 76) |
| ln_unemployment | 1.94 (1.79, 2.11) |
| ln_area | 7.34 (6.87, 8.15) |
| ln_population | 12.10 (10.76, 13.39) |
| ln_test | 5.74 (5.63, 6.00) |
ca_model_data %>%
dplyr::select(-location) %>%
GGally::ggpairs(upper = list(continuous = GGally::wrap("cor", size = 3)))

vaccination and labor_rate, implying that
including that including both of these variables in the model could
result in poor coefficient estimation and inflated standard errors (due
to multicollinearity). Our model selection techniques are able to figure
this out.reg_full = lm(prevalence ~ ., data = ca_model_data)
reg_intercept = lm(prevalence ~ 1, data = ca_model_data)
#forward Selection
step(reg_intercept, direction='forward', scope=formula(reg_full),trace=0) %>%
broom::tidy() %>%
mutate(term = str_replace(term, "^location", "Location: ")) %>%
knitr::kable(digits = 3)
| term | estimate | std.error | statistic | p.value |
|---|---|---|---|---|
| (Intercept) | -71.447 | 8.097 | -8.823 | 0.000 |
| ln_test | 9.815 | 1.128 | 8.703 | 0.000 |
| ln_unemployment | 8.066 | 1.855 | 4.349 | 0.000 |
| ln_area | 1.341 | 0.472 | 2.839 | 0.006 |
| ln_population | 0.820 | 0.270 | 3.038 | 0.004 |
| Location: inland | 1.956 | 1.108 | 1.765 | 0.083 |
#backwards elimination
step(reg_full, direction='backward', scope=formula(reg_full),trace=0) %>%
broom::tidy() %>%
mutate(term = str_replace(term, "^location", "Location: ")) %>%
knitr::kable(digits = 3)
| term | estimate | std.error | statistic | p.value |
|---|---|---|---|---|
| (Intercept) | -71.447 | 8.097 | -8.823 | 0.000 |
| Location: inland | 1.956 | 1.108 | 1.765 | 0.083 |
| ln_unemployment | 8.066 | 1.855 | 4.349 | 0.000 |
| ln_area | 1.341 | 0.472 | 2.839 | 0.006 |
| ln_population | 0.820 | 0.270 | 3.038 | 0.004 |
| ln_test | 9.815 | 1.128 | 8.703 | 0.000 |
#stepwise Selection
step(reg_full, direction = "both", scope=formula(reg_full),trace=0) %>%
broom::tidy() %>%
mutate(term = str_replace(term, "^location", "Location: ")) %>%
knitr::kable(digits = 3)
| term | estimate | std.error | statistic | p.value |
|---|---|---|---|---|
| (Intercept) | -71.447 | 8.097 | -8.823 | 0.000 |
| Location: inland | 1.956 | 1.108 | 1.765 | 0.083 |
| ln_unemployment | 8.066 | 1.855 | 4.349 | 0.000 |
| ln_area | 1.341 | 0.472 | 2.839 | 0.006 |
| ln_population | 0.820 | 0.270 | 3.038 | 0.004 |
| ln_test | 9.815 | 1.128 | 8.703 | 0.000 |
# Use criterion-based procedures to guide your selection of the ‘best subset’
# chosen using SSE/RSS (smaller is better)
criterion_selected = regsubsets(prevalence ~ ., data = ca_model_data)
criterion_plot = summary(criterion_selected)
# plot of Cp and Adj-R2 as functions of parameters
x_new = (2:8)
y_new = criterion_plot$cp
z_new = criterion_plot$adjr2
cp_new_p =
cbind(x_new, y_new, z_new) %>%
as.tibble() %>%
rename(`Number of parameter` = x_new,
`Cp value` = y_new,
`Adj R-square` = z_new) %>%
mutate(p = `Number of parameter`)
cp_fe_new_1 =
cp_new_p %>%
ggplot() +
geom_point(aes(x = `Number of parameter`, y = `Cp value`, color = `Cp value`)) +
theme(legend.position = "none") +
geom_abline(intercept = 0, slope = 1, color = "red")
cp_fe_new_2 =
cp_new_p %>%
ggplot() +
geom_point(aes(x = `Number of parameter`, y = `Adj R-square`, color = `Adj R-square`)) +
theme(legend.position = "none")
cp_fe_new_1 + cp_fe_new_2

\(\hat{prevalence}=-71.4474+1.9559*I(location=inland)+8.0663*ln(unemployment)+1.3412*ln(area)+0.8201*ln(population)+9.8152*ln(test)\)
ca_lasso_data = ca_model_data %>% mutate(
location=ifelse(location =="costal", 1,0))
# using cross validation to choose lambda
lambda_seq = 10^seq(-5, 0, by = .01)
set.seed(2022)
cv_object <- cv.glmnet(as.matrix(ca_lasso_data[2:8]), ca_lasso_data$prevalence,
lambda = lambda_seq, nfolds = 5)
cv_object
##
## Call: cv.glmnet(x = as.matrix(ca_lasso_data[2:8]), y = ca_lasso_data$prevalence, lambda = lambda_seq, nfolds = 5)
##
## Measure: Mean-Squared Error
##
## Lambda Index Measure SE Nonzero
## min 0.2818 56 11.76 3.542 5
## 1se 0.9550 3 15.21 5.401 4
# plot the CV results
tibble(lambda = cv_object$lambda,
mean_cv_error = cv_object$cvm) %>%
mutate(text_label = str_c("Lambda: ", lambda,
"\n Mean CV error: ", mean_cv_error)) %>%
plotly::plot_ly(y = ~mean_cv_error, x = ~lambda,
color = ~lambda,
width = 900,
height = 500,
type = "scatter",
mode = "markers",
marker = list(size = 6),
colors = "magma",
text = ~ text_label)
# refit the lasso model with the "best" lambda
fit_bestcv <- glmnet(as.matrix(ca_lasso_data[2:8]), ca_lasso_data$prevalence, lambda = cv_object$lambda.min)
fit_bestcv %>%
broom::tidy() %>%
mutate(term = str_replace(term, "^location", "Location: ")) %>%
knitr::kable(digits = 3)
| term | step | estimate | lambda | dev.ratio |
|---|---|---|---|---|
| (Intercept) | 1 | -59.142 | 0.282 | 0.724 |
| Location: | 1 | -0.884 | 0.282 | 0.724 |
| ln_unemployment | 1 | 7.620 | 0.282 | 0.724 |
| ln_area | 1 | 1.131 | 0.282 | 0.724 |
| ln_population | 1 | 0.637 | 0.282 | 0.724 |
| ln_test | 1 | 8.767 | 0.282 | 0.724 |
To make it easier for LASSO to proceed, I converted the
position variables to 0 and 1. Using the LASSO method to
perform variable selection, the key step is to make sure choosing the
optimal \(\lambda\) to efficiently
reduce the complexity of the model while some descriptive prediction
ability maintained. Thus a five-fold cross validation was
performed.
Since the stepwise selection techniques and the criterion techniques all chose the same model with 5 predictors, we recommend this as our final model. Also, the LASSO gave a very similar suggested model.
mlr_model = lm(prevalence~location+ln_test+ln_unemployment+ln_area+ln_population,data = ca_model_data)
mlr_model %>%
broom::tidy() %>%
mutate(term = str_replace(term, "^location", "Location: ")) %>%
knitr::kable(digits = 3)
| term | estimate | std.error | statistic | p.value |
|---|---|---|---|---|
| (Intercept) | -71.447 | 8.097 | -8.823 | 0.000 |
| Location: inland | 1.956 | 1.108 | 1.765 | 0.083 |
| ln_test | 9.815 | 1.128 | 8.703 | 0.000 |
| ln_unemployment | 8.066 | 1.855 | 4.349 | 0.000 |
| ln_area | 1.341 | 0.472 | 2.839 | 0.006 |
| ln_population | 0.820 | 0.270 | 3.038 | 0.004 |
As almost all the terms has p-value < 0.05, with the
location variable < 0.1, we can conclude that all the
predictor coefficients are statistical significant at 0.1 significance
level for location and 0.05 significance level for others,
hence all the five variables will be included in the model. All these
methods agree on the same suggested model or predictors. This might give
good evidence that we should explore this model further.
From the outputs above, we see that location had a p-value close
to 0.05. This might mean that the relationship between prevalence and
location is weak. Also, the highly correlated variables
vaccination and labor_rate neither was
included in the final models.
check_model(mlr_model, check = c("linearity", "qq", "normality", "outliers", "homogeneity", "vif"))

# define sd function
sd_function <- lm(abs(reg_full$residuals) ~ reg_full$fitted.values)
var_fitted <- sd_function$fitted.values^2
#define weight
wt <- 1/var_fitted
wls_full = lm(prevalence~.,data = ca_model_data, weights = wt)
wls_prevalence = step(wls_full, direction='backward', scope=formula(wls_full),trace=0)
wls_prevalence %>%
broom::tidy() %>%
mutate(term = str_replace(term, "^location", "Location: ")) %>%
knitr::kable(digits = 3)
| term | estimate | std.error | statistic | p.value |
|---|---|---|---|---|
| (Intercept) | -73.046 | 7.992 | -9.140 | 0.000 |
| Location: inland | 2.131 | 1.099 | 1.938 | 0.058 |
| ln_unemployment | 7.619 | 1.884 | 4.045 | 0.000 |
| ln_area | 1.344 | 0.464 | 2.898 | 0.005 |
| ln_population | 0.904 | 0.265 | 3.406 | 0.001 |
| ln_test | 10.044 | 1.144 | 8.779 | 0.000 |
check_model(wls_prevalence, check = c("linearity", "qq", "normality", "outliers", "homogeneity", "vif"))

set.seed(2022)
# use 10-fold validation and create the training sets
train = trainControl(method = "cv", number = 10)
# fit the 4-variables model that we selected as our final model
model_caret = train(prevalence~location+ln_test+ln_unemployment+ln_area+ln_population, data = ca_model_data, trControl = train, method = 'lm', na.action = na.pass)
model_caret
## Linear Regression
##
## 58 samples
## 5 predictor
##
## No pre-processing
## Resampling: Cross-Validated (10 fold)
## Summary of sample sizes: 53, 52, 51, 53, 52, 52, ...
## Resampling results:
##
## RMSE Rsquared MAE
## 3.079446 0.7261623 2.558792
##
## Tuning parameter 'intercept' was held constant at a value of TRUE
model_caret$resample
## RMSE Rsquared MAE Resample
## 1 2.825007 0.76359260 2.519925 Fold01
## 2 3.190194 0.71590015 2.853202 Fold02
## 3 1.433165 0.88045483 1.304306 Fold03
## 4 1.979195 0.92212958 1.704161 Fold04
## 5 1.933953 0.96590869 1.669703 Fold05
## 6 3.339120 0.53507207 2.909027 Fold06
## 7 3.306837 0.71923646 2.572679 Fold07
## 8 3.019968 0.87696581 2.490659 Fold08
## 9 4.714584 0.78563673 3.646323 Fold09
## 10 5.052439 0.09672592 3.917936 Fold10
\(\hat{prevalence}=-71.4474+1.9559*I(location=inland)+8.0663*ln(unemploy)+1.3412*ln(area)+0.8201*ln(population)+9.8152*ln(test)\)
From this model, we can see that as the while the location is inland, unemployment, land area, population and test participation increases, the predicted prevalence increases. This is probably because inland counties, counties with high unemployment generally have poor economy so that poor medical conditions, which may finally lead to increased prevalence; while the large size of the county and the high population of the county may lead to increased prevalence due to dense population, but still needs further investigation. In addition, the high prevalence is from increased test participation is somewhat surprising, but it may be because more potential positive patients are able to be detected.
Overall, from our 10-fold cross validation, we see that our model has pretty good predictive ability for new points. However, this model was only built on data on the county level, so there may be some special information missing, but the overall model reflects the correlation and relationship between the prevalence and some predictors in California.