Lab 2: Spatial Analysis and Visualization

Healthcare Access and Equity in Pennsylvania

Author

Mackenna Amole

Published

February 24, 2026

Assignment Overview

Learning Objectives: - Apply spatial operations to answer policy-relevant research questions - Integrate census demographic data with spatial analysis - Create publication-quality visualizations and maps - Work with spatial data from multiple sources - Communicate findings effectively for policy audiences


Part 1: Healthcare Access for Vulnerable Populations

Research Question

Which Pennsylvania counties have the highest proportion of vulnerable populations (elderly + low-income) living far from hospitals?

Your analysis should identify counties that should be priorities for healthcare investment and policy intervention.

Step 1: Data Collection (5 points)

Load the required spatial data: - Pennsylvania county boundaries - Pennsylvania hospitals (from lecture data) - Pennsylvania census tracts

Your Task:

# Load required packages
library(sf)
library(tidyverse)
library(tigris)
library(tidycensus)
library(scales)
library(patchwork)
library(here)

# Load spatial data
pa_counties <- st_read(here("labs/lab_2/CPLN-5920-SP26-main/lectures/week_04/data/Pennsylvania_County_Boundaries.shp"))
#pa_counties <- st_read(here("CPLN-5920-SP26-main/CPLN-5920-SP26-main/lectures/week_04/data/Pennsylvania_County_Boundaries.shp"))
#districts <- st_read(here("CPLN-5920-SP26-main/CPLN-5920-SP26-main/lectures/week_04/data/districts.geojson"))
districts <- st_read(here("labs/lab_2/CPLN-5920-SP26-main/lectures/week_04/data/districts.geojson"))
#hospitals <- st_read(here("CPLN-5920-SP26-main/CPLN-5920-SP26-main/lectures/week_04/data/hospitals.geojson"))
hospitals <- st_read(here("labs/lab_2/CPLN-5920-SP26-main/lectures/week_04/data/hospitals.geojson"))
census_tracts <- tracts(state = "PA", cb = TRUE)
metro_areas <- core_based_statistical_areas(cb = TRUE)



# Check that all data loaded correctly
glimpse(pa_counties)
glimpse(districts)
glimpse(hospitals)


# Check coordinates
st_crs(pa_counties)
st_crs(districts)
st_crs(hospitals)
st_crs(metro_areas)
st_crs(census_tracts)

Questions - How many hospitals are in your dataset? 223

  • How many census tracts? 3,445

  • What coordinate reference system is each dataset in? WGS84, except for metro_areas and census_tracts which are in NAD83


Step 2: Get Demographic Data

Use tidycensus to download tract-level demographic data for Pennsylvania.

Required variables: - Total population - Median household income - Population 65 years and over (you may need to sum multiple age categories)

Your Task:

# Get demographic data from ACS

# Get demographic data from ACS (tract-level, all of PA)
demo_vars <- c(
  total_pop = "B01003_001",
  med_income = "B19013_001",
  male_65_66 = "B01001_020",
  male_67_69 = "B01001_021",
  male_70_74 = "B01001_022",
  male_75_79 = "B01001_023",
  male_80_84 = "B01001_024",
  male_85_plus = "B01001_025",
  female_65_66 = "B01001_044",
  female_67_69 = "B01001_045",
  female_70_74 = "B01001_046",
  female_75_79 = "B01001_047",
  female_80_84 = "B01001_048",
  female_85_plus = "B01001_049"
)
tract_demographics <- get_acs(
  geography = "tract",
  state = "PA",
  variables = demo_vars,
  year = 2022,
  survey = "acs5",
  geometry = TRUE,
  output = "wide"
)
tract_demographics <- tract_demographics %>%
  mutate(
    pop_65_plus =
      male_65_66E + male_67_69E + male_70_74E +
      male_75_79E + male_80_84E + male_85_plusE +
      female_65_66E + female_67_69E + female_70_74E +
      female_75_79E + female_80_84E + female_85_plusE
  )
#tract demographics already has geometry

#tracts with missing income
sum(is.na(tract_demographics$med_incomeE))

#median income PA tracts
median(tract_demographics$med_incomeE, na.rm = TRUE)

Questions - What year of ACS data are you using? 2022

  • How many tracts have missing income data? 63

  • What is the median income across all PA census tracts? $70,188


Step 3: Define Vulnerable Populations

