Introduction

Allegheny County (AC) is one of the metropolitan areas in Pennsylvania with a population of over 1.2 million. Home to Pittsburgh, the county is divided in 5,638 census tracts. The metro area was a manufacturing hub in the late 19th and early 20th centuries, attracting job-seekers from different parts of the United States. Since 1990, the population of the metro area has been on a decline. In this document, we will see the average commute length and median household income of residents in different census tracts within the metro area.

Data Sources

  • We leveraged the American Community Survey data (ACS) from the U.S. Census Bureau website. Particularly, we narrowed down our search to two relevant tables called B1903 (Median household income in the past 12 months (in 2021 inflation-adjusted dollars)) and B08303 (Travel Time To Work).
  • We downloaded the shapefiles of census tract from the US Census Bureau webpage.

Methodology

For data cleaning and visualization codes, please view RMD/PDF file.

Results

# reading the median income related csv file
median_income_allegheny <- read.csv('median_income.csv')
# head(my_data)

# colnames(median_income_allegheny)

# naming the columns
names(median_income_allegheny)[1] <- "label"
names(median_income_allegheny)[2] <- "median_income"

# creating a new column based on the label column and trimming any white space
median_income_allegheny <- median_income_allegheny %>% 
  # removing white space in the new Label column molded on label
  mutate(Label = str_trim(label)) %>% 
  # removing rows with 'Margin of Error'
  filter(Label != 'Margin of Error') %>% 
  # converting median_income to numbers
  mutate(median_income = as.numeric(gsub("[^0-9.]", "", median_income))) %>% 
  # lifting the median income row by 1
  mutate(median_income = lead(median_income)) %>% 
  # removing the rows with Estimate
  filter(Label != 'Estimate') %>% 
  # string operations on label column
  mutate(label = str_replace(label, ", Allegheny County, Pennsylvania", "")) %>%
  select(label, median_income) #choosing relevant columns for the matching purpose

Map: Median Household Income (2021) by Allegheny County Tracts

Interactive version

Please move the mouse cursor to learn about tract and the median income there.

allegheny_tracts_median_income <- st_transform(allegheny_tracts_median_income, crs=4326)

# Creating an interactive map using leaflet
leaflet(data = allegheny_tracts_median_income) %>%
  addTiles() %>%
  addPolygons(
    fillColor = ~colorNumeric("plasma", 
                              allegheny_tracts_median_income$median_income, 
                              reverse = TRUE)(allegheny_tracts_median_income$median_income),
    color = "black",
    weight = 1,
    opacity = 1,
    fillOpacity = 0.7,
    highlightOptions = highlightOptions(
      weight = 3,
      color = "#666",
      fillOpacity = 0.7,
      bringToFront = TRUE
    ),
    label = ~paste0(NAMELSAD, " has ",
                    "an average median income of USD ", median_income),
    labelOptions = labelOptions(
      style = list("font-weight" = "normal", padding = "3px 8px"),
      textsize = "15px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    pal = colorNumeric("plasma", 
                       allegheny_tracts_median_income$median_income, 
                       reverse = TRUE),
    values = allegheny_tracts_median_income$median_income,
    title = "Median Income",
    position = "bottomleft"
  )

Static Version

# Plotting using ggplot2
ggplot(data = allegheny_tracts_median_income) +
  geom_sf(aes(fill = median_income)) +
  scale_fill_viridis_c(option = "plasma", na.value = "grey50", direction=-1) +
  theme_minimal() +
  labs(
    title = "Median Household Income (2021) Across Census Tracts in Allegheny County, PA",
    fill = "Median Income"
  )

Results and Discussion

The center of the map is Downtown Pittsburgh. It appears that people living in Downtown or its vicinity census tracts have a considerably low median income than people living in the outskirts or semi-urban areas away from the center. There is a gradual increase in the median income in census tracts away from the city center.

Map: Average Commute Time (2021) by Allegheny County Tracts

To map commute time, we downloaded an ACS dataset, which possessed information on the number of people who fell under different commute time ranges per census tract. For example, the dataset offered information on the number of people in a given census tract which took 15-19 minutes to travel to work.

For simplicity and aggregated analysis, we summed up different commute ranges to arrive to three broad commute time ranges: 1) less than 30 minutes; 2) between 30 minutes and an hour; and 3) more than an hour.

The map below shows the population concentration per tract who fell under different commute time ranges.

# reading travel_time_to_work csv file
travel_time_to_work <- read.csv('traveltimetowork.csv')

# renaming the first column
names(travel_time_to_work)[1] <- "label"

travel_time_to_work <- travel_time_to_work %>% 
  # trimming any white space in the label column
  mutate(label = str_trim(label)) %>%
  # removing rows with 'Margin of Error' and 'Total population'
  filter(!label %in% c('Margin of Error', 'Total population')) %>% 
  # lifting rows other than those of the first columnn by 1
  # removing rows with Estimate in label column
  mutate(across(-1,lead)) %>% filter(label != 'Estimate') %>%
  # string operation in label column
  mutate(label = str_replace(label, "; Allegheny County; Pennsylvania", ""))
