AE 17: Visualizing household income in New York

Suggested answers

Application exercise
Answers
Modified

March 26, 2026

Packages

library(tidyverse)
library(sf)
library(colorspace)
library(scales)
library(crsuggest)

# set default theme
theme_set(theme_minimal())

# create reusable labels for each plot
map_labels <- labs(
  title = "Median household income in New York in 2024",
  subtitle = "By census tract",
  color = NULL,
  fill = NULL,
  caption = "Source: American Community Survey"
)

Load New York 2024 median household income

We will use two data files for this analysis. The first contains median household incomes for each census tract in New York from 2024. The second contains the boundaries of each county in New York.

# load data
ny_inc <- st_read(dsn = "data/ny-inc.geojson")
Reading layer `ny-inc.geojson' from data source 
  `/Users/bcs88/Projects/info-3312/course-site/ae/data/ny-inc.geojson' 
  using driver `GeoJSON'
Simple feature collection with 5397 features and 4 fields (with 17 geometries empty)
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -79.76215 ymin: 40.4961 xmax: -71.85648 ymax: 45.01585
Geodetic CRS:  NAD83
ny_counties <- st_read(dsn = "data/ny-counties.geojson")
Reading layer `ny-counties.geojson' from data source 
  `/Users/bcs88/Projects/info-3312/course-site/ae/data/ny-counties.geojson' 
  using driver `GeoJSON'
Simple feature collection with 62 features and 4 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -79.76215 ymin: 40.4961 xmax: -71.85648 ymax: 45.01585
Geodetic CRS:  NAD83
ny_inc
Simple feature collection with 5397 features and 4 fields (with 17 geometries empty)
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -79.76215 ymin: 40.4961 xmax: -71.85648 ymax: 45.01585
Geodetic CRS:  NAD83
First 10 features:
         GEOID                                          NAME medincomeE
1  36015010800    Census Tract 108; Chemung County; New York      55881
2  36015000100      Census Tract 1; Chemung County; New York      43271
3  36051030100 Census Tract 301; Livingston County; New York      78649
4  36055013401  Census Tract 134.01; Monroe County; New York      59034
5  36055002300      Census Tract 23; Monroe County; New York      22010
6  36055014301  Census Tract 143.01; Monroe County; New York      67581
7  36055008500      Census Tract 85; Monroe County; New York      44507
8  36055013604  Census Tract 136.04; Monroe County; New York      61912
9  36055001900      Census Tract 19; Monroe County; New York      43411
10 36055006000      Census Tract 60; Monroe County; New York      53478
   medincomeM                       geometry