Identify census tracts with vulnerable populations based on TWO criteria: 1. Low median household income 2. Significant elderly population

Your Task:

#identifying vulnerable: $45k b/c less than median but more than poverty level in PA, 25% b/c PA average is about 20% so this represents a higher concentration
tract_demographics <- tract_demographics %>%
  mutate(
    pct_65_plus = pop_65_plus / total_popE,
    vulnerable = if_else(
      med_incomeE < 45000 & pct_65_plus > 0.25,
      TRUE, FALSE
    )
  )


# Filter for vulnerable tracts based on your criteria

vulnerable_tracts <- tract_demographics %>%
  filter(vulnerable == TRUE)

Questions - What income threshold did you choose and why? I chose 45,000 as the median income threshold. The PA tract average was just slightly over 70,000 and the PA poverty level is approx. 20,000 for 2-person household/approx. 25,000 for 3-person household; 45,000 falls in the middle of this range, highlighting people who are low-income and below state median but not necessarily living below the poverty level.

  • What elderly population threshold did you choose and why? I chose 25% age 65+. The PA average population over 65 is about 20% (according to a quick google search for the state scale), so I raised this slightly to identify places with even higher than average concentrations.

  • How many tracts meet your vulnerability criteria? 78

  • What percentage of PA census tracts are considered vulnerable by your definition? approx. 2.26%


Step 4: Calculate Distance to Hospitals

For each vulnerable tract, calculate the distance to the nearest hospital.

Your Task:

# Transform to appropriate projected CRS
vulnerable_proj <- st_transform(vulnerable_tracts, 2272)
hospitals_proj <- st_transform(hospitals, 2272)

#tract centroids
tract_centroids <- st_centroid(vulnerable_proj)

#distance matrix to all hospitals
distance_matrix <- st_distance(tract_centroids, hospitals_proj)

#min dist to nearest hospital in ft
nearest_distance_ft <- apply(distance_matrix, 1, min)

#ft to miles
vulnerable_proj$nearest_hospital_miles <- as.numeric(nearest_distance_ft) / 5280

#avg dist
mean_distance <- mean(vulnerable_proj$nearest_hospital_miles, na.rm = TRUE)
mean_distance

#max dist
max_distance <- max(vulnerable_proj$nearest_hospital_miles, na.rm = TRUE)
max_distance

#number of tracts > 15 miles from hospital
num_far <- sum(vulnerable_proj$nearest_hospital_miles > 15)
num_far

Requirements: - Use an appropriate projected coordinate system for Pennsylvania - Calculate distances in miles - Explain why you chose your projection

Questions - What is the average distance to the nearest hospital for vulnerable tracts? approx. 3.88 miles

  • What is the maximum distance? 18.13 miles

  • How many vulnerable tracts are more than 15 miles from the nearest hospital? 3

  • Which projection did you choose and why? I transformed the data to Pennsylvania State Plane South (EPSG:2272). This is a projected coordinate system designed specifically for Pennsylvania (the southern half of the state, so chose south just because Philadelphia and Pittsburgh are both in the southern half).


Step 5: Identify Underserved Areas

Define “underserved” as vulnerable tracts that are more than 15 miles from the nearest hospital.

Your Task:

# Create underserved variable
vulnerable_proj <- vulnerable_proj %>%
  mutate(underserved = nearest_hospital_miles > 15)

#number underserved tracts
num_underserved <- sum(vulnerable_proj$underserved)
num_underserved

#% vulnerable tracts that are underserved
pct_underserved <- (3/78 *100)
pct_underserved

Questions to answer: - How many tracts are underserved? 3

  • What percentage of vulnerable tracts are underserved? 3.85%

  • Does this surprise you? Why or why not? This number feels low to me, especially with 78 vulnerable tracts identified. However, it does seem like a good thing to know that even though vulnerable tracts exist across the state, there are not nearly as many which are a far distance from a hospital.


Step 6: Aggregate to County Level

Use spatial joins and aggregation to calculate county-level statistics about vulnerable populations and hospital access.

Your Task:

#county FIPS from tract GEOID
vulnerable_proj <- vulnerable_proj %>%
  mutate(county_fips = substr(GEOID, 1, 5))