# converting a bunch of columns starting with word commute to numbers
allegheny_commute_to_work <- allegheny_commute_to_work %>% 
  mutate(across(starts_with("commute"), as.numeric))

# creating three new 'commute' variables for visualization purpose
# these variables aggregate information from other columns
allegheny_commute_to_work <- allegheny_commute_to_work %>% 
  mutate(commute_less_than_30_mins = commute_5_to_9_mins + commute_10_to_14_mins + commute_15_to_19_mins + commute_20_to_24_mins + commute_30_to_34_mins,
         commute_betwn_30_to_60_mins = commute_30_to_34_mins + commute_35_to_39_mins + commute_40_to_44_mins + commute_45_to_59_mins,
         commute_more_than_1_hour = commute_60_to_89_mins + commute_90_or_more_mins)

Interactive version

Please move the mouse cursor to learn about census tract and the commute time there.

# for mapping purpose, setting to certain coordinate system
allegheny_commute_to_work <- st_transform(allegheny_commute_to_work, crs=4326)

# Create an interactive map using leaflet
leaflet(data = allegheny_commute_to_work) %>%
  addTiles() %>%
  addPolygons(
    fillColor = ~colorNumeric("plasma", 
                              allegheny_commute_to_work$commute_more_than_1_hour, 
                              reverse = TRUE)(allegheny_commute_to_work$commute_more_than_1_hour),
    color = "black",
    weight = 1,
    opacity = 1,
    fillOpacity = 0.7,
    highlightOptions = highlightOptions(
      weight = 3,
      color = "#666",
      fillOpacity = 0.7,
      bringToFront = TRUE
    ),
    label = ~paste0(commute_more_than_1_hour, " people in ", NAMELSAD,
                    " commute more than an hour."),
    labelOptions = labelOptions(
      style = list("font-weight" = "normal", padding = "3px 8px"),
      textsize = "15px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    pal = colorNumeric("plasma", 
                       allegheny_commute_to_work$commute_more_than_1_hour, 
                       reverse = TRUE),
    values = allegheny_commute_to_work$commute_more_than_1_hour,
    title = "Concentration of people by census tract<br>who commute more than an hour",
    position = "bottomleft"
  )

Static Version

From the map below, it appears that a majority of people in census tracts away from the city center commute less than 30 minutes to work. This could be for a variety of reasons. For example, these tracts or their nearby tracts may have manufacturing plants or businesses that offer employment–to reach which, it takes under 30 minutes. Other explanation can be that these census tracts have direct and most efficient transportation routes, saving people time to commute. An alternative explanation can be that a majority of people in these census tracts may work virtually or part-time.

# Plot using ggplot2
ggplot(data = allegheny_commute_to_work) +
  geom_sf(aes(fill = commute_less_than_30_mins)) +
  scale_fill_viridis_c(option = "plasma", na.value = "grey50", direction = -1) +
  theme_minimal() +
  labs(
    title = "Concentration of people who commute less than 30 minutes in AC, PA",
    fill = "Commute time less than 30 minutes"
  )

Compared to the map above, this graph becomes lighter in the color legend intensity, denoting that at a higher level, there are relatively less people per census tract who travel 30 to 60 minutes for their work. However, there are pockets of tracts, where there are more people who have to travel between 30 to 60 minutes to work. This could be because they have to travel to another census tracts or that they may not have access to direct public transportation and therefore may have to walk as a part of their commute schedule. Like in the previous map, there are fewer people in Downtown Pittsburgh areas who have to commute more than 30 minutes to commute to work, which may also denote most of the jobs are concentrated in the Downtown area.

# Plot using ggplot2
ggplot(data = allegheny_commute_to_work) +
  geom_sf(aes(fill = commute_betwn_30_to_60_mins)) +
  scale_fill_viridis_c(option = "plasma", na.value = "grey50", direction = -1) +
  theme_minimal() +
  labs(
    title = "Concentration of people who commute between 30 to 60 minutes in AC, PA",
    fill = "Commute time between 30 to 60 minutes"
  )

This final static map is a lighter version of all the maps, symbolizing there are a fewer people per census tract who have to commute over an hour to travel to work. However, there are people in certain census tracts, mainly on the outskirts such as the one in the south (the fat C-shaped census tract) with over 600 people who has to commute over an hour to travel to work. From planning and equity points of view, it is important to see who reside in these neighborhoods, what their modes of transportation are, their median income, and the access to job opportunities they possess.

# Plot using ggplot2
ggplot(data = allegheny_commute_to_work) +
  geom_sf(aes(fill = commute_more_than_1_hour)) +
  scale_fill_viridis_c(option = "plasma", na.value = "grey50", direction = -1) +
  theme_minimal() +
  labs(
    title = "Concentration of people who commute more than an hour in AC, PA",
    fill = "Commute time more than an hour"
  )

Conclusions and Future Direction

The American Community Survey data on household median income and commute time to travel could be useful to demonstrate any correlation between those variables, and as such, in the future research, it would be beneficial to display both features on one map to show any correlation. Furthermore, it will also be important to demonstrate who lives in these census tracts with relatively longer commute times and understand the infrastructure and job opportunities there.