Healthcare Equity Analysis

Zihan Weng

2024-10-03

1. Load Yelp Hospital Data

We begin by loading the hospital POI data from Yelp using the st_read.

hospital_data_url <- "https://raw.githubusercontent.com/ujhwang/urban-analytics-2024/main/Assignment/mini_3/yelp_hospital.geojson"
hospital_data <- st_read(hospital_data_url)
## Reading layer `yelp_hospital' from data source 
##   `https://raw.githubusercontent.com/ujhwang/urban-analytics-2024/main/Assignment/mini_3/yelp_hospital.geojson' 
##   using driver `GeoJSON'
## Simple feature collection with 129 features and 23 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: -84.56242 ymin: 33.60009 xmax: -84.08677 ymax: 34.0701
## Geodetic CRS:  WGS 84
# Preview the data
skim(hospital_data)
Data summary
Name hospital_data
Number of rows 129
Number of columns 24
_______________________
Column type frequency:
character 18
logical 1
numeric 5
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
id 0 1.00 22 22 0 129 0
alias 0 1.00 17 73 0 129 0
name 0 1.00 8 64 0 115 0
image_url 0 1.00 0 68 86 44 0
url 0 1.00 174 230 0 129 0
categories 0 1.00 9 62 0 21 0
transactions 0 1.00 0 0 129 1 0
phone 0 1.00 0 12 5 107 0
display_phone 0 1.00 0 14 5 107 0
location.address1 2 0.98 0 34 14 87 0
location.address2 13 0.90 0 7 100 15 0
location.address3 23 0.82 0 52 104 3 0
location.city 0 1.00 6 14 0 13 0
location.zip_code 0 1.00 0 5 1 33 0
location.country 0 1.00 2 2 0 1 0
location.state 0 1.00 2 2 0 1 0
location.display_address 0 1.00 17 93 0 93 0
geometry 0 1.00 21 38 0 103 0

Variable type: logical

skim_variable n_missing complete_rate mean count
is_closed 0 1 0 FAL: 129

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
review_count 0 1 12.90 42.98 0.00 0.00 0.00 2.00 319.00 ▇▁▁▁▁
rating 0 1 1.05 1.47 0.00 0.00 0.00 2.00 5.00 ▇▁▂▁▁
distance 0 1 1188.10 735.72 204.09 564.81 1199.68 1647.70 4098.37 ▇▇▂▁▁
coordinates.latitude 0 1 33.86 0.12 33.60 33.77 33.81 33.92 34.07 ▁▇▅▅▅
coordinates.longitude 0 1 -84.34 0.06 -84.56 -84.39 -84.35 -84.32 -84.09 ▁▆▇▁▁

2 & 3. Choose Variables and Download Census Data for Fulton and DeKalb Counties

We will use the tidycensus package to download the ACS 5-year estimate data for socioeconomic factors. For Census Tracts in these counties, we will focus on the following variables:
income (median household income) Represents economic capacity, a key factor influencing access to healthcare services;
education level (bachelor’s degree or higher and high school graduate) Often correlates with health literacy and income, which in turn can impact healthcare access;
race (white and black) Racial disparities in healthcare access are well-documented, so we may explore whether predominantly Black or White communities have similar access to hospitals;
and total population/population density Ensures that healthcare access is analyzed relative to population size.
By selecting these variables, we aim to capture a broad understanding of socioeconomic conditions (income, education) and demographic characteristics (race) that could indicate whether hospital access is distributed equitably across different communities in Fulton and DeKalb counties.

# Define the ACS variables for equity analysis
acs_vars <- c(
  median_income = "B19013_001",  # Median household income
  total_population = "B02001_001", # Total population
  race_white = "B02001_002",      # White Alone population
  race_black = "B02001_003",      # Black population
  race_asian = "B02001_005",    # Asian population
  race_hispanic = "B03003_003",    # Hispanic population
  education_bachelors = "B15003_022", # Bachelor's degree
  education_high_school = "B15003_017" # High school graduate population
)

# Get ACS data for Fulton and DeKalb counties
tract_acs <- get_acs(geography = "tract", 
                     variables = acs_vars, 
                     state = "GA", 
                     county = c("Fulton", "DeKalb"), 
                     geometry = TRUE,
                     year = 2020,
                     output = "wide")