#aggregate by county
county_summary <- vulnerable_proj %>%
  st_drop_geometry() %>%
  group_by(county_fips) %>%
  summarise(
    vulnerable_tracts = n(),
    underserved_tracts = sum(underserved),
    pct_underserved = underserved_tracts / vulnerable_tracts,
    avg_distance_miles = mean(nearest_hospital_miles, na.rm = TRUE),
    total_vulnerable_pop = sum(pop_65_plus, na.rm = TRUE),
    .groups = "drop"
  )

#join county names using FIPS column from shapefile
county_summary <- county_summary %>%
  left_join(
    pa_counties %>%
      st_drop_geometry() %>%
      mutate(FIPS_COUNT = sprintf("%05d", as.numeric(FIPS_COUNT))) %>%  # pad to 5 digits
      select(FIPS_COUNT, COUNTY_NAM),
    by = c("county_fips" = "FIPS_COUNT")
  ) %>%
  arrange(desc(pct_underserved))

#top 5 counties by % underserved tracts
head(county_summary, 5)

#counties with most vulnerable population 15+ miles from hospitals
county_population_far <- vulnerable_proj %>%
  filter(underserved) %>%
  st_drop_geometry() %>%
  group_by(county_fips) %>%
  summarise(vulnerable_people_far = sum(pop_65_plus, na.rm = TRUE)) %>%
  left_join(
    pa_counties %>%
      st_drop_geometry() %>%
      mutate(FIPS_COUNT = sprintf("%05d", as.numeric(FIPS_COUNT))) %>%  # pad to 5 digits
      select(FIPS_COUNT, COUNTY_NAM),
    by = c("county_fips" = "FIPS_COUNT")
  ) %>%
  arrange(desc(vulnerable_people_far))

head(county_population_far, 5)

#5 counties highest % underserved vulnerable tracts
head(county_summary, 5) 
#county FIPS 42033, 42053, 42067, 42003, 42007
  #42033 – Clearfield County, PA
  #42053 – Forest County, PA
  #42067 – Juniata County, PA
  #42003 – Allegheny County, PA
  #42007 – Beaver County, PA

# Which counties have the most vulnerable people living far from hospitals?
head(county_population_far, 5)
# county FIPS 42033, 42053, 42067

Required county-level statistics: - Number of vulnerable tracts - Number of underserved tracts
- Percentage of vulnerable tracts that are underserved - Average distance to nearest hospital for vulnerable tracts - Total vulnerable population

Questions to answer: - Which 5 counties have the highest percentage of underserved vulnerable tracts? 42033/Clearfield County, 42053/Forest County, 42067/Juniata County, 42003/Allegheny County, 42007/Beaver County

  • Which counties have the most vulnerable people living far from hospitals? 42033/Clearfield County (987 people), 42053/Forest County (802 people), 42067/Juniata County (492 people)

  • Are there any patterns in where underserved counties are located? Aside from Juniata County, 4 of the 5 identified counties are in Western PA. They otherwise have no similar geographic patterns, except that all except for Allegheny County (containing Pittsburgh and its suburbs) may be considered more rural.


Step 7: Create Summary Table

Create a professional table showing the top 10 priority counties for healthcare investment.

Your Task:

# Create and format priority counties table
# Map county FIPS codes to actual county names
priority_counties <- county_population_far %>%
  filter(county_fips %in% c("42033", "42053", "42067")) %>%  # only your priority counties
  mutate(
    COUNTY_NAME = case_when(
      county_fips == "42033" ~ "Clearfield County",
      county_fips == "42053" ~ "Forest County",
      county_fips == "42067" ~ "Juniata County"
    )
  ) %>%
  left_join(
    county_summary %>%
      select(county_fips, pct_underserved) %>%
      mutate(pct_underserved = percent(pct_underserved, accuracy = 1)),
    by = "county_fips"
  ) %>%
  mutate(
    vulnerable_people_far = comma(vulnerable_people_far)  # format numbers
  ) %>%
  select(
    COUNTY_NAME,
    vulnerable_people_far,
    pct_underserved
  ) %>%
  arrange(desc(vulnerable_people_far))  # sort by number of vulnerable people far from hospitals

# Create formatted table with kable
knitr::kable(
  priority_counties,
  caption = "Top Pennsylvania Counties for Healthcare Investment: Vulnerable Populations Far from Hospitals",
  col.names = c("County", "Vulnerable People >15 miles from Hospital", "% of Vulnerable Tracts Underserved"),
  align = c("l", "r", "r")
)

