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)
| 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)
| 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?