##   |                                                                              |                                                                      |   0%  |                                                                              |                                                                      |   1%  |                                                                              |=                                                                     |   1%  |                                                                              |==                                                                    |   2%  |                                                                              |==                                                                    |   3%  |                                                                              |===                                                                   |   4%  |                                                                              |====                                                                  |   6%  |                                                                              |======                                                                |   8%  |                                                                              |======                                                                |   9%  |                                                                              |=======                                                               |  11%  |                                                                              |========                                                              |  12%  |                                                                              |=========                                                             |  14%  |                                                                              |==========                                                            |  14%  |                                                                              |===========                                                           |  15%  |                                                                              |===========                                                           |  16%  |                                                                              |============                                                          |  17%  |                                                                              |=============                                                         |  19%  |                                                                              |==============                                                        |  20%  |                                                                              |=================                                                     |  24%  |                                                                              |=====================                                                 |  31%  |                                                                              |===========================                                           |  39%  |                                                                              |=============================                                         |  41%  |                                                                              |==============================                                        |  43%  |                                                                              |===============================                                       |  44%  |                                                                              |=================================                                     |  47%  |                                                                              |======================================                                |  54%  |                                                                              |===========================================                           |  62%  |                                                                              |=================================================                     |  70%  |                                                                              |=====================================================                 |  75%  |                                                                              |=====================================================                 |  76%  |                                                                              |=======================================================               |  78%  |                                                                              |==========================================================            |  83%  |                                                                              |================================================================      |  91%  |                                                                              |===================================================================== |  98%  |                                                                              |======================================================================|  99%  |                                                                              |======================================================================| 100%
skim(tract_acs)
Data summary
Name tract_acs
Number of rows 530
Number of columns 19
_______________________
Column type frequency:
character 3
numeric 16
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
GEOID 0 1 11 11 0 530 0
NAME 0 1 38 43 0 530 0
geometry 0 1 193 3796 0 530 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
median_incomeE 8 0.98 81559.87 49076.11 13577 46780.50 69623.0 100893.50 250001 ▇▇▃▁▁
median_incomeM 17 0.97 20906.62 15297.29 1325 10589.00 17280.0 26107.00 102240 ▇▃▁▁▁
total_populationE 0 1.00 3409.13 1276.00 0 2498.25 3267.0 4243.00 7401 ▁▇▇▃▁
total_populationM 0 1.00 681.82 399.85 14 440.00 605.0 803.25 3135 ▇▆▁▁▁
race_whiteE 0 1.00 1337.87 1198.31 0 237.75 1062.5 2146.00 5039 ▇▅▃▁▁
race_whiteM 0 1.00 328.76 261.12 3 141.00 285.5 445.75 2943 ▇▁▁▁▁
race_blackE 0 1.00 1624.68 1545.94 0 367.00 996.5 2739.25 7021 ▇▂▂▁▁
race_blackM 0 1.00 494.38 423.03 14 199.25 393.0 685.75 2987 ▇▃▁▁▁
race_asianE 0 1.00 232.71 381.87 0 0.25 79.5 265.00 2376 ▇▁▁▁▁
race_asianM 0 1.00 131.48 149.16 2 20.00 81.5 181.75 910 ▇▂▁▁▁
race_hispanicE 0 1.00 262.40 422.26 0 51.00 128.5 288.75 3053 ▇▁▁▁▁
race_hispanicM 0 1.00 168.67 166.15 2 51.00 119.5 226.00 1439 ▇▂▁▁▁
education_bachelorsE 0 1.00 685.15 413.17 0 359.75 646.5 944.50 2027 ▆▇▅▂▁
education_bachelorsM 0 1.00 235.45 145.37 14 148.25 209.0 282.75 1321 ▇▂▁▁▁
education_high_schoolE 0 1.00 382.63 304.87 0 144.25 303.0 564.50 1560 ▇▅▂▁▁
education_high_schoolM 0 1.00 191.42 148.49 5 92.00 155.5 240.75 1218 ▇▂▁▁▁

4. Prepare Data for Analysis

Next, we will prepare the data for analysis by handling/cleaning missing values (NA values) and calculating additional metrics such as the proportion of different racial groups and population density.

# Calculate race proportions and population density
tract_acs <- tract_acs %>%
  mutate(white_pct = 100 * race_whiteE / total_populationE, # E for estimate
         black_pct = 100 * race_blackE / total_populationE,
         hispanic_pct = 100 * race_hispanicE / total_populationE,
         asian_pct = 100 * race_asianE / total_populationE,
         population_density = 100 * total_populationE / st_area(geometry))