Requirements: - Use knitr::kable() or similar for formatting - Include descriptive column names - Format numbers appropriately (commas for population, percentages, etc.) - Add an informative caption - Sort by priority (you decide the metric)


Part 2: Comprehensive Visualization

Using the skills from Week 3 (Data Visualization), create publication-quality maps and charts.

Map 1: County-Level Choropleth

Create a choropleth map showing healthcare access challenges at the county level.

Your Task:

# Create county-level access map
county_map <- pa_counties %>%
  st_transform(4326) %>%
  left_join(
    county_summary %>%
      mutate(county_fips3 = substr(county_fips, 3, 5)) %>%  # drop the 42
      select(county_fips3, pct_underserved),
    by = c("FIPS_COUNT" = "county_fips3")
  )

hospitals_map <- hospitals %>%
  st_transform(4326)

county_map <- county_map %>%
  mutate(underserved_category = case_when(
    is.na(pct_underserved) ~ "No data",
    pct_underserved == 0 ~ "0%",
    pct_underserved > 0 ~ ">0%"
  ))

#choropleth with hospital points
ggplot() +
  geom_sf(data = county_map, aes(fill = underserved_category), color = "white", size = 0.3) +
  geom_sf(data = hospitals_map, shape = 21, color = "black", fill = "violetred4", size = 2, alpha = 0.7) +
  scale_fill_manual(
    values = c("0%" = "lightblue", ">0%" = "lightskyblue4", "No data" = "grey90"),
    name = "% Vulnerable Tracts Underserved"
  ) +
  theme_void() +
  labs(
    title = "Healthcare Access Challenges in Pennsylvania",
    subtitle = "Counties colored by % of vulnerable tracts >15 miles from nearest hospital",
    caption = "Hospital locations shown as pink points. Vulnerable tracts are 25%+ elderly and low-income (<= $45k) populations."
  )

Requirements: - Fill counties by percentage of vulnerable tracts that are underserved - Include hospital locations as points - Use an appropriate color scheme - Include clear title, subtitle, and caption - Use theme_void() or similar clean theme - Add a legend with formatted labels


Map 2: Detailed Vulnerability Map

Create a map highlighting underserved vulnerable tracts.

Your Task:

# Create detailed tract-level map
#from above, for render purposes
county_map <- pa_counties %>%
  st_transform(4326) %>%
  left_join(
    county_summary %>%
      mutate(county_fips3 = substr(county_fips, 3, 5)) %>%  # drop the 42
      select(county_fips3, pct_underserved),
    by = c("FIPS_COUNT" = "county_fips3")
  )
hospitals_map <- hospitals %>%
  st_transform(4326)
county_map <- county_map %>%
  mutate(underserved_category = case_when(
    is.na(pct_underserved) ~ "No data",
    pct_underserved == 0 ~ "0%",
    pct_underserved > 0 ~ ">0%"
  ))

#make vulnerable tracts match plotting CRS WGS84
vulnerable_map <- vulnerable_proj %>%
  st_transform(4326) %>%
  mutate(underserved_category = case_when(
    is.na(underserved) ~ "Not vulnerable",
    underserved == TRUE ~ "Underserved",
    TRUE ~ "Vulnerable"
  ))

#tract-level map
ggplot() +
  geom_sf(data = county_map, fill = NA, color = "grey90", size = 0.3) +
  geom_sf(data = filter(vulnerable_map, underserved_category == "Vulnerable"),
          fill = "lightblue", color = "lightblue4", size = 0.1) +
  geom_sf(data = filter(vulnerable_map, underserved_category == "Underserved"),
          fill = "cyan4", color = "darkslategrey", size = 0.2, alpha = 0.7) +
  geom_sf(data = hospitals_map, shape = 21, color = "black", fill = "violetred4", size = 1.25, alpha = 0.5) +
  labs(
    title = "Underserved Vulnerable Tracts in Pennsylvania",
    subtitle = "Dark blue tracts are >15 miles from nearest hospital; hospitals shown as points",
    caption = "Counties shown as grey outlines. Vulnerable tracts are 25%+ elderly and low-income populations."
  ) +
  theme_void() +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", size = 16),
    plot.subtitle = element_text(size = 12),
    plot.caption = element_text(size = 10)
  )