1       11963 MULTIPOLYGON (((-76.83044 4...
2        4429 MULTIPOLYGON (((-76.82196 4...
3       13483 MULTIPOLYGON (((-77.93474 4...
4       13000 MULTIPOLYGON (((-77.657 43....
5       12220 MULTIPOLYGON (((-77.64784 4...
6       19903 MULTIPOLYGON (((-77.7016 43...
7       11561 MULTIPOLYGON (((-77.62735 4...
8       32602 MULTIPOLYGON (((-77.69645 4...
9       15481 MULTIPOLYGON (((-77.64533 4...
10      15700 MULTIPOLYGON (((-77.56453 4...
ny_counties
Simple feature collection with 62 features and 4 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -79.76215 ymin: 40.4961 xmax: -71.85648 ymax: 45.01585
Geodetic CRS:  NAD83
First 10 features:
   GEOID                         NAME medincomeE medincomeM
1  36005       Bronx County, New York      48676        895
2  36119 Westchester County, New York     118976       2567
3  36053     Madison County, New York      75499       3230
4  36029        Erie County, New York      72839       1077
5  36091    Saratoga County, New York     100787       3028
6  36031       Essex County, New York      71661       4292
7  36113      Warren County, New York      78442       3623
8  36017    Chenango County, New York      62948       2655
9  36051  Livingston County, New York      74001       2171
10 36063     Niagara County, New York      69633       2124
                         geometry
1  MULTIPOLYGON (((-73.77242 4...
2  MULTIPOLYGON (((-73.77237 4...
3  MULTIPOLYGON (((-75.99243 4...
4  MULTIPOLYGON (((-79.13689 4...
5  MULTIPOLYGON (((-74.1601 43...
6  MULTIPOLYGON (((-74.33683 4...
7  MULTIPOLYGON (((-74.21462 4...
8  MULTIPOLYGON (((-75.88983 4...
9  MULTIPOLYGON (((-78.06078 4...
10 MULTIPOLYGON (((-79.07537 4...

Part 1

Draw a continuous choropleth of median household income

Your turn: Create a choropleth map of median household income in New York. Use a continuous color gradient to identify each tract’s median household income.

Tip

Use the stored map_labels to set the title, subtitle, and caption for this and the remaining plots.

ggplot(data = ny_inc) +
  # use fill and color to avoid gray boundary lines
  geom_sf(aes(fill = medincomeE, color = medincomeE)) +
  # increase interpretability of graph
  scale_color_continuous(labels = label_currency()) +
  scale_fill_continuous(labels = label_currency()) +
  map_labels

Your turn: Now revise the map to use an optimized color gradient for improved readability.

ggplot(data = ny_inc) +
  # use fill and color to avoid gray boundary lines
  geom_sf(mapping = aes(fill = medincomeE, color = medincomeE)) +
  # increase interpretability of graph
  scale_fill_continuous_sequential(
    palette = "viridis",
    rev = FALSE,
    aesthetics = c("fill", "color"),
    labels = label_currency(),
    name = NULL
  ) +
  map_labels

Overlay county borders

Your turn: To provide better context, overlay the NY county borders on the choropleth map.

ggplot(data = ny_inc) +
  # use fill and color to avoid gray boundary lines
  geom_sf(mapping = aes(fill = medincomeE, color = medincomeE)) +
  # add county borders
  geom_sf(data = ny_counties, color = "white", fill = NA) +
  # increase interpretability of graph
  scale_fill_continuous_sequential(
    palette = "viridis",
    rev = FALSE,
    aesthetics = c("fill", "color"),
    labels = label_currency(),
    name = NULL
  ) +
  map_labels

Part 2

Your turn: Continuous color palettes can be hard to distinguish visibly. To improve readability, convert the continuous color palette into a discrete one with 6 levels. Additionally, select an appropriate projection method using {crsuggest}.

suggest_crs(ny_inc)
# A tibble: 10 × 6
   crs_code crs_name                        crs_type crs_gcs crs_units crs_proj4
   <chr>    <chr>                           <chr>      <dbl> <chr>     <chr>    
 1 6563     NAD83(2011) / Pennsylvania Nor… project…    6318 us-ft     +proj=lc…
 2 6562     NAD83(2011) / Pennsylvania Nor… project…    6318 m         +proj=lc…
 3 3650     NAD83(NSRS2007) / Pennsylvania… project…    4759 us-ft     +proj=lc…
 4 3649     NAD83(NSRS2007) / Pennsylvania… project…    4759 m         +proj=lc…
 5 3363     NAD83(HARN) / Pennsylvania Nor… project…    4152 us-ft     +proj=lc…
 6 3362     NAD83(HARN) / Pennsylvania Nor… project…    4152 m         +proj=lc…
 7 32128    NAD83 / Pennsylvania North      project…    4269 m         +proj=lc…
 8 32028    NAD27 / Pennsylvania North      project…    4267 us-ft     +proj=lc…
 9 2271     NAD83 / Pennsylvania North (ft… project…    4269 us-ft     +proj=lc…
10 6535     NAD83(2011) / New York Central… project…    6318 us-ft     +proj=tm…
# binned_scale() - default breaks
ggplot(data = ny_inc) +
  geom_sf(mapping = aes(fill = medincomeE, color = medincomeE)) +
  geom_sf(data = ny_counties, color = "white", fill = NA) +
  scale_fill_binned_sequential(
    palette = "viridis",
    rev = FALSE,
    aesthetics = c("fill", "color"),
    labels = label_currency()
  ) +
  # increase interpretability of graph
  map_labels +
  # NY Central
  coord_sf(crs = 6535)

sessioninfo::session_info()
─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.5.2 (2025-10-31)
 os       macOS Tahoe 26.4
 system   aarch64, darwin20
 ui       X11
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       America/New_York
 date     2026-04-02
 pandoc   3.4 @ /usr/local/bin/ (via rmarkdown)
 quarto   1.9.36 @ /usr/local/bin/quarto

─ Packages ───────────────────────────────────────────────────────────────────
 ! package      * version date (UTC) lib source
 P base64enc      0.1-3   2015-07-28 [?] RSPM (R 4.5.0)
 P class          7.3-23  2025-01-01 [?] CRAN (R 4.5.2)
 P classInt       0.4-11  2025-01-08 [?] RSPM (R 4.5.0)
 P cli            3.6.5   2025-04-23 [?] RSPM (R 4.5.0)
 P codetools      0.2-20  2024-03-31 [?] CRAN (R 4.5.2)
 P colorspace   * 2.1-2   2025-09-22 [?] RSPM
 P crosstalk      1.2.2   2025-08-26 [?] RSPM
 P crsuggest    * 0.4     2022-07-06 [?] RSPM
 P DBI            1.2.3   2024-06-02 [?] RSPM (R 4.5.0)
 P digest         0.6.39  2025-11-19 [?] RSPM (R 4.5.0)
 P dplyr        * 1.2.0   2026-02-03 [?] RSPM
 P e1071          1.7-17  2025-12-18 [?] CRAN (R 4.5.2)
 P evaluate       1.0.5   2025-08-27 [?] RSPM (R 4.5.0)
 P farver         2.1.2   2024-05-13 [?] RSPM (R 4.5.0)
 P fastmap        1.2.0   2024-05-15 [?] RSPM (R 4.5.0)
 P forcats      * 1.0.1   2025-09-25 [?] RSPM (R 4.5.0)
 P generics       0.1.4   2025-05-09 [?] RSPM (R 4.5.0)
 P ggplot2      * 4.0.1   2025-11-14 [?] RSPM (R 4.5.0)
 P glue           1.8.0   2024-09-30 [?] RSPM (R 4.5.0)
 P gtable         0.3.6   2024-10-25 [?] RSPM (R 4.5.0)
 P here           1.0.2   2025-09-15 [?] CRAN (R 4.5.0)
 P hms            1.1.4   2025-10-17 [?] RSPM (R 4.5.0)
 P htmltools      0.5.9   2025-12-04 [?] RSPM (R 4.5.0)
 P htmlwidgets    1.6.4   2023-12-06 [?] RSPM (R 4.5.0)
 P jsonlite       2.0.0   2025-03-27 [?] RSPM (R 4.5.0)
 P KernSmooth     2.23-26 2025-01-01 [?] CRAN (R 4.5.2)
 P knitr          1.51    2025-12-20 [?] RSPM (R 4.5.0)
 P labeling       0.4.3   2023-08-29 [?] RSPM (R 4.5.0)
 P lattice        0.22-7  2025-04-02 [?] CRAN (R 4.5.2)
 P leafem         0.2.5   2025-08-28 [?] RSPM
 P leaflet        2.2.3   2025-09-04 [?] RSPM
 P lifecycle      1.0.5   2026-01-08 [?] RSPM (R 4.5.0)
 P lubridate    * 1.9.4   2024-12-08 [?] RSPM (R 4.5.0)
 P magrittr       2.0.4   2025-09-12 [?] RSPM (R 4.5.0)
 P mapview        2.11.4  2025-09-08 [?] RSPM
 P otel           0.2.0   2025-08-29 [?] RSPM (R 4.5.0)
 P pillar         1.11.1  2025-09-17 [?] RSPM (R 4.5.0)
 P pkgconfig      2.0.3   2019-09-22 [?] RSPM (R 4.5.0)
 P png            0.1-8   2022-11-29 [?] RSPM (R 4.5.0)
 P proxy          0.4-29  2025-12-29 [?] CRAN (R 4.5.2)
 P purrr        * 1.2.0   2025-11-04 [?] CRAN (R 4.5.0)
 P R6             2.6.1   2025-02-15 [?] RSPM (R 4.5.0)
 P raster         3.6-32  2025-03-28 [?] RSPM
 P RColorBrewer   1.1-3   2022-04-03 [?] RSPM (R 4.5.0)
 P Rcpp           1.1.0   2025-07-02 [?] RSPM (R 4.5.0)
 P readr        * 2.1.6   2025-11-14 [?] RSPM (R 4.5.0)
 P renv           1.1.8   2026-03-05 [?] RSPM
 P rlang          1.1.7   2026-01-09 [?] RSPM (R 4.5.0)
 P rmarkdown      2.30    2025-09-28 [?] RSPM (R 4.5.0)
 P rprojroot      2.1.1   2025-08-26 [?] RSPM (R 4.5.0)
 P S7             0.2.1   2025-11-14 [?] RSPM (R 4.5.0)
 P satellite      1.0.6   2025-08-21 [?] RSPM
 P scales       * 1.4.0   2025-04-24 [?] RSPM (R 4.5.0)
 P sessioninfo    1.2.3   2025-02-05 [?] RSPM (R 4.5.0)
 P sf           * 1.0-23  2025-11-28 [?] CRAN (R 4.5.2)
 P sp             2.2-0   2025-02-01 [?] RSPM
 P stringi        1.8.7   2025-03-27 [?] RSPM (R 4.5.0)
 P stringr      * 1.6.0   2025-11-04 [?] RSPM (R 4.5.0)
 P terra          1.8-86  2025-11-28 [?] CRAN (R 4.5.2)
 P tibble       * 3.3.0   2025-06-08 [?] RSPM (R 4.5.0)
 P tidyr        * 1.3.2   2025-12-19 [?] RSPM (R 4.5.0)
 P tidyselect     1.2.1   2024-03-11 [?] RSPM (R 4.5.0)
 P tidyverse    * 2.0.0   2023-02-22 [?] RSPM (R 4.5.0)
 P timechange     0.3.0   2024-01-18 [?] RSPM (R 4.5.0)
 P tzdb           0.5.0   2025-03-15 [?] RSPM (R 4.5.0)
 P units          1.0-0   2025-10-09 [?] RSPM
 P utf8           1.2.6   2025-06-08 [?] RSPM (R 4.5.0)
 P vctrs          0.7.1   2026-01-23 [?] RSPM
 P withr          3.0.2   2024-10-28 [?] RSPM (R 4.5.0)
 P xfun           0.55    2025-12-16 [?] CRAN (R 4.5.2)
 P yaml           2.3.12  2025-12-10 [?] RSPM (R 4.5.0)

 [1] /Users/bcs88/Projects/info-3312/course-site/renv/library/macos/R-4.5/aarch64-apple-darwin20
 [2] /Users/bcs88/Library/Caches/org.R-project.R/R/renv/sandbox/macos/R-4.5/aarch64-apple-darwin20/4cd76b74

 * ── Packages attached to the search path.
 P ── Loaded and on-disk path mismatch.

──────────────────────────────────────────────────────────────────────────────