# Remove any rows with missing data
tract_acs <- tract_acs %>%
  drop_na()

head(tract_acs)
## Simple feature collection with 6 features and 23 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -84.47101 ymin: 33.65339 xmax: -84.33035 ymax: 33.85418
## Geodetic CRS:  NAD83
##         GEOID                                        NAME median_incomeE
## 1 13089020500    Census Tract 205, DeKalb County, Georgia          73382
## 2 13121010601 Census Tract 106.01, Fulton County, Georgia          55479
## 3 13121002300     Census Tract 23, Fulton County, Georgia          28611
## 4 13121004200     Census Tract 42, Fulton County, Georgia          23906
## 5 13121003100     Census Tract 31, Fulton County, Georgia          82309
## 6 13121009601  Census Tract 96.01, Fulton County, Georgia         118045
##   median_incomeM total_populationE total_populationM race_whiteE race_whiteM
## 1          27540              3347               574        1629         409
## 2          11675              3673               960        1057         545
## 3          16783              1384               322          63          70
## 4           4494              2675               577         252         146
## 5          39452              2290               397        1324         243
## 6          19862              3209               606        2251         307
##   race_blackE race_blackM race_asianE race_asianM race_hispanicE race_hispanicM
## 1        1260         358          70          57            250            158
## 2        2514         788          30          32            100             91
## 3        1293         323           0          14              6             11
## 4        2423         606           0          14             58             38
## 5         900         361           0          14            228            154
## 6         275         132         567         541            118             75
##   education_bachelorsE education_bachelorsM education_high_schoolE
## 1                  827                  212                    567
## 2                  696                  343                    448
## 3                   98                   66                    370
## 4                  287                  146                    482
## 5                  695                  213                    348
## 6                  942                  165                    104
##   education_high_schoolM white_pct black_pct hispanic_pct asian_pct
## 1                    203 48.670451 37.645653     7.469376  2.091425
## 2                    219 28.777566 68.445412     2.722570  0.816771
## 3                    142  4.552023 93.424855     0.433526  0.000000
## 4                    207  9.420561 90.579439     2.168224  0.000000
## 5                    311 57.816594 39.301310     9.956332  0.000000
## 6                     67 70.146463  8.569648     3.677158 17.669056
##   population_density                       geometry
## 1  0.1985401 [1/m^2] MULTIPOLYGON (((-84.34919 3...
## 2  0.1258719 [1/m^2] MULTIPOLYGON (((-84.46957 3...
## 3  0.1232838 [1/m^2] MULTIPOLYGON (((-84.42613 3...
## 4  0.2162567 [1/m^2] MULTIPOLYGON (((-84.42334 3...
## 5  0.2802013 [1/m^2] MULTIPOLYGON (((-84.35705 3...
## 6  0.3896040 [1/m^2] MULTIPOLYGON (((-84.38269 3...
# Transform hospital data to the same CRS as tract data
hospital_data <- st_transform(hospital_data, st_crs(tract_acs))

5. Spatial Analysis of Hospital Access From an Equity Perspective

We will calculate hospital access by buffering of each census tract by 0.25 miles (~402.34 meters) and counting the number of hospitals within the buffer.

# Calculate number of hospitals within 0.25 miles of each tract
tract_acs$hospitals_nearby <- lengths(st_intersects(st_buffer(tract_acs, dist = 402.34), hospital_data))

# Calculate distance to nearest hospital for each tract
tract_acs$nearest_hospital <- st_distance(tract_acs, hospital_data) %>% apply(1, min)