Requirements: - Show underserved vulnerable tracts in a contrasting color - Include county boundaries for context - Show hospital locations - Use appropriate visual hierarchy (what should stand out?) - Include informative title and subtitle


Chart: Distribution Analysis

Create a visualization showing the distribution of distances to hospitals for vulnerable populations.

Your Task:

# Create distribution visualization

#| message: false
#| warning: false
#| results: 'hide'

#histogram with 1 mile bins

ggplot(vulnerable_proj, aes(x = nearest_hospital_miles)) +
  geom_histogram(
    binwidth = 1,                 
    fill = "lightblue",
    color = "grey40",
    alpha = 0.8
  ) +
  geom_vline(
    xintercept = 15,              
    linetype = "dashed",
    color = "magenta3",
    linewidth = 1
  ) +
  labs(
    title = "Distribution of Distances to Nearest Hospital",
    subtitle = "Vulnerable Census Tracts in Pennsylvania",
    x = "Distance to Nearest Hospital (miles)",
    y = "Number of Vulnerable Tracts",
    caption = "Pink dashed line indicates 15-mile threshold after which tracts are considered to be underserved"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(size = 12),
    plot.caption = element_text(size = 10)
  )

Requirements: - Clear axes labels with units - Appropriate title - Professional formatting - Brief interpretation (1-2 sentences as a caption or in text)


Part 3: Bring Your Own Data Analysis

Choose your own additional spatial dataset and conduct a supplementary analysis.

Challenge Options


Infrastructure & Services

Option E: Polling Place Accessibility - Data: Polling Places, SEPTA stops, Census tracts (elderly population, disability rates) - Question: “Are polling places accessible for elderly and disabled voters?” - Operations: Buffer polling places and transit stops, identify vulnerable populations, find areas lacking access - Policy relevance: Voting rights, election infrastructure, ADA compliance


Data Sources

OpenDataPhilly: https://opendataphilly.org/datasets/ - Most datasets available as GeoJSON, Shapefile, or CSV with coordinates - Always check the Metadata for a data dictionary of the fields.

Additional Sources: - Pennsylvania Open Data: https://data.pa.gov/ - Census Bureau (via tidycensus): Demographics, economic indicators, commute patterns - TIGER/Line (via tigris): Geographic boundaries


Your Analysis

Your Task:

  1. Find and load additional data
    • Document your data source
    • Check and standardize the CRS
    • Provide basic summary statistics
# Load your additional dataset
#Infrastructure & Services
pollingplaces <- st_read("C:/Users/macke/Downloads/polling_places.geojson")

#philly census tracts
tracts_phl <- tracts(state = "PA", county = "Philadelphia", year = 2022)

#total population
total_pop <- get_acs(
  geography = "tract",
  variables = "B01003_001",  
  state = "PA",
  county = "Philadelphia",
  year = 2022
) %>%
  select(GEOID, total_population = estimate)

#median hh income
hhincome_data <- get_acs(
  geography = "tract",
  variables = "B19013_001",  # Median household income
  state = "PA",
  county = "Philadelphia",
  year = 2022
) %>%
  select(GEOID, median_income = estimate)

#elderly population 65+ total
elderly_vars <- c(
  "B01001_020", "B01001_021", "B01001_022",
  "B01001_023", "B01001_024", "B01001_025",
  "B01001_044", "B01001_045", "B01001_046",
  "B01001_047", "B01001_048", "B01001_049"
)
elderly_data <- get_acs(
  geography = "tract",
  variables = elderly_vars,
  state = "PA",
  county = "Philadelphia",
  year = 2022
)
elderly_total <- elderly_data %>%
  group_by(GEOID) %>%
  summarise(
    elderly_65_plus = sum(estimate, na.rm = TRUE),
    .groups = "drop"
  )

Questions - What dataset did you choose and why? Polling places

  • What is the data source and date? OpenDataPhilly, 10/28/2025 (last data update)

  • How many features does it contain? 67 features

  • What CRS is it in? Did you need to transform it? WGS84, no transformation needed


  1. Research question

Do census tracts with high elderly populations and low median incomes have adequate walking-distance access to polling places?


  1. Spatial analysis

Use at least TWO spatial operations to answer your research question.

Required operations (choose 2+): - Buffers - Spatial joins - Spatial filtering with predicates - Distance calculations - Intersections or unions - Point-in-polygon aggregation

Your Task:

# Your spatial analysis

