# Install pacman if not available
if (!require("pacman")) install.packages("pacman")
## Loading required package: pacman
# Load or install needed packages
pacman::p_load(
tidyverse,
sf,
tidycensus,
tmap,
units,
spdep,
broom,
tidyr
)
# Global chunk options
knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)
# Define URL to the GeoJSON
hospital_poi_path <- "https://raw.githubusercontent.com/ujhwang/urban-analytics-2025/main/Assignment/mini_3/hospital_11counties.geojson"
# Read the GeoJSON directly into an sf object
hospitals <- st_read(hospital_poi_path)
## Reading layer `hospital_11counties' from data source
## `https://raw.githubusercontent.com/ujhwang/urban-analytics-2025/main/Assignment/mini_3/hospital_11counties.geojson'
## using driver `GeoJSON'
## Simple feature collection with 119 features and 8 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: -84.73147 ymin: 33.42719 xmax: -83.92052 ymax: 34.24585
## Geodetic CRS: WGS 84
# Quick look
print(hospitals)
## Simple feature collection with 119 features and 8 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: -84.73147 ymin: 33.42719 xmax: -83.92052 ymax: 34.24585
## Geodetic CRS: WGS 84
## First 10 features:
## id name
## 1 ChIJZfvWaSfv9IgRO0Er_vksXXU Piedmont Fayette
## 2 ChIJg5T0Tsxb9IgRmDdy4cVBydU Primary Pediatrics
## 3 ChIJeZlNG9RE9IgRsiEsG6aOOPI Aylo Health - Primary Care at McDonough, Hwy 81
## 4 ChIJn4tmsLD69IgR3Jdd7nzpAkY Southeast Medical Group at Fayetteville
## 5 ChIJA3catM1b9IgRa7fpG9cm3Iw Resurgens Orthopaedics
## 6 ChIJBduprw5X9IgRygZRl0TyxOs Piedmont Henry
## 7 ChIJWZQbcK9Q9IgR8vTxX-YOAr4 Southeast Medical Group at Walnut Creek
## 8 ChIJF7PSY7NQ9IgRvYa0LtT0qsw Southern Crescent TBI
## 9 ChIJVVXlsSL79IgR3R39iXXfC9Y Tri-County Pediatrics
## 10 ChIJaxgtgJz49IgRyV_ERJL8ZaE Southeast Medical Group at Stockbridge
## address primary_type
## 1 1255 Hwy 54 W, Fayetteville, GA 30214, USA hospital
## 2 110-A Regency Park Dr, McDonough, GA 30253, USA hospital
## 3 65 Old Jackson Rd, McDonough, GA 30252, USA hospital
## 4 105 Carnegie Pl #103, Fayetteville, GA 30214, USA hospital
## 5 156 Foster Dr Ste B, McDonough, GA 30253, USA hospital
## 6 1133 Eagles Landing Pkwy, Stockbridge, GA 30281, USA hospital
## 7 4303 Jodeco Rd, McDonough, GA 30253, USA hospital
## 8 2125 HWY 42N, McDonough, GA 30253, USA hospital
## 9 808 Commerce Blvd suite a, Riverdale, GA 30296, USA hospital
## 10 1035 Southcrest Dr #200, Stockbridge, GA 30281, USA hospital
## types status rating
## 1 hospital,health,point_of_interest,establishment OPERATIONAL 2.6
## 2 hospital,health,point_of_interest,establishment OPERATIONAL 4.0
## 3 hospital,doctor,health,point_of_interest,establishment OPERATIONAL 4.7
## 4 hospital,doctor,health,point_of_interest,establishment OPERATIONAL 4.5
## 5 hospital,doctor,health,point_of_interest,establishment OPERATIONAL 4.8
## 6 hospital,health,point_of_interest,establishment OPERATIONAL 2.0
## 7 hospital,doctor,health,point_of_interest,establishment OPERATIONAL 4.6
## 8 hospital,health,point_of_interest,establishment OPERATIONAL 4.1
## 9 hospital,doctor,point_of_interest,health,establishment OPERATIONAL 3.3
## 10 hospital,doctor,health,point_of_interest,establishment OPERATIONAL 4.6
## rating_count geometry
## 1 1096 POINT (-84.50847 33.45259)
## 2 69 POINT (-84.17159 33.43241)
## 3 1165 POINT (-84.09618 33.42719)
## 4 739 POINT (-84.42877 33.50894)
## 5 3180 POINT (-84.2052 33.46362)
## 6 1356 POINT (-84.22713 33.51114)
## 7 420 POINT (-84.17572 33.48875)
## 8 74 POINT (-84.18394 33.49794)
## 9 76 POINT (-84.41797 33.54758)
## 10 827 POINT (-84.27538 33.54389)
plot(st_geometry(hospitals), main = "Hospital Locations in Metro Atlanta")
print(glimpse(hospitals))
## Rows: 119
## Columns: 9
## $ id <chr> "ChIJZfvWaSfv9IgRO0Er_vksXXU", "ChIJg5T0Tsxb9IgRmDdy4cVBy…
## $ name <chr> "Piedmont Fayette", "Primary Pediatrics", "Aylo Health - …
## $ address <chr> "1255 Hwy 54 W, Fayetteville, GA 30214, USA", "110-A Rege…
## $ primary_type <chr> "hospital", "hospital", "hospital", "hospital", "hospital…
## $ types <chr> "hospital,health,point_of_interest,establishment", "hospi…
## $ status <chr> "OPERATIONAL", "OPERATIONAL", "OPERATIONAL", "OPERATIONAL…
## $ rating <dbl> 2.6, 4.0, 4.7, 4.5, 4.8, 2.0, 4.6, 4.1, 3.3, 4.6, 2.4, 2.…
## $ rating_count <int> 1096, 69, 1165, 739, 3180, 1356, 420, 74, 76, 827, 52, 57…
## $ geometry <POINT [°]> POINT (-84.50847 33.45259), POINT (-84.17159 33.432…
## Simple feature collection with 119 features and 8 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: -84.73147 ymin: 33.42719 xmax: -83.92052 ymax: 34.24585
## Geodetic CRS: WGS 84
## First 10 features:
## id name
## 1 ChIJZfvWaSfv9IgRO0Er_vksXXU Piedmont Fayette
## 2 ChIJg5T0Tsxb9IgRmDdy4cVBydU Primary Pediatrics
## 3 ChIJeZlNG9RE9IgRsiEsG6aOOPI Aylo Health - Primary Care at McDonough, Hwy 81
## 4 ChIJn4tmsLD69IgR3Jdd7nzpAkY Southeast Medical Group at Fayetteville
## 5 ChIJA3catM1b9IgRa7fpG9cm3Iw Resurgens Orthopaedics
## 6 ChIJBduprw5X9IgRygZRl0TyxOs Piedmont Henry
## 7 ChIJWZQbcK9Q9IgR8vTxX-YOAr4 Southeast Medical Group at Walnut Creek
## 8 ChIJF7PSY7NQ9IgRvYa0LtT0qsw Southern Crescent TBI
## 9 ChIJVVXlsSL79IgR3R39iXXfC9Y Tri-County Pediatrics
## 10 ChIJaxgtgJz49IgRyV_ERJL8ZaE Southeast Medical Group at Stockbridge
## address primary_type
## 1 1255 Hwy 54 W, Fayetteville, GA 30214, USA hospital
## 2 110-A Regency Park Dr, McDonough, GA 30253, USA hospital
## 3 65 Old Jackson Rd, McDonough, GA 30252, USA hospital
## 4 105 Carnegie Pl #103, Fayetteville, GA 30214, USA hospital
## 5 156 Foster Dr Ste B, McDonough, GA 30253, USA hospital
## 6 1133 Eagles Landing Pkwy, Stockbridge, GA 30281, USA hospital
## 7 4303 Jodeco Rd, McDonough, GA 30253, USA hospital
## 8 2125 HWY 42N, McDonough, GA 30253, USA hospital
## 9 808 Commerce Blvd suite a, Riverdale, GA 30296, USA hospital
## 10 1035 Southcrest Dr #200, Stockbridge, GA 30281, USA hospital
## types status rating
## 1 hospital,health,point_of_interest,establishment OPERATIONAL 2.6
## 2 hospital,health,point_of_interest,establishment OPERATIONAL 4.0
## 3 hospital,doctor,health,point_of_interest,establishment OPERATIONAL 4.7
## 4 hospital,doctor,health,point_of_interest,establishment OPERATIONAL 4.5
## 5 hospital,doctor,health,point_of_interest,establishment OPERATIONAL 4.8
## 6 hospital,health,point_of_interest,establishment OPERATIONAL 2.0
## 7 hospital,doctor,health,point_of_interest,establishment OPERATIONAL 4.6
## 8 hospital,health,point_of_interest,establishment OPERATIONAL 4.1
## 9 hospital,doctor,point_of_interest,health,establishment OPERATIONAL 3.3
## 10 hospital,doctor,health,point_of_interest,establishment OPERATIONAL 4.6
## rating_count geometry
## 1 1096 POINT (-84.50847 33.45259)
## 2 69 POINT (-84.17159 33.43241)
## 3 1165 POINT (-84.09618 33.42719)
## 4 739 POINT (-84.42877 33.50894)
## 5 3180 POINT (-84.2052 33.46362)
## 6 1356 POINT (-84.22713 33.51114)
## 7 420 POINT (-84.17572 33.48875)
## 8 74 POINT (-84.18394 33.49794)
## 9 76 POINT (-84.41797 33.54758)
## 10 827 POINT (-84.27538 33.54389)
summary(st_geometry(hospitals))
## POINT epsg:4326 +proj=long...
## 119 0 0
library(tidyverse)
library(sf)
library(tidycensus)
library(units)
library(purrr)
options(tigris_progress = FALSE)
# ---- Setup: metro counties, state, ACS year ----
metro_counties <- c(
"Cherokee","Clayton","Cobb","DeKalb","Douglas",
"Fayette","Forsyth","Fulton","Gwinnett","Henry","Rockdale"
)
state_name <- "GA"
acs_year <- 2021
access_miles <- 5
buffer_m <- access_miles * 1609.344 # 5 miles in meters
# ---- Define variables to fetch from ACS ----
vars <- c(
total_pop = "B01003_001",
median_hh_income = "B19013_001",
pov_total = "B17001_001", # total in poverty table
pov_under = "B17001_002", # people below poverty
med_age = "B01002_001",
white = "B02001_002",
black = "B02001_003",
asian = "B02001_005",
hispanic = "B03003_003",
uninsured_total = "B27010_001"
)
# ---- Fetch ACS data by county and bind ----
tracts_list <- list()
for (cty in metro_counties) {
message("Downloading ACS data for county: ", cty)
tr <- get_acs(
geography = "tract",
variables = vars,
state = state_name,
county = cty,
year = acs_year,
geometry = TRUE,
output = "wide"
)
tr$county <- cty
tracts_list[[cty]] <- tr
}
## | | | 0% | | | 1% | |= | 1% | |= | 2% | |== | 2% | |== | 3% | |== | 4% | |=== | 4% | |=== | 5% | |==== | 5% | |==== | 6% | |===== | 7% | |===== | 8% | |====== | 8% | |====== | 9% | |======= | 9% | |======= | 10% | |======== | 11% | |======== | 12% | |========= | 13% | |========== | 14% | |========== | 15% | |=========== | 15% | |=========== | 16% | |============ | 17% | |============ | 18% | |============= | 18% | |============= | 19% | |============== | 20% | |============== | 21% | |=============== | 21% | |=============== | 22% | |================ | 23% | |================= | 24% | |================= | 25% | |================== | 26% | |=================== | 27% | |=================== | 28% | |==================== | 29% | |===================== | 30% | |====================== | 31% | |======================= | 32% | |======================= | 33% | |======================== | 34% | |========================= | 35% | |========================= | 36% | |========================== | 37% | |=========================== | 38% | |=========================== | 39% | |============================ | 40% | |============================= | 41% | |============================= | 42% | |============================== | 43% | |=============================== | 44% | |=============================== | 45% | |================================ | 45% | |================================ | 46% | |================================= | 47% | |================================= | 48% | |================================== | 48% | |================================== | 49% | |=================================== | 50% | |=================================== | 51% | |==================================== | 51% | |==================================== | 52% | |===================================== | 53% | |====================================== | 54% | |====================================== | 55% | |======================================= | 56% | |======================================== | 57% | |========================================= | 58% | |========================================= | 59% | |========================================== | 60% | |=========================================== | 61% | |=========================================== | 62% | |============================================ | 63% | |============================================= | 64% | |============================================= | 65% | |============================================== | 66% | |=============================================== | 67% | |=============================================== | 68% | |================================================ | 68% | |================================================ | 69% | |================================================= | 70% | |================================================= | 71% | |================================================== | 71% | |================================================== | 72% | |=================================================== | 73% | |=================================================== | 74% | |==================================================== | 74% | |===================================================== | 75% | |===================================================== | 76% | |====================================================== | 76% | |====================================================== | 77% | |======================================================= | 78% | |======================================================= | 79% | |======================================================== | 79% | |======================================================== | 80% | |========================================================= | 81% | |========================================================= | 82% | |========================================================== | 82% | |========================================================== | 83% | |=========================================================== | 84% | |============================================================ | 85% | |============================================================ | 86% | |============================================================= | 87% | |============================================================== | 88% | |============================================================== | 89% | |=============================================================== | 90% | |================================================================ | 91% | |================================================================ | 92% | |================================================================= | 92% | |================================================================= | 93% | |================================================================== | 94% | |================================================================== | 95% | |=================================================================== | 95% | |=================================================================== | 96% | |==================================================================== | 97% | |==================================================================== | 98% | |===================================================================== | 99% | |======================================================================| 100%
tracts <- bind_rows(tracts_list)
# ---- Clean column names for estimates ----
tracts_clean <- tracts %>%
rename_with(~str_replace(., "E$", ""), ends_with("E")) %>%
mutate(
pct_poverty = as.numeric(pov_under) / as.numeric(pov_total) * 100,
pct_white = as.numeric(white) / as.numeric(total_pop) * 100,
pct_black = as.numeric(black) / as.numeric(total_pop) * 100,
pct_asian = as.numeric(asian) / as.numeric(total_pop) * 100,
pct_hispanic = as.numeric(hispanic) / as.numeric(total_pop) * 100,
median_hh_income = as.numeric(median_hh_income),
total_pop = as.numeric(total_pop)
)
# ---- Calculate population density (pop/km^2) ----
# Project to US Albers Equal Area for accurate area calculation (EPSG:5070)
tracts_clean <- st_transform(tracts_clean, crs = 5070)
tracts_clean$area_m2 <- st_area(tracts_clean)
tracts_clean$area_km2 <- as.numeric(tracts_clean$area_m2) / 1e6
tracts_clean$pop_density_per_km2 <- tracts_clean$total_pop / tracts_clean$area_km2
# ---- Load your hospital data ----
# Assuming you have your hospitals sf object ready as `hospitals`
# Example: hospitals <- st_read("path_to_hospital_shapefile.shp")
# Ensure hospitals are in the same CRS as tracts_clean
hospitals <- st_transform(hospitals, crs = st_crs(tracts_clean))
# ---- Compute tract centroids ----
tracts_clean <- tracts_clean %>%
mutate(centroid = st_centroid(geometry))
# ---- Count hospitals within 5 miles buffer of each tract centroid ----
within_mat <- st_is_within_distance(tracts_clean$centroid, hospitals, dist = buffer_m)
hosp_count_vec <- map_int(within_mat, length)
tracts_clean$hosp_count_5mi <- hosp_count_vec
# ---- Compute distance to nearest hospital for each tract centroid ----
nearest_dists <- st_distance(tracts_clean$centroid, hospitals) # matrix (tracts x hospitals)
min_dist_m <- apply(nearest_dists, 1, min)
tracts_clean$dist_nearest_hosp_m <- as.numeric(min_dist_m)
# ---- Compute hospitals per 10,000 population ----
tracts_clean$hosp_per_10k <- (tracts_clean$hosp_count_5mi / tracts_clean$total_pop) * 10000
# ---- Quick summary check ----
tracts_clean %>%
select(GEOID, total_pop, pct_poverty, median_hh_income, pop_density_per_km2,
hosp_count_5mi, hosp_per_10k, dist_nearest_hosp_m) %>%
slice_head(n = 10) %>%
print()
## Simple feature collection with 10 features and 8 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 1040348 ymin: 1287449 xmax: 1066289 ymax: 1309259
## Projected CRS: NAD83 / Conus Albers
## GEOID total_pop pct_poverty median_hh_income pop_density_per_km2
## 1 13057090706 4985 1.364092 136871 489.9003
## 2 13057090707 4506 24.260355 85684 1999.7331
## 3 13057091105 5233 9.038792 64471 949.9736
## 4 13057090504 5277 0.000000 121997 328.9437
## 5 13057090910 4692 8.289007 116458 601.5607
## 6 13057090805 4328 14.060325 73932 221.4724
## 7 13057090807 7217 5.459332 118631 781.3943
## 8 13057091012 3736 12.503383 70221 857.2000
## 9 13057090709 2100 8.904762 NA 590.7546
## 10 13057090703 5071 8.508112 43647 594.8065
## hosp_count_5mi hosp_per_10k dist_nearest_hosp_m
## 1 3 6.018054 6165.243
## 2 5 11.096316 4316.060
## 3 4 7.643799 2107.924
## 4 1 1.895016 4733.206
## 5 4 8.525149 3279.010
## 6 1 2.310536 6995.083
## 7 5 6.928086 4727.944
## 8 6 16.059957 1592.492
## 9 4 19.047619 1329.856
## 10 4 7.887991 4027.541
## geometry
## 1 MULTIPOLYGON (((1040348 129...
## 2 MULTIPOLYGON (((1046938 129...
## 3 MULTIPOLYGON (((1041061 128...
## 4 MULTIPOLYGON (((1061145 130...
## 5 MULTIPOLYGON (((1048781 128...
## 6 MULTIPOLYGON (((1049858 129...
## 7 MULTIPOLYGON (((1050971 129...
## 8 MULTIPOLYGON (((1044037 128...
## 9 MULTIPOLYGON (((1047915 129...
## 10 MULTIPOLYGON (((1048432 129...
# ---- Your data is ready for further analysis ----
# Scatter: income vs dist (convert dist to miles)
tracts_clean <- tracts_clean %>% mutate(dist_nearest_hosp_mi = dist_nearest_hosp_m / 1609.344)
p1 <- ggplot(st_drop_geometry(tracts_clean), aes(x = median_hh_income, y = dist_nearest_hosp_mi)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE) +
labs(x = "Median household income (USD)", y = "Distance to nearest hospital (miles)",
title = "Income vs Distance to Nearest Hospital (Tract centroids)")
# Create poverty quartiles
tracts_clean <- tracts_clean %>%
mutate(pov_q = ntile(pct_poverty, 4))
p2 <- ggplot(st_drop_geometry(tracts_clean), aes(x = factor(pov_q), y = dist_nearest_hosp_mi)) +
geom_boxplot() +
labs(x = "Poverty quartile (1=low,4=high)", y = "Distance to nearest hospital (miles)",
title = "Distance to nearest hospital by tract poverty quartile")
p1
p2
While higher-income tracts tend to be slightly farther from hospitals, the overall pattern is not strongly pronounced. This supports findings from your earlier model where income was only marginally significant in predicting distance to hospitals. It also reinforces that population density and poverty are stronger predictors of access than income alone.
The boxplot supports the earlier statistical findings that poverty is negatively associated with distance to hospitals hat is, poor communities are physically closer to healthcare facilities. This is likely a result of hospitals clustering in denser, lower-income areas with greater healthcare needs.
However, as seen in other models and visuals, physical proximity alone does not guarantee equitable access, especially when racial disparities or per capita availability are considered.
tmap_mode("plot") # or "view" for interactive
map1 <- tm_shape(tracts_clean) +
tm_polygons("dist_nearest_hosp_mi", palette = "Reds", style = "quantile",
title = "Dist to nearest hospital (mi)") +
tm_shape(hospitals) + tm_symbols(size = 0.1, shape = 21, col = "black") +
tm_layout(main.title = "Distance to nearest hospital — Metro Atlanta tracts")
map1
This choropleth map illustrates the spatial distribution of hospital access across census tracts in Metro Atlanta, measured by the distance to the nearest hospital. The color gradient shows a clear pattern: lighter areas, representing shorter distances (0.05 to 1.11 miles), are concentrated in the urban core, while darker shades, indicating longer distances (up to 12.34 miles), are found primarily in the outer suburban and rural tracts. This pattern highlights a strong urban sububan divide in hospital accessibility, with central Atlanta enjoying significantly closer proximity to healthcare facilities. The map visually confirms earlier statistical findings that tracts with higher population density nd often higher poverty levels end to have better access to hospitals. In contrast, wealthier or lower dnsity suburban areas are farther from hospital infrastructure. This spatial clustering of hospitals in central areas suggests that while urban residents may benefit from closer access, equitable distribution based on population needs specially for racially and economically marginalized communities remains critical concern.
map2 <- tm_shape(tracts_clean) +
tm_polygons("hosp_count_5mi", palette = "Blues", style = "fixed",
breaks = c(0,1,2,4,10), title = "Hospitals within 5 miles (count)") +
tm_shape(hospitals) + tm_symbols(size = 0.1, col = "red") +
tm_layout(main.title = "Hospital counts within 5 miles of tract centroid")
map2
The map illustrates the spatial distribution of hospital accessibility within a 5mile radius from each census tract centroid. It reveals a clear urban–rural disparity, with urban tracts showing higher access density due to the clustering of hospitals, while peripheral and rural areas exhibit sparse coverage. These low-access regions may face delayed medical care and higher health risks, particularly where socioeconomic vulnerabilities are concentrated. Overall, the map underscores significant spatial inequities in healthcare infrastructure, highlighting the need for targeted interventions such as improved transport connectivity, mobile health units, or new facility planning in underserved areas.
library(MASS) # for Negative Binomial
if (!requireNamespace("AER", quietly = TRUE)) {
install.packages("AER")
}
library(AER)
# Prepare dataset - remove NAs and extreme cases
analysis_data <- tracts_clean %>%
st_drop_geometry() %>%
filter(!is.na(hosp_count_5mi), !is.na(dist_nearest_hosp_m),
!is.na(median_hh_income), !is.na(pct_poverty), total_pop > 0) %>%
mutate(
log_dist_nearest_hosp = log(dist_nearest_hosp_m + 1) # add 1 to avoid log(0)
)
# ---- Model 1: Count model for hospitals within 5 miles ----
# Check for overdispersion
pois_mod <- glm(hosp_count_5mi ~ median_hh_income + pct_poverty + pct_black + pct_white + pct_asian + pct_hispanic + pop_density_per_km2,
family = poisson(link = "log"),
data = analysis_data)
# Dispersion test (if available, e.g., AER package's dispersiontest)
library(AER)
dispersiontest(pois_mod) # If overdispersed, move to negative binomial
##
## Overdispersion test
##
## data: pois_mod
## z = 14.008, p-value < 2.2e-16
## alternative hypothesis: true dispersion is greater than 1
## sample estimates:
## dispersion
## 2.482923
# If overdispersion detected, fit negative binomial
nb_mod <- glm.nb(hosp_count_5mi ~ median_hh_income + pct_poverty + pct_black + pct_white + pct_asian + pct_hispanic + pop_density_per_km2,
data = analysis_data)
summary(nb_mod)
##
## Call:
## glm.nb(formula = hosp_count_5mi ~ median_hh_income + pct_poverty +
## pct_black + pct_white + pct_asian + pct_hispanic + pop_density_per_km2,
## data = analysis_data, init.theta = 3.220802598, link = log)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 1.166e+00 4.113e-01 2.835 0.00458 **
## median_hh_income 1.881e-06 7.503e-07 2.506 0.01220 *
## pct_poverty 2.285e-02 2.620e-03 8.720 < 2e-16 ***
## pct_black -8.353e-03 4.121e-03 -2.027 0.04268 *
## pct_white -1.635e-03 4.071e-03 -0.402 0.68793
## pct_asian 1.133e-02 4.621e-03 2.453 0.01417 *
## pct_hispanic -3.281e-03 2.999e-03 -1.094 0.27391
## pop_density_per_km2 2.397e-04 1.296e-05 18.493 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for Negative Binomial(3.2208) family taken to be 1)
##
## Null deviance: 1872.4 on 1233 degrees of freedom
## Residual deviance: 1400.7 on 1226 degrees of freedom
## AIC: 6230.7
##
## Number of Fisher Scoring iterations: 1
##
##
## Theta: 3.221
## Std. Err.: 0.225
##
## 2 x log-likelihood: -6212.690
# ---- Model 2: Linear regression for distance to nearest hospital
lm_mod <- lm(log_dist_nearest_hosp ~ median_hh_income + pct_poverty + pct_black + pct_white + pct_asian + pct_hispanic + pop_density_per_km2,
data = analysis_data)
summary(lm_mod)
##
## Call:
## lm(formula = log_dist_nearest_hosp ~ median_hh_income + pct_poverty +
## pct_black + pct_white + pct_asian + pct_hispanic + pop_density_per_km2,
## data = analysis_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.6393 -0.3865 0.0737 0.4652 2.8590
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 8.115e+00 3.909e-01 20.762 < 2e-16 ***
## median_hh_income 1.273e-06 7.168e-07 1.777 0.075881 .
## pct_poverty -9.563e-03 2.533e-03 -3.776 0.000167 ***
## pct_black 4.487e-03 3.914e-03 1.146 0.251871
## pct_white 1.865e-03 3.868e-03 0.482 0.629803
## pct_asian -1.158e-02 4.453e-03 -2.601 0.009409 **
## pct_hispanic 2.825e-03 2.863e-03 0.987 0.323970
## pop_density_per_km2 -1.944e-04 1.448e-05 -13.425 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.6811 on 1226 degrees of freedom
## Multiple R-squared: 0.2207, Adjusted R-squared: 0.2163
## F-statistic: 49.61 on 7 and 1226 DF, p-value: < 2.2e-16
# ---- Model 3: Linear regression for hospitals per 10k population
lm_rate_mod <- lm(hosp_per_10k ~ median_hh_income + pct_poverty + pct_black + pct_white + pct_asian + pct_hispanic + pop_density_per_km2,
data = analysis_data)
summary(lm_rate_mod)
##
## Call:
## lm(formula = hosp_per_10k ~ median_hh_income + pct_poverty +
## pct_black + pct_white + pct_asian + pct_hispanic + pop_density_per_km2,
## data = analysis_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -90.758 -9.203 -4.128 3.763 315.636
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.503e+01 1.075e+01 1.398 0.1624
## median_hh_income 2.443e-05 1.972e-05 1.239 0.2156
## pct_poverty 5.177e-01 6.968e-02 7.430 2.03e-13 ***
## pct_black -1.772e-01 1.077e-01 -1.646 0.1001
## pct_white -6.557e-02 1.064e-01 -0.616 0.5379
## pct_asian 3.788e-03 1.225e-01 0.031 0.9753
## pct_hispanic -1.964e-01 7.877e-02 -2.493 0.0128 *
## pop_density_per_km2 4.468e-03 3.983e-04 11.218 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 18.74 on 1226 degrees of freedom
## Multiple R-squared: 0.1757, Adjusted R-squared: 0.171
## F-statistic: 37.33 on 7 and 1226 DF, p-value: < 2.2e-16
Model 1:Negative Binomial – Hospitals Within 5 Miles
Hospitals tend to cluster in areas with high population density and higher poverty likely reflecting urban cores and safety-net infrastructure. However, the significant negative association with % Black suggests a racial disparity: even within high density or high poverty tracts, those with larger Black populations tend to have less hospital proximity, highlighting a potential spatial inequity not explained by income or density alone.
Model 2:Linear – Distance to Nearest Hospital Proximity to the nearest hospital is primarily driven by population density and poverty, not race. Racial disparities seen in Model 1 are less evident here, possibly because all urban tracts are relatively close to at least one hospital. However, this metric alone may not capture access quality or system capacity, just distance.
Model 3: Linear – Hospitals Per 10,000 Residents Hospital resources per capita are concentrated in dense and high-poverty areas, possibly reflecting public health priorities. However, the negative relationship with % Hispanic signals a potential equity concern: areas with larger Hispanic populations appear under-served relative to their population size, even after accounting for poverty and density.
Equity Verdict
The analysis reveals that healthcare access in Metro Atlanta is not fully equitable. Although areas with higher poverty and greater population density tend to have better overall access to hospitals, significant racial disparities persist. Notably, tracts with higher percentages of Black residents have fewer hospitals within a 5mile radius, even after accounting for poverty and density. This suggests a systemic spatial inequity in how healthcare infrastructure has been distributed, disproportionately affecting Black communities. Similarly, areas with larger Hispanic populations have fewer hospitals per 10,000 residents, indicating potential strain on existing healthcare resources and raising concerns about whether hospital capacity aligns with population needs. These disparities underscore that improving access in general does not necessarily ensure equitable access across racial and ethnic groups.
Recommendations
To address the observed disparities in Black communities, local and state health agencies should conduct spatial equity audits focused on Black-majority tracts to identify potential service deserts. Investments in healthcare infrastructure should be prioritized in areas where historical disinvestment may have contributed to lower access. Furthermore, active engagement with community stakeholders is essential to ensure that future planning efforts respond directly to the proximity and accessibility needs of these communities.
For Hispanic communities agencies should evaluate healthcare service capacity in Hispanic-majority areas, including metrics like provider availability, wait times, and overcrowding. Ensuring the equitable allocation of hospital resources relative to population size is critical. Additionally, expanding culturally and linguistically appropriate services may improve both utilization and the siting of future facilities in Hispanic communities.
More broadly, policymakers should integrate equity indicators into hospital siting and planning processes. Health departments and regional planners are encouraged to use data driven tools to monitor and address spatial disparities over time, especially as urban growth and demographic patterns continue to shift. Proactive, equity-informed infrastructure planning is key to ensuring that all residents regardless of race, ethnicity, or income have fair access to healthcare services.