print(tract_acs$hospitals_nearby)
##   [1]  0  0  3  0  0  1  0  0  0  0  0  0  0  3  0  1  0  0  0  0  2  0  0  0  0
##  [26]  6  1  0  0  0  0  0  0  0  0  1  0  1  0  0  0  0  0  1  0  2  0  0  0  0
##  [51]  0  0  0  0  1  4  0  1  6  8  0  0  0  0  2  0  0  8  0  0  0  0  0  1  1
##  [76]  0  0  2  1  0  0  1  0  0  4  0  0  1  0  5  5  0  0  1  1  0  0  9  1  0
## [101]  6  0  0  0  1  0  0  0  0  5  0  1  0  0  0  0  0  0  0  0  0  1  0  0  0
## [126]  1  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  2  0  0  0  0  0  0  0
## [151]  0  0  0  0  1  1  0  0  0  0  4  0  0  0  0  0  0  0  0  1  0 17  5  2  0
## [176]  0  0  0  0  0  1  0  0  0  0  1  0  0  0  0  0  0  1  1  1  0  1 12  0  0
## [201]  0  0  0  0  0  0  0  0  0  6  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
## [226]  0  0  6  0  1  0  0  2  1  0  0  1  0  0  0  1  0  1  0  0  2  0  0  0  0
## [251]  0  3  0  0  2  0  0  0  0  0  0  0  0  0  0  1  0  6  0  1  0  0  3  0  0
## [276]  1  0  1  0  0  0  0  0  0  1  0  0  0  0  3  2  0  0  4  0  0  9  1  0  4
## [301]  0  0  0  0  0  0  0  0  0  0  0  0  1  0  0  0  0  1  1  0  0  0  0  0  0
## [326]  1  0  0  0  0  1  1  0  0  1  0  0  0  1  1  0  0  2  0  1  0  0  0  0  8
## [351]  0  0  0  0  0  0  0  0  0  0  0  0  0  2  0  0  2  0  0  0  3  1  0  0  0
## [376]  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  1  3  0
## [401]  0  0  0  0  0  0  7  0  0  0  0  1  0  0  0  8  0  0  0  0  0  1  0  0  0
## [426]  1  2  1  0  1  0  0  2  1  0  0  0  0  0  0  0  0  0  0  0  1  0  0  0  0
## [451]  0  2  0  0  0  0  0  0  0  0  1  0  0  0  0  4  0  0  0  0  0 20  0  0  0
## [476]  1  0  2  0  1  0  0  0  0  0  0  0  0  0  0  0  1  0  0  0  0  1  0  0  0
## [501]  1  1  0  0  0  0  0  0  7  0  0  1  0
print(tract_acs$nearest_hospital)
##   [1]   432.054214  1381.140247   136.586597   890.152983  2172.188502
##   [6]   354.382774   682.958995  2072.340370  1257.766726  3553.566113
##  [11]   713.916615  7223.353614  2013.728654   104.862816  4904.610936
##  [16]     0.000000   576.174424  1340.628527  4100.951331  3895.531109
##  [21]   239.766153  1058.011449  2410.346186   687.900910  4044.728930
##  [26]   107.799324   124.940836  3404.079105   666.238598   530.516269
##  [31]  1727.487874  1569.132633   828.679683   595.860948  2836.967705
##  [36]     0.000000  6164.945535   179.057163  3446.835281  2912.257435
##  [41]   565.847665   640.697101  3455.697297     8.227623   834.359072
##  [46]    68.841153  1310.438705  1203.855896   494.817203  3073.441834
##  [51]  2675.415930  2190.441028  3740.430157   851.802058     0.000000
##  [56]   244.155760   892.187129   192.903685    22.036189     0.000000
##  [61]  1844.668143  1109.432019   730.238669  2598.383817   375.789070
##  [66]  1482.078386  8974.087478   138.182504   414.620802   426.249788
##  [71]   740.239711  5385.382222  4527.118142     0.000000   392.387283
##  [76]   451.056139  2500.067712   332.574291     0.000000  2529.137347
##  [81]  5073.116179     0.000000  1413.422953   816.464180   242.310445
##  [86]  1819.511098  3338.240218    23.309050   614.305870   125.792961
##  [91]   307.008500  3733.259777  5500.233539   339.879308   313.835988
##  [96]  2213.711606  1904.763430     0.000000     0.000000  2313.898193
## [101]    87.630323  4781.343739  1414.374004  3066.199541   318.614690
## [106]   870.932597  2227.765624  7200.595552  1598.300172   381.997512
## [111]  6661.624367   100.805369  4578.056055  1396.127544  2762.135674
## [116]  3092.212567  3416.874729  4889.588887   432.640459   416.663366
## [121]  1372.441122     0.000000  1160.337234   811.996452  3656.030634
## [126]    87.405906   292.269947  2342.265548   826.300078  1064.013570
## [131]  1428.075558  2252.110870  3106.498569  1834.123919  4035.558706
## [136]  1105.564125  2023.959698   637.510284  1908.205731  2551.993757
## [141]  3303.961429   647.614553     0.000000  5230.567234  5079.108250
## [146]  4753.272493   583.903165  3592.670226  3025.964912  1317.950980
## [151]  1677.627549  3348.260417  1373.878347  1057.546542   315.710958
## [156]     0.000000  1820.114379   479.411394   413.596994  1536.417766
## [161]     0.000000  1881.786114  2313.087458  2168.119945  1501.281200
## [166]  2763.255865  1183.572342  2652.861416  1800.722498    31.223943
## [171]  1734.134986     0.000000     0.000000     0.000000  4911.482741
## [176]  1633.063848  2632.123677  2425.232247   626.096641   755.752810
## [181]     0.000000  2976.227639   788.935227   803.367068  1980.098973
## [186]   406.398376  1613.015387  2304.755714   483.982915   828.982795
## [191]  2410.834018   917.726953   286.355309   343.369300     0.000000
## [196]  4386.761482   318.781102     0.000000   425.909987   918.917239
## [201]   530.469741  1908.452142  1768.195831   912.406080  3108.066225
## [206]   623.062243   508.322068   556.153153   520.901131     0.000000
## [211]  1279.087948  1710.428711  6688.342139  1452.964911 10323.416927
## [216]  6191.158773  3245.625245  8867.322329  4204.017058  7635.072003
## [221]  2638.619730  5299.313750  3955.369847  1560.108421  6874.357316
## [226] 12882.476464  5378.377723     0.000000   988.796904     0.000000
## [231]  3149.197794   976.005089     0.000000     0.000000   494.626112
## [236]  2480.486491     0.000000   746.596353   900.202869   897.510435
## [241]     0.000000  4277.752304     0.000000  2668.687198   563.086546
## [246]     0.000000  2484.470992  2272.611965   679.501289  2917.914612
## [251]  2601.841918    98.840876  2241.933421   799.625442   354.121654
## [256]   738.108810  4374.786554  3333.202922  1174.436076   667.129879
## [261]  1021.156876   682.952437  1023.243804   606.891598  4293.080375
## [266]    63.243784  1445.968016   315.262199   988.727036   351.727461
## [271]   685.932610  1702.880272     0.000000  1768.254167  3565.384599
## [276]   376.585068  1370.604870   258.728621  3629.831842  4034.164105
## [281]  2031.076420  2786.232301  3073.000448  1531.365332     0.000000
## [286]  2090.629722  1096.212892   897.022831  2166.968053    20.177673
## [291]     0.000000  1553.114913   582.364678     0.000000   967.956738
## [296]   805.255912     0.000000    67.729465   843.760935   230.728758
## [301]  9405.156722 10951.359105  4359.328288   844.917050  1586.369246
## [306]  2779.299297  1491.160516  2030.719310  2114.382516  2213.267312
## [311]  5432.611739  3493.635261   341.188114  1349.856669  6976.280354
## [316]  2769.876241   505.055708   387.064306     0.000000  1429.290564
## [321]  1415.820109  2211.033519  2498.887436  1703.272356   590.451972
## [326]    85.839508  1164.880060  1322.279455   819.193735  1901.548107
## [331]     0.000000    93.140643  1924.931242   494.236211     0.000000
## [336]  3343.245292  1219.585540   618.722753     0.000000     0.000000
## [341]  2569.523042  2360.189506     0.000000  5405.299425   283.467190
## [346]  3327.892401  3301.788349  2813.853285  1190.282391     0.000000
## [351]  1850.944878  1718.560691  1441.769997  1683.143089  1638.935539
## [356]  4899.495424  2000.522773   841.316501   608.970537   627.247389
## [361]  1601.401635  1309.831849  1409.652137     0.000000  2081.071624
## [366]   977.841995   181.433966  1298.591798  3535.338892  4571.158064
## [371]    65.805932   153.553161   528.436314  2008.134401  3709.282753
## [376]  2447.643382     0.000000  2002.085800  5306.046123  1636.569979
## [381]  8147.858818  2803.357882  1275.738119   960.237483  3087.668648
## [386]  2423.409891  1628.772023  4083.814961  4031.145113  1836.444554
## [391]  4658.174430  2659.007043  3129.826753  2709.173944  1267.201466
## [396]   683.830006  2034.833886   310.662131    70.725956  5311.254406
## [401]  4724.660203   957.475807  2562.609136  4011.335753 14958.326346
## [406]  3152.836589    39.579400  3140.249229  1445.137073  1457.282386
## [411]  4257.124746     0.000000  5373.049851  3255.095771  1129.380552
## [416]     0.000000  2568.070646  1497.699776  1252.767720  4252.197879
## [421]   520.976498    23.767973 11933.571558 16402.852999  1299.672429
## [426]     6.806092     0.000000    47.292315  6379.412097     0.000000
## [431]  2885.403028  2420.948235     0.000000     0.000000  4536.691790
## [436]   895.254408  1689.138765  1570.910520  1265.190606  2379.469591
## [441]  1491.967272   604.599707  2148.647797  3584.161802  2093.061483
## [446]   383.362039  2165.486091   440.164373  1559.550555   871.541582
## [451]  1630.046914     0.000000  1376.830055   462.182779  1270.546939
## [456]  2228.389461  7179.177601  6354.129059  1950.225685   813.815459
## [461]    46.410180   633.266022   886.880788  4629.408581  1308.817476
## [466]   378.084148  1545.473354 10533.209115  3993.833040  1275.738119
## [471]  2358.898355     0.000000   644.019248  3097.499046   597.878971
## [476]     0.000000  1626.061913     0.000000  3243.250192   112.487976
## [481]  1045.149600  1013.746554  1187.406180  4349.730734  2242.307208
## [486]  1575.911923  4918.010343  2173.370033  2228.299012  1248.865446
## [491]  2506.308761   340.362162  2611.309807  1558.129878  2471.398305
## [496]  1101.196859   328.964735   430.318385   974.873235  1248.227125
## [501]     0.000000    25.289464   631.835522  2272.319431   883.153858
## [506]   647.717629  2683.223799  2284.030808   149.699177  5183.055508
## [511]   587.455918    91.983170   705.966055

Then, we will create maps and graphs to visualize the distribution of hospitals and its relationship with socioeconomic variables we mentioned before.

First, I want to present the maps that show the hospitals condition in general:

# Map that shows the general condition of hospitals nearby (within 0.25 miles) for all tracts
tm_shape(tract_acs) +
  tm_polygons("hospitals_nearby", palette = "Greens", title = "Hospitals Nearby (within 0.25 miles)") +
  tm_shape(hospital_data) +
  tm_dots(col = "blue", size = 0.05, title = "Hospitals") +
  tm_layout(title = "Count of Hospitals Within 0.25 miles of Tract")
# A similar map that shows the distance to nearest hospital
tm_shape(tract_acs) +
  tm_polygons("nearest_hospital", palette = "Blues", title = "Nearest Hospital (m)") +
  tm_shape(hospital_data) +
  tm_dots(col = "blue", size = 0.05, title = "Hospitals") +
  tm_layout(title = "Distance to Nearest Hospital")
# Plot relationship between population density and distance to hospitals
ggplot(tract_acs, aes(x = total_populationE, y = nearest_hospital)) +
  geom_point() +
  labs(title = "Total Population vs. Distance to Hospital", x = "Population", y = "Distance to Hospital (m)")

ggplot(tract_acs, aes(x = population_density, y = nearest_hospital)) +
  geom_point() +
  labs(title = "Population Density vs. Distance to Hospital", x = "Population Density", y = "Distance to Hospital (m)")