#join total pop, hh income, elderly
tracts_phl <- tracts_phl %>%
  left_join(total_pop, by = "GEOID") %>%
  left_join(hhincome_data, by = "GEOID") %>%
  left_join(elderly_total, by = "GEOID")

#vulnerable flags #same as parts 1 & 2
tracts_phl <- tracts_phl %>%
  mutate(
    pct_elderly = elderly_65_plus / total_population,
    high_elderly = pct_elderly >= 0.25,
    low_income = median_income <= 45000,
    vulnerable = high_elderly & low_income
  )

#CRS
st_crs(tracts_phl)
st_crs(pollingplaces)
pollingplaces <- st_transform(pollingplaces, st_crs(tracts_phl))

#tract centroids
tract_centroids <- st_centroid(tracts_phl)

#dist matrix in meters 
distance_matrix <- st_distance(tract_centroids, pollingplaces)

#min dist by tract
min_distance_meters <- apply(distance_matrix, 1, min)

#meters to miles
tracts_phl$distance_to_polling_miles <- as.numeric(min_distance_meters) / 1609.34

#underserved
tracts_phl <- tracts_phl %>%
  mutate(
    underserved = distance_to_polling_miles > 0.5,
    underserved_vulnerable = vulnerable & underserved
  )

#summary stats/counts
summary_stats <- tracts_phl %>%
  summarise(
    total_tracts = n(),
    vulnerable_tracts = sum(vulnerable, na.rm = TRUE),
    underserved_tracts = sum(underserved, na.rm = TRUE),
    underserved_vulnerable_tracts = sum(underserved_vulnerable, na.rm = TRUE),
    average_distance_miles = mean(distance_to_polling_miles, na.rm = TRUE),
    max_distance_miles = max(distance_to_polling_miles, na.rm = TRUE)
  )
summary_stats
knitr::kable(
  summary_stats,
  caption = "Summary of Polling Place Accessibility and Vulnerability in Philadelphia",
  align = "c",
  digits = 2
)

#vulnerable? underserved? both?
tracts_phl <- tracts_phl %>%
  mutate(
    access_category = case_when(
      vulnerable & underserved ~ "Vulnerable & Underserved",
      vulnerable & !underserved ~ "Vulnerable Only",
      !vulnerable & underserved ~ "Underserved Only",
      TRUE ~ "Neither"
    )
  )

#map
ggplot() +
  geom_sf(
    data = tracts_phl,
    aes(fill = access_category),
    color = "white",
    size = 0.2
  ) +
  geom_sf(
    data = pollingplaces,
    shape = 21,
    fill = "plum",
    color = "white",
    size = 1.25
  ) +
  scale_fill_manual(
    name = "Access Classification",
    values = c(
      "Vulnerable & Underserved" = "darkred",
      "Vulnerable Only" = "cadetblue3",
      "Underserved Only" = "steelblue",
      "Neither" = "grey90"
    )
  ) +
  labs(
    title = "Polling Place Access and Elderly or Low-Income Vulnerability",
    subtitle = "Philadelphia Census Tracts by Demographic and Spatial Criteria",
    caption = "Underserved defined as >0.5 miles from nearest polling place; Vulnerable defined as 25%+ age 65+ & Median Income $45,000 or less"
  ) +
  theme_void() +
  theme(
    legend.position = "right",
    plot.title = element_text(face = "bold", size = 16),
    plot.subtitle = element_text(size = 12)
  )

Your interpretation: From this analysis, I found no census tracts in Philadelphia county that were both vulnerable and underserved. This suggests that polling place distribution may not disproportionately burden tracts that have both higher-than-state-average elderly populations AND low median incomes. That said, areas which are vulnerable OR underserved still deserve extra attention. And, with additional time for research, I would be interested to see whether areas with ONLY high elderly populations OR low median incomes are ever both vulnerable and underserved.

Finally - Feedback Incorporation!

In this 2nd lab, I took more creative freedom in deleting unnecessary written elements directly from the assignment; in the first lab, I was hesitant to delete potentially necessary context or instructions, but after reading feedback understand that I can adjust these labs for my own portfolio. Additionally, I tried to make more frequent use of more formal table printing; I’m not sure that I had the issue (at least not frequently) of having just head() or glimpse(), but the reminder certainly made me aware to check! Thanks for your feedback–looking forward to continuing to strengthen my skills!