library(tidyverse)
library(plotly)
library(scales)
library(colorspace)
library(ggrepel)
theme_set(theme_minimal())
AE 19: Increase in cost-burdened households in the United States
Suggested answers
We have already seen this semester that the cost of housing in the United States has been rising for several decades. A household is considered cost-burdened if they spend more than 30% of their income on housing costs.
In this application exercise we will explore trends in the percentage of cost-burdened rental households in the 10 largest metropolitan statistical areas (MSAs).1 The relevant data can be found in data/msa-renters-burden.csv
.
<- read_csv(file = "data/msa-renters-burden.csv")
renter_burden renter_burden
# A tibble: 110 × 4
year geoid name pct_burdened
<dbl> <dbl> <chr> <dbl>
1 2013 12060 Atlanta-Sandy Springs-Roswell, GA 0.500
2 2013 16980 Chicago-Naperville-Elgin, IL-IN 0.493
3 2013 19100 Dallas-Fort Worth-Arlington, TX 0.453
4 2013 26420 Houston-Pasadena-The Woodlands, TX 0.460
5 2013 31080 Los Angeles-Long Beach-Anaheim, CA 0.561
6 2013 33100 Miami-Fort Lauderdale-West Palm Beach, FL 0.595
7 2013 35620 New York-Newark-Jersey City, NY-NJ 0.511
8 2013 37980 Philadelphia-Camden-Wilmington, PA-NJ-DE-MD 0.503
9 2013 38060 Phoenix-Mesa-Chandler, AZ 0.477
10 2013 47900 Washington-Arlington-Alexandria, DC-VA-MD-WV 0.466
# ℹ 100 more rows
pct_burdened
reports the percentage of renter-occupied housing units that spend 30%+ of their household income on gross rent.2
Communicating trends with a static visualization
Your turn: While Americans face rising housing costs, the percentage of cost-burdened households has not increased uniformly across the country. Design and implement a static visualization to communicate the trends for these 10 MSAs. Ensure it can reasonably be used to identify trends specific to each MSA.
# use color to distinguish between MSAs
|>
renter_burden # order the names by the most recent year for improved clarity in the legend
mutate(name = fct_reorder2(.f = name, .x = year, .y = pct_burdened)) |>
ggplot(mapping = aes(x = year, y = pct_burdened, color = name)) +
geom_line() +
# better x-axis breaks
scale_x_continuous(breaks = seq(2013, 2023, by = 2)) +
scale_y_continuous(labels = label_percent()) +
scale_color_discrete_qualitative() +
labs(
x = NULL,
y = NULL,
color = NULL,
title = "Share of households spending 30%+ income on rent",
subtitle = "Largest MSAs by population in 2023",
caption = "Source: American Community Survey"
)
# set seed for reproducibility
set.seed(123)
ggplot(data = renter_burden, mapping = aes(x = year, y = pct_burdened, group = name)) +
geom_line() +
# just label the last year in the data
geom_text_repel(
data = renter_burden |>
slice_max(order_by = year, n = 1, by = geoid),
# wrap the character strings for space
mapping = aes(label = str_wrap(name, width = 20)),
# move labels over 10 units to the right before repelling them
nudge_x = 10,
# left alignment of text
hjust = 0,
segment.color = "grey70"
+
) scale_x_continuous(breaks = seq(2013, 2023, by = 2)) +
scale_y_continuous(labels = label_percent()) +
labs(
x = NULL,
y = NULL,
title = "Share of households spending 30%+ income on rent",
subtitle = "Largest MSAs by population in 2023",
caption = "Source: American Community Survey"
+
) theme(panel.grid.minor = element_blank())
Communicating trends with an interactive visualization
Your turn: Design and implement an interactive visualization to communicate the trends for these 10 MSAs. Ensure it can reasonably be used to identify trends specific to each MSA. Leverage interactive components to reduce clutter in the visualization and effectively utilize interactivity.
- Customizing the tooltip to provide better-formatted information
highlight()
trend lines to draw attention to selected MSA- Implement the plot purely using
plot_ly()
Using ggplotly()
and customizing the tooltip
<- renter_burden |>
p # create custom tooltip
mutate(tooltip = str_glue("{name}<br>Year: {year}<br>Share of cost-burdened renters: {label_percent(accuracy = 1)(pct_burdened)}")) |>
ggplot(mapping = aes(x = year, y = pct_burdened, group = name)) +
geom_line(mapping = aes(text = tooltip)) +
scale_x_continuous(breaks = seq(2013, 2023, by = 2)) +
scale_y_continuous(labels = label_percent()) +
labs(
x = NULL,
y = NULL,
title = "Share of households spending 30%+ income on rent, by MSA"
)
# render with plotly
ggplotly(p, tooltip = "text")
Highlight a specific line
{# create tooltip
|>
renter_burden mutate(tooltip = str_glue("{name}<br>Year: {year}<br>Share of cost-burdened renters: {label_percent(accuracy = 1)(pct_burdened)}")) |>
# create a SharedData object for use in the ggplot() below and group by name
highlight_key(~name) |>
# create the static plot
ggplot(mapping = aes(x = year, y = pct_burdened, group = name)) +
geom_line(mapping = aes(text = tooltip)) +
scale_x_continuous(breaks = seq(2013, 2023, by = 2)) +
scale_y_continuous(labels = label_percent()) +
labs(
x = NULL,
y = NULL,
title = "Share of households spending 30%+ income on rent, by MSA"
)|>
} # convert to interactive plot
ggplotly(tooltip = "text") |>
# activate highlight on hover
highlight(
# events triggering highlight on and off
on = "plotly_hover",
off = "plotly_doubleclick",
# highlight color
color = "orange"
)
Implement using plot_ly()
# Load required libraries
library(plotly)
library(dplyr)
library(stringr)
library(scales)
library(crosstalk) # for highlight_key()
# Build interactive line plot with highlighting using plotly
|>
renter_burden # Create a custom tooltip for each data point
# Tooltip includes metro area name, year, and the percent of cost-burdened renters
mutate(tooltip = str_glue(
"{name}<br>Year: {year}<br>Share of cost-burdened renters: {label_percent(accuracy = 1)(pct_burdened)}"
|>
))
# Register 'name' as the group key for interactivity
# This allows plotly to know which line each point belongs to
highlight_key(~name) |>
# Create the interactive plot
plot_ly(
x = ~year, # x-axis: year
y = ~pct_burdened, # y-axis: percent cost-burdened
text = ~tooltip, # use custom tooltip text
hoverinfo = "text", # only show the text we defined, not default info
type = "scatter", # create a scatterplot (with lines below)
mode = "lines", # connect points with lines
split = ~name, # create a separate trace for each metro area (MSA)
line = list(color = "gray"), # set all lines to gray by default
showlegend = FALSE # don't show legend for each metro area
|>
)
# Customize plot layout
layout(
title = "Share of households spending 30%+ income on rent, by MSA",
xaxis = list(
title = NA,
tickvals = seq(2013, 2023, by = 2) # show ticks every 2 years
),yaxis = list(
title = NA,
tickformat = ".0%", # format y-axis as percentages
rangemode = "normal" # ensure full range is shown
),hovermode = "closest", # only show tooltip for the closest point being hovered
showlegend = FALSE # make sure legend stays hidden
|>
)
# Enable interactive highlighting
highlight(
on = "plotly_hover", # highlight a line when user hovers over it
off = "plotly_doubleclick", # reset highlights when user double-clicks
color = "orange" # highlight color for selected line
)
::session_info() sessioninfo
─ Session info ───────────────────────────────────────────────────────────────
setting value
version R version 4.4.2 (2024-10-31)
os macOS Sonoma 14.6.1
system aarch64, darwin20
ui X11
language (EN)
collate en_US.UTF-8
ctype en_US.UTF-8
tz America/New_York
date 2025-04-11
pandoc 3.4 @ /usr/local/bin/ (via rmarkdown)
quarto 1.6.40 @ /usr/local/bin/quarto
─ Packages ───────────────────────────────────────────────────────────────────
! package * version date (UTC) lib source
bit 4.6.0 2025-03-06 [1] RSPM
bit64 4.6.0-1 2025-01-16 [1] RSPM
P cli 3.6.4 2025-02-13 [?] RSPM
P colorspace * 2.1-1 2024-07-26 [?] RSPM
P crayon 1.5.3 2024-06-20 [?] CRAN (R 4.4.0)
crosstalk * 1.2.1 2023-11-23 [1] CRAN (R 4.3.1)
data.table 1.17.0 2025-02-22 [1] RSPM
digest 0.6.37 2024-08-19 [1] RSPM
P dplyr * 1.1.4 2023-11-17 [?] CRAN (R 4.3.1)
evaluate 1.0.3 2025-01-10 [1] RSPM
P farver 2.1.2 2024-05-13 [?] CRAN (R 4.3.3)
fastmap 1.2.0 2024-05-15 [1] CRAN (R 4.4.0)
forcats * 1.0.0 2023-01-29 [1] CRAN (R 4.3.0)
P generics 0.1.3 2022-07-05 [?] CRAN (R 4.3.0)
P ggplot2 * 3.5.1 2024-04-23 [?] CRAN (R 4.3.1)
ggrepel * 0.9.6 2024-09-07 [1] RSPM
P glue 1.8.0 2024-09-30 [?] RSPM
P gtable 0.3.6 2024-10-25 [?] RSPM
here 1.0.1 2020-12-13 [1] CRAN (R 4.3.0)
P hms 1.1.3 2023-03-21 [?] CRAN (R 4.3.0)
htmltools 0.5.8.1 2024-04-04 [1] CRAN (R 4.3.1)
htmlwidgets 1.6.4 2023-12-06 [1] CRAN (R 4.3.1)
httpuv 1.6.15 2024-03-26 [1] CRAN (R 4.3.1)
httr 1.4.7 2023-08-15 [1] CRAN (R 4.3.0)
jsonlite 2.0.0 2025-03-27 [1] RSPM
knitr 1.50 2025-03-16 [1] RSPM
P labeling 0.4.3 2023-08-29 [?] CRAN (R 4.3.0)
later 1.4.1 2024-11-27 [1] RSPM
lazyeval 0.2.2 2019-03-15 [1] CRAN (R 4.3.0)
P lifecycle 1.0.4 2023-11-07 [?] CRAN (R 4.3.1)
lubridate * 1.9.4 2024-12-08 [1] RSPM
P magrittr 2.0.3 2022-03-30 [?] CRAN (R 4.3.0)
mime 0.13 2025-03-17 [1] RSPM
P pillar 1.10.2 2025-04-05 [?] CRAN (R 4.4.1)
P pkgconfig 2.0.3 2019-09-22 [?] CRAN (R 4.3.0)
P plotly * 4.10.4 2024-01-13 [?] CRAN (R 4.4.0)
promises 1.3.2 2024-11-28 [1] RSPM
P purrr * 1.0.4 2025-02-05 [?] RSPM
P R6 2.6.1 2025-02-15 [?] RSPM
P RColorBrewer 1.1-3 2022-04-03 [?] CRAN (R 4.3.0)
P Rcpp 1.0.14 2025-01-12 [?] RSPM
readr * 2.1.5 2024-01-10 [1] CRAN (R 4.3.1)
renv 1.0.11 2024-10-12 [1] CRAN (R 4.4.1)
P rlang 1.1.5 2025-01-17 [?] RSPM
rmarkdown 2.29 2024-11-04 [1] RSPM
rprojroot 2.0.4 2023-11-05 [1] CRAN (R 4.3.1)
scales * 1.3.0.9000 2024-11-14 [1] Github (r-lib/scales@ee03582)
sessioninfo 1.2.3 2025-02-05 [1] RSPM
shiny 1.10.0 2024-12-14 [1] RSPM
P stringi 1.8.7 2025-03-27 [?] RSPM
P stringr * 1.5.1 2023-11-14 [?] CRAN (R 4.3.1)
P tibble * 3.2.1 2023-03-20 [?] CRAN (R 4.3.0)
P tidyr * 1.3.1 2024-01-24 [?] CRAN (R 4.3.1)
P tidyselect 1.2.1 2024-03-11 [?] CRAN (R 4.3.1)
tidyverse * 2.0.0 2023-02-22 [1] CRAN (R 4.3.0)
timechange 0.3.0 2024-01-18 [1] CRAN (R 4.3.1)
tzdb 0.5.0 2025-03-15 [1] RSPM
P utf8 1.2.4 2023-10-22 [?] CRAN (R 4.3.1)
P vctrs 0.6.5 2023-12-01 [?] CRAN (R 4.3.1)
P viridisLite 0.4.2 2023-05-02 [?] CRAN (R 4.3.0)
vroom 1.6.5 2023-12-05 [1] CRAN (R 4.3.1)
P withr 3.0.2 2024-10-28 [?] RSPM
xfun 0.52 2025-04-02 [1] CRAN (R 4.4.1)
xtable 1.8-4 2019-04-21 [1] CRAN (R 4.3.0)
yaml 2.3.10 2024-07-26 [1] RSPM
[1] /Users/soltoffbc/Projects/info-3312/course-site/renv/library/macos/R-4.4/aarch64-apple-darwin20
[2] /Users/soltoffbc/Library/Caches/org.R-project.R/R/renv/sandbox/macos/R-4.4/aarch64-apple-darwin20/f7156815
* ── Packages attached to the search path.
P ── Loaded and on-disk path mismatch.
──────────────────────────────────────────────────────────────────────────────
Footnotes
Based on population as of 2023.↩︎
Specifically Table B25070 from the American Community Survey.↩︎