# Map showing population density and distance to nearest hospital
tm_shape(tract_acs) +
  tm_polygons("total_populationE", palette = "Greens", title = "Total Population") +
  tm_shape(hospital_data) +
  tm_dots(size = 0.05, col = "blue", title = "Hospitals") +
  tm_layout(main.title = "Total Population and Distance to Hospitals")
tm_shape(tract_acs) +
  tm_polygons("population_density", palette = "Greens", title = "Population Density") +
  tm_shape(hospital_data) +
  tm_dots(size = 0.05, col = "blue", title = "Hospitals") +
  tm_layout(main.title = "Population Density and Distance to Hospitals")

Then, I’m going to compare that condition for different races:

# Plot relationship between percentage of Black population and distance to hospitals
ggplot(tract_acs, aes(x = black_pct, y = nearest_hospital)) +
  geom_point() +
  labs(title = "Percentage of Black Population vs. Distance to Hospital", x = "Black Population (%)", y = "Distance to Hospital (m)")

# Another way to show: instead of showing percentage, let's observe that for the total black populations
# First plot: Black Population vs. Distance to Hospital
plot_black <- ggplot(tract_acs, aes(x = race_blackE, y = nearest_hospital)) +
  geom_point() +
  labs(title = "Total Black Population vs. Distance to Hospital", 
       x = "Black Population (count)", y = "Distance to Hospital (m)")

