This document explores a handful of characteristics of Buncombe County, NC which could be relevant to planning efforts in the area. It includes an exploration of 2022 American Community Survey (ACS) estimates at the block group level, some basic summary statistics of automobile accidents over the past decade, and federal grants from the US Department of Transportation.
knitr::opts_chunk$set(echo = TRUE)
knitr::opts_chunk$set(message = FALSE)
knitr::opts_chunk$set(warning = FALSE)
buncombe16_clean <- readRDS('buncombe16_clean.rds')
buncombe22_clean <- read_rds('buncombe22_clean.rds')
acs_long <- readRDS('acs_long.rds')
The following table and plot analyze change from 2016 ACS estimates to 2022. The figures listed represent estimated averages for all Census blocks in Buncombe County in their respective years. Notably, Buncombe County witnessed a population increase of 19,337 or about 7.7%, and an increase in average median household income of about 13% after adjusting for inflation. The county had about 12% total more housing units in 2022 than 2016, yet the vacancy rate appears to have considerably increased from 11.98% to 20.66%. Additionally, the unemployment and poverty rate both decreased while the population under 18 stayed the same while the elderly population (over 65) increased somewhat.
knitr::opts_chunk$set(echo = TRUE)
knitr::opts_chunk$set(message = FALSE)
knitr::opts_chunk$set(warning = FALSE)
acs_table <- kable(acs_long, format = 'markdown')
acs_table <- kable(acs_long, format = 'markdown', caption = "Comparison of ACS Variables from 2016 to 2022") %>%
kable_styling(bootstrap_options = c('striped', 'hover'), full_width = F) %>%
scroll_box(width = '100%', height = '500px')
acs_table
| Variable | 2016 | 2022 |
|---|---|---|
| TotalPopulation | 250112 | 269449 |
| AvgMedianHHIncome | 48218.34 | 70803.58 |
| AvgMeanAge | 42.08 | 43.93 |
| TotalHousingUnits | 115984 | 130081 |
| AvgOwnerOccupancyRate | 56.46 | 52.68 |
| AvgRenterOccupancyRate | 31.56 | 26.66 |
| AvgVacancyRate | 11.98 | 20.66 |
| AvgElderlyPop | 17.84 | 21.40 |
| AvgMinorPop | 18.80 | 18.30 |
| AvgNewResidents | 4.57 | 4.94 |
| AvgPovertyRate | 5.99 | 4.76 |
| AvgUnemploymentRate | 6.36 | 3.47 |
| AvgCommuteLessThan30Min | 68.46 | 64.09 |
| AvgCommuteMoreThan45Min | 2.39 | 2.32 |
| AvgDroveAlonetoWork | 73.12 | 70.27 |
| AvgCarpooledtoWork | 7.53 | 6.55 |
| AvgWhite | 88.25 | 85.22 |
| AvgBlack | 6.84 | 5.88 |
| AvgAsian | 1.25 | 1.24 |
| AvgHispanic | 6.06 | 6.41 |
| AvgNativAmer | 0.32 | 0.21 |
knitr::opts_chunk$set(echo = TRUE)
knitr::opts_chunk$set(message = FALSE)
knitr::opts_chunk$set(warning = FALSE)
acs_long <- acs_long %>%
pivot_longer(
cols = c(`2016`, `2022`),
names_to = 'Year',
values_to = 'Value')
acs_plot <- ggplot(acs_long[9:42, ], aes(x = Variable, y = as.numeric(Value),
fill = Year)) +
geom_bar(stat = 'identity', position = position_dodge(width = 0.7)) +
theme_minimal() +
labs(title = 'Comparison of 2016 & 2022 ACS Variables',
x = '', y = '', fill = 'Year') +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = 'bottom',
title = element_text(family = 'Artifakt Element'),
legend.text = element_text(family = 'Artifakt Element'))
acs_plot
Given the apparantly dramatic uptick in vacancy rates, closer examination of the Census blocks reveals many are clustered in the neighborhoods of Montreat, Black Mountain, Weaverville, and Royal Pines.
# Mapping Vacant Units
# 2016
vacancy16 <- buncombe16_clean %>%
ggplot() +
geom_sf(aes(fill = PctVacant)) +
scale_fill_met_c(name = 'OKeeffe2') +
labs(title = '2016', fill = 'Vacancy Rate (%)') +
theme_void() +
theme(legend.position = 'none',
plot.title = element_text(hjust = 0.5, family = 'Artifakt Element'),
legend.title = element_text(family = 'Artifakt Element'))
# Create the second plot for 2022
vacancy22 <- buncombe22_clean %>%
ggplot() +
geom_sf(aes(fill = PctVacant)) +
scale_fill_met_c(name = 'OKeeffe2') +
labs(title = '2022', fill = 'Vacancy Rate (%)') +
theme_void() +
theme(legend.position = 'none',
plot.title = element_text(hjust = 0.5, family = 'Artifakt Element'),
legend.title = element_text(family = 'Artifakt Element'))
vacancy_combined <- ggarrange(vacancy16, vacancy22, nrow = 1,
common.legend = TRUE, legend = 'bottom')
vacancy_combined <- annotate_figure(vacancy_combined, top = text_grob('Buncombe County Vacancy Rates',
just = 'centre', size = 16,
family = 'Artifakt Element', face = 'bold'))
vacancy_combined
Buncombe County had an average vacancy rate of 11.98% in 2016 and 20.66% in 2022. In 2016, there were 70, 45%, Census block groups where the vacancy rate was above the average and 77, 46%, in 2022.
The interactive map below features 2022 5-year ACS estimates for Buncombe County acquired through the US Census Bureau’s public API. By navigating the map, users can examine each block group’s characteristics including selected demographics, commute variables, and unemployment rates among others. Note that “New Residents” are those who lived in a different Metropolitan Statistical Area than Asheville within the past year.
knitr::opts_chunk$set(echo = TRUE)
knitr::opts_chunk$set(message = FALSE)
knitr::opts_chunk$set(warning = FALSE)
# interactive map
pal <- RColorBrewer::brewer.pal(n = 12, name = 'YlGnBu')
tmap_mode('view')
map <- tm_shape(buncombe22_clean) +
tm_basemap('OpenStreetMap') +
tm_polygons("TotPopE",
title = "Total Population",
palette = pal,
style = 'quantile', n = 8,
alpha = 0.25,
border.col = "black",
id = 'NAME',
popup.vars = c('Total Population' = 'TotPopE',
'Median Household Income' = 'MedHHIncomeE',
'Median Structure Year' = 'MedianStructureYearE',
'Total Housing Units' = 'TotHousingUnitsE',
'Percent Vacant Housing' = 'PctVacant',
'Percent Owner Occupied' = 'PctOwnerOcc',
'Percent Renter Occupied' = 'PctRenterOcc',
'Percent White' = 'PctWhite',
'Percent Black' = 'PctBlack',
'Percent Hispanic' = 'PctHispanic',
'Percent Asian' = 'PctAsian',
'Percent Native American' = 'PctIndian',
'Percent Elderly' = 'PctElderly',
'Percent Minors' = 'PctMinors',
'Percent New Residents' = 'PctMigrants',
'Percent Less Than 30 Minute Commute' = 'PctLessThan30MinCommute',
'Percent More Than 45 Minute Commute' = 'PctMoreThan45MinCommute',
'Percent Drove Alone to Work' = 'PctDroveAlone',
'Percent Carpooled to Work' = 'PctCarpooled',
'Percent Unemployed' = 'PctUnemployed',
'Percent Poverty' = 'PctPov'))
map
The following plots and map were created by acquiring a shapefile containing crash data from the North Carolina Department of Transportation ArcGIS Online Profile. Further analysis of this dataset could include correlations between various conditions such as alcohol, weather, or speed; performing a spatial join with NCDOT’s Annual Average Daily Traffic volume maps to assess where the highest incidents per capita (or vehicle, in this case) occur.
knitr::opts_chunk$set(echo = TRUE)
knitr::opts_chunk$set(message = FALSE)
knitr::opts_chunk$set(warning = FALSE)
# load crash data and filter to buncombe
crash_shp <- read_rds('crash_shp.rds')
# crash summary stats
# wrangle time data first...
crash_shp <- crash_shp %>%
mutate(Date = str_remove(Date, "\\."),
Date = mdy(Date))
crash_shp <- crash_shp %>%
mutate(Year = year(Date),
Month = month(Date))
crash_shp <- crash_shp %>%
mutate(Hour = hour(hm(Time)),
Minute = minute(hm(Time)))
crash_severity_data <- st_drop_geometry(crash_shp) %>%
filter(Crash_Seve %in% c('K', 'A')) %>%
group_by(Month, Crash_Seve) %>%
summarise(Count = n(), .groups = 'drop')
knitr::opts_chunk$set(echo = TRUE)
knitr::opts_chunk$set(message = FALSE)
knitr::opts_chunk$set(warning = FALSE)
# monthly distribution of fatal/serious injuries plot
p1 <- crash_severity_data %>%
ggplot(aes(x = factor(Month, levels = 1:12), y = Count,
fill = Crash_Seve)) +
geom_bar(stat = 'identity', position = 'dodge') +
scale_fill_viridis_d(begin = 0.3, end = 0.8, direction = 1, name = 'Severity',
labels = c('K' = 'Fatal', 'A' = 'Serious')) +
labs(x = '', y = 'Number of Crashes',
title = 'Distribution of Fatal and Serious Injuries by Month',
subtitle = 'Buncombe County, NC | 2014 - 2023') +
theme_minimal(base_family = 'Artifakt Element') +
theme(legend.position = 'bottom') +
scale_x_discrete(labels = c("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"))
p1
fatal_ratio <- crash_severity_data %>%
group_by(Month) %>%
summarise(TotalFatal = sum(Count[Crash_Seve == 'K']),
TotalCrashes = sum(Count),
FatalRatio = TotalFatal / TotalCrashes) %>%
arrange(desc(FatalRatio))
fatal_ratio <- fatal_ratio %>%
mutate(MonthName = factor(Month, levels = 1:12,
labels = month.name),
FatalRatio = round(FatalRatio*100, 2))
fatal_ratio <- fatal_ratio[c(5, 3, 2, 4)]
fatal_ratio <- fatal_ratio %>%
rename(`Total Fatal` = TotalFatal,
`Total Crashes` = TotalCrashes,
`Fatality Ratio` = FatalRatio,
Month = MonthName)
# Create a kable
kable_fatal_ratio <- kable(fatal_ratio, format = 'markdown',
col.names = c('Month Name', 'Total Crashes', 'Total Fatal', 'Fatality Ratio'),
caption = 'Most Fatal Months (Greatest to Least)')
kable_fatal_ratio
| Month Name | Total Crashes | Total Fatal | Fatality Ratio |
|---|---|---|---|
| January | 65 | 31 | 47.69 |
| March | 59 | 28 | 47.46 |
| September | 75 | 32 | 42.67 |
| November | 60 | 25 | 41.67 |
| December | 64 | 24 | 37.50 |
| February | 70 | 26 | 37.14 |
| June | 76 | 28 | 36.84 |
| July | 63 | 23 | 36.51 |
| October | 86 | 30 | 34.88 |
| April | 63 | 21 | 33.33 |
| August | 68 | 22 | 32.35 |
| May | 67 | 17 | 25.37 |
knitr::opts_chunk$set(echo = TRUE)
knitr::opts_chunk$set(message = FALSE)
knitr::opts_chunk$set(warning = FALSE)
# crash severity by accident type
crash_sev_bytype <- st_drop_geometry(crash_shp) %>%
group_by(Crash_Seve, Crash_Type) %>%
summarise(Count = n(), .groups = 'drop')
p2 <- crash_sev_bytype %>%
ggplot(aes(x = Crash_Type, y = Count, fill = Crash_Seve)) +
geom_bar(stat = 'identity', position = 'dodge') +
scale_fill_viridis_d(name = 'Severity', labels = c('K' = 'Fatal', 'A' = 'Serious'),
option = 'cividis') +
labs(x = '', y = 'Number of Crashes',
title = 'Crash Severity by Type') +
theme_minimal(base_family = 'Artifakt Element') + coord_flip()
p2
knitr::opts_chunk$set(echo = TRUE)
knitr::opts_chunk$set(message = FALSE)
knitr::opts_chunk$set(warning = FALSE)
crash_shp <- crash_shp %>%
mutate(Fatal = ifelse(Crash_Seve == 'K', 'Fatal', 'Non-fatal'))
tmap_mode('view')
# Create heatmap
heat_map <- tm_shape(crash_shp) +
tm_basemap('OpenStreetMap') +
tm_dots(col = "Fatal", palette = c("red", "grey"), size = 0.1,
style = "fixed", alpha = 0.5,
title = "Crash Severity",
id = 'On_Road',
popup.vars = c('Date' = 'Date',
'Time' = 'Time',
'Crash Severity' = 'Fatal',
'Crash Type' = 'Crash_Type',
'Number of Vehicles' = 'Num_Vehicl',
'Weather' = 'Weather',
'Alcohol Related' = 'Alcohol_Re',
'Speed Related' = 'Speed_Rela',
'Unbelted Driver' = 'Unbelted_D',
'Heavy Truck' = 'Heavy_Truc')) +
tm_layout(main.title = 'Buncombe County, NC Auto Accidents\n2014-2023',
main.title.position = "center",
main.title.size = 1.5,
frame = FALSE)
heat_map
To get a sense of many federal transportation dollars are reaching Buncombe County, I searched usaspending.gov for all grants where the Department of Transportation was the prime award funding agency and Buncombe County was listed as the subawardee. I repeated this process for the Land of Sky Regional Council (LOSRC). Between 2011 and 2022, Buncombe County was listed as a subawardee on 28 DOT funded grants while LOSRC was listed as a subawardee on 34. LOSRC primarily received funding through 20.505, Highway Planning and Construction Program.
knitr::opts_chunk$set(echo = TRUE)
knitr::opts_chunk$set(message = FALSE)
knitr::opts_chunk$set(warning = FALSE)
buncombe_sub <- readRDS('buncombe_sub.rds')
losrc_sub <- readRDS('losrc_sub.rds')
# wrangle buncombe co. subaward data
buncombe_sub <- buncombe_sub %>%
filter(prime_award_awarding_agency_name == 'Department of Transportation (DOT)')
buncombe_sub <- buncombe_sub %>%
separate(col = prime_award_cfda_numbers_and_titles,
into = c('CFDA', 'Description'),
sep = ':\\s*',
extra = 'merge')
buncombe_summary <- buncombe_sub %>%
group_by(CFDA) %>%
summarise(Count = n(),
TotalSubaward = sum(subaward_amount))
knitr::opts_chunk$set(echo = TRUE)
knitr::opts_chunk$set(message = FALSE)
knitr::opts_chunk$set(warning = FALSE)
# create dataframe legend
legend <- data.frame(
CFDA = c('20.703', '20.601', '20.526', '20.516', '20.509', '20.505'),
Description = c('Interagency Hazardous Materials Public Sector Training and Planning Grants',
'Alcohol Impaired Driving Countermeasures Incentive Grants',
'Buses and Bus Facilities Formula, Competitive, and Low or No Emissions Program',
'Job Access and Reverse Commute Program',
'Formula Grants for Rural Areas and Tribal Transit Program',
'Metropolitan Transportation Planning and State and Non-Metropolitan Planning and Research'))
kable_output <- kable(legend, format = 'markdown', caption = 'List of CFDA Programs')
kable_output
| CFDA | Description |
|---|---|
| 20.703 | Interagency Hazardous Materials Public Sector Training and Planning Grants |
| 20.601 | Alcohol Impaired Driving Countermeasures Incentive Grants |
| 20.526 | Buses and Bus Facilities Formula, Competitive, and Low or No Emissions Program |
| 20.516 | Job Access and Reverse Commute Program |
| 20.509 | Formula Grants for Rural Areas and Tribal Transit Program |
| 20.505 | Metropolitan Transportation Planning and State and Non-Metropolitan Planning and Research |
p3 <- buncombe_summary %>%
ggplot(aes(x = CFDA, y = TotalSubaward, fill = CFDA)) +
geom_col() +
scale_fill_viridis_d() +
labs(x = '', y = 'Total Subaward',
title = 'Buncombe County DOT Grant Funding',
subtitle = '2011-2022') +
theme_minimal(base_family = 'Artifakt Element') +
scale_y_continuous(labels = scales::dollar_format(prefix = '$', suffix = '')) +
theme(legend.position = 'none') +
coord_flip()
p3