# Second plot: White Population vs. Distance to Hospital
plot_white <- ggplot(tract_acs, aes(x = race_whiteE, y = nearest_hospital)) +
  geom_point() +
  labs(title = "Total White Population vs. Distance to Hospital", 
       x = "White Population (count)", y = "Distance to Hospital (m)")

# Arrange the two plots side by side
grid.arrange(plot_black, plot_white, ncol = 2)

Map can show this result more directly, I’m going to include all four targeted races here and compare them side by side

# Create individual maps
map_black <- tm_shape(tract_acs) +
  tm_polygons("race_blackE", palette = "Reds", title = "Black Population") +
  tm_shape(hospital_data) +
  tm_dots(size = 0.05, col = "blue", title = "Hospitals") +
  tm_layout(main.title = "Black Population and Distance to Hospitals")

map_white <- tm_shape(tract_acs) +
  tm_polygons("race_whiteE", palette = "Greens", title = "White Population") +
  tm_shape(hospital_data) +
  tm_dots(size = 0.05, col = "blue", title = "Hospitals") +
  tm_layout(main.title = "White Population and Distance to Hospitals")

map_asian <- tm_shape(tract_acs) +
  tm_polygons("race_asianE", palette = "Oranges", title = "Asian Population") +
  tm_shape(hospital_data) +
  tm_dots(size = 0.05, col = "blue", title = "Hospitals") +
  tm_layout(main.title = "Asian Population and Distance to Hospitals")

map_hispanic <- tm_shape(tract_acs) +
  tm_polygons("race_hispanicE", palette = "Purples", title = "Hispanic Population") +
  tm_shape(hospital_data) +
  tm_dots(size = 0.05, col = "blue", title = "Hospitals") +
  tm_layout(main.title = "Hispanic Population and Distance to Hospitals")

# Arrange the maps side by side using tmap_arrange
tmap_arrange(map_black, map_white, map_asian, map_hispanic, ncol = 2)

I’ll also show that condition regarding the income level:

# Plot relationship between income and distance to hospitals
ggplot(tract_acs, aes(x = median_incomeE, y = nearest_hospital)) +
  geom_point() +
  labs(title = "Income vs. Distance to Hospital", x = "Median Income", y = "Distance to Hospital (m)")

And lastly compare that conditions between different education levels with two maps:

# Create Bachelor's Degree map
bachelors_map <- tm_shape(tract_acs) +
  tm_polygons("education_bachelorsE", palette = "Blues", title = "Bachelor's Degree (%)") +
  tm_shape(hospital_data) +
  tm_dots(size = 0.02, col = "black", title = "Hospitals")

# Create High School map
highschool_map <- tm_shape(tract_acs) +
  tm_polygons("education_high_schoolE", palette = "Reds", title = "High School Education (%)") +
  tm_shape(hospital_data) +
  tm_dots(size = 0.02, col = "black", title = "Hospitals")

# Arrange maps side by side
tmap_arrange(bachelors_map, highschool_map)

Finally, we interpret the results to answer the question: Is the spatial distribution of hospitals in Fulton and DeKalb counties equitable?
Short answer: No, the spatial distribution of hospitals in Fulton and DeKalb counties is not entirely equitable.
Based on the results from the maps and plots, there are clear disparities based on region, race, income, and education levels. Predominantly Black and other minority communities, as well as lower-income areas, tend to have fewer hospitals or are located farther from the nearest hospital, while predominantly White and wealthier communities enjoy better access. The distribution of education levels follows a similar pattern to that of income, as education is often correlated with income levels and racial demographics. In other words, tracts with a higher percentage of residents with bachelor’s degrees tend to have better access to hospitals compared to those where high school graduates are more common. This is likely related to overall socioeconomic status, as higher educational attainment often aligns with wealthier areas. In areas with higher population densities, hospital accessibility is mixed. Some high-density areas have hospitals nearby, while others do not, suggesting an uneven distribution, which may be somewhat correlated with racial demographics. This could result in overcrowding in hospitals serving certain high-density areas.

Confusion: I initially performed the join and buffering around the centroid, but I found it unnecessary since the results were the same as what I’m getting now. Is this step actually necessary?