I'm the Map

Getting Prepped

Download the script

Copying and pasting syntax from a browser can cause problems. To avoid this issue, please download a script with all of the needed code presented in this walkthrough.

Possible Solutions

Remember just because your code doesn’t look like mine doesn’t mean anything. Included are examples of approaches that can be used to complete this task, but there are so many more!

library(tidyverse)
library(tidycensus)
library(DT)
library(wesanderson)
library(ggthemes)
library(scales)

First let’s check the FIPS codes for the states and counties

head(tidycensus::fips_codes)
##   state state_code state_name county_code         county
## 1    AL         01    Alabama         001 Autauga County
## 2    AL         01    Alabama         003 Baldwin County
## 3    AL         01    Alabama         005 Barbour County
## 4    AL         01    Alabama         007    Bibb County
## 5    AL         01    Alabama         009  Blount County
## 6    AL         01    Alabama         011 Bullock County

Prior Things

Data Set

For the following, we’ll use a single variable though you can absolutely use many more! In this case, let’s utilize the average income measured by medians in 2016.

vars_of_interest <- c(median_income = "B19013_001")

Color Palettes

We will need continuous color palettes to display the data well. In this case we’ll use the wesanderson collection, but please feel free to use your own.

National Level Data

national_data <- get_acs(geography = "county",
                         variables = vars_of_interest,
                         year = 2016,
                         survey = "acs5",
                         shift_geo = TRUE,
                         geometry = TRUE) %>%
                         rename(`Median Income` = estimate)

Make sure to check it

datatable(national_data)  

Let’s now create a palette

wes_palette("Darjeeling2")

make it continuous

darj2 <- wes_palette("Darjeeling2",
                      max(national_data$`Median Income`, 
                      na.rm = TRUE), 
                      type = "continuous")

and plot it for the entire US

ggplot() + 
  geom_sf(data = national_data,
          mapping = aes(fill = `Median Income`),
          color = NA) + 
  coord_sf(datum = NA) +
  scale_fill_gradientn(colors = darj2)  + 
  theme_minimal() +
  theme(plot.title = element_text(size = 20,
                                  face = "bold",
                                  color = "#005b5b",
                                  hjust = 0.5)) +
  ggtitle(stringr::str_wrap("2016 ACS Median Income for the United States", 35))

Regional Level Data

Here is a US Census PDF of what the regions are. Now Region 1 can be defined by

region1 = c("CT", "ME", "MA", "NH", "RI", "VT", "NJ", "NY", "PA")

We get acs data and geographies for our variable at the

  • tract level
multistate_tract_data <- get_acs(geography = "tract",
                                 variables = vars_of_interest,
                                 state = region1,
                                 year = 2016,
                                 survey = "acs5",
                                 geometry = TRUE) %>%
                          rename(`Median Income` = estimate)

Make sure to check it

datatable(multistate_tract_data)  
GEOIDNAMEvariableMedian Incomemoegeometry
09001011000Census Tract 110, Fairfield County, Connecticutmedian_income24817133760MULTIPOLYGON (((-73.58765 4…
09001020800Census Tract 208, Fairfield County, Connecticutmedian_income13493415960MULTIPOLYGON (((-73.54896 4…
09001021400Census Tract 214, Fairfield County, Connecticutmedian_income551017886MULTIPOLYGON (((-73.56834 4…
09001022200Census Tract 222, Fairfield County, Connecticutmedian_income5888910637MULTIPOLYGON (((-73.54503 4…
09001043100Census Tract 431, Fairfield County, Connecticutmedian_income12553631382MULTIPOLYGON (((-73.47108 4…
09001045400Census Tract 454, Fairfield County, Connecticutmedian_income18937538749MULTIPOLYGON (((-73.42248 4…
  • state level
multistate_state_data <- get_acs(geography = "state",
                                 variables = vars_of_interest,
                                 state = region1,
                                 year = 2016,
                                 survey = "acs5",
                                 geometry = TRUE) %>%
                          rename(`Median Income` = estimate)

Make sure to check it

datatable(multistate_state_data)  
GEOIDNAMEvariableMedian Incomemoegeometry
09001011000Census Tract 110, Fairfield County, Connecticutmedian_income24817133760MULTIPOLYGON (((-73.58765 4…
09001020800Census Tract 208, Fairfield County, Connecticutmedian_income13493415960MULTIPOLYGON (((-73.54896 4…
09001021400Census Tract 214, Fairfield County, Connecticutmedian_income551017886MULTIPOLYGON (((-73.56834 4…
09001022200Census Tract 222, Fairfield County, Connecticutmedian_income5888910637MULTIPOLYGON (((-73.54503 4…
09001043100Census Tract 431, Fairfield County, Connecticutmedian_income12553631382MULTIPOLYGON (((-73.47108 4…
09001045400Census Tract 454, Fairfield County, Connecticutmedian_income18937538749MULTIPOLYGON (((-73.42248 4…

Putting geometry = TRUE brings in the geographic information (aka shapefiles) so make sure that is on, though if you only need the data and not a map, save yourself some time and turn it off. Geographic data is relatively dense and can take some time depending on how much you need.

Optionally let’s create a palette,

wes_palette("Darjeeling1")

make it continuous

darj1 <- wes_palette("Darjeeling1",
                     max(multistate_tract_data$`Median Income`, 
                         na.rm = TRUE), 
                     type = "continuous")

and plot it. This is tract level data so you’ll have to wait a little bit

ggplot() + 
  geom_sf(data = multistate_tract_data,
                   mapping = aes(fill = `Median Income`),
                   color = NA) + 
  scale_fill_gradientn(colors = darj1) +
  theme_map() 

Let’s give it a go with state borders so its a bit more familiar and with a formatted title and some finagling of the legend

ggplot() + 
  geom_sf(data = multistate_tract_data,
                   mapping = aes(fill = `Median Income`),
                   color = NA) + 
  geom_sf(data = multistate_state_data,
                   color = "#f7f7f7",
                   fill = NA) + 
  scale_fill_gradientn(colors = darj1,
                       label = scales::dollar) +
  theme_map() +
  theme(plot.title = element_text(size = 20,
                                  face = "bold",
                                  color = "#005b5b",
                                  hjust = 0.5),
        legend.position = c(0.8,0.1),
        legend.title = element_text(size = 12,
                                    face = "bold",
                                    color = "#3392a4",
                                    hjust = 0.5,
                                    margin = margin(b = 10)),
        legend.text = element_text(size = 10,
                                   face = "bold",
                                   color = "#2d8292")) +
  ggtitle("2016 ACS Median Income for Census Region 1")

State Level Data

We get acs data and geographies for our variable using

state_data <- get_acs(geography = "state",
                      variables = vars_of_interest,
                      year = 2016,
                      survey = "acs5",
                      shift_geo = TRUE,
                      geometry = TRUE) %>%
                      rename(`Median Income` = estimate)

Make sure to check it

datatable(state_data)  

Optionally let’s create a palette,

wes_palette("Zissou1")

make it continuous

ziss1 <- wes_palette("Zissou1",
                     max(state_data$`Median Income`, 
                         na.rm = TRUE), 
                     type = "continuous")

and plot it for the entire US

ggplot() + 
  geom_sf(data = state_data,
          mapping = aes(fill = `Median Income`),
          color = NA) + 
  scale_fill_gradientn(colors = ziss1) +
  theme_map() +
  theme(plot.title = element_text(size = 20,
                                  face = "bold",
                                  color = "#005b5b",
                                  hjust = 0.5)) +
  ggtitle("2016 ACS Median Income for All US States")

or just the lower 48 states

lower_48 <- state_data %>%
            filter(GEOID < 60) %>%
            filter(!NAME == "Hawaii") %>%
            filter(!NAME == "Alaska")
ggplot() + 
 geom_sf(data = lower_48,
         mapping = aes(fill = `Median Income`),
         color = NA) + 
 scale_fill_gradientn(colors = ziss1) +
 theme_map() +
    coord_sf(crs = 5070, 
             datum = NA) +
 theme(plot.title = element_text(size = 20,
                                  face = "bold",
                                  color = "#005b5b",
                                  hjust = 0.5)) +
  ggtitle(stringr::str_wrap("2016 ACS Median Income for the Contiguous US States", 35))

County Level Data

Tract level data is sometimes tricky to get, in that not all data sets with geographies have them. Sticking to the widespread surveys such as the decennial census and acs will always work, while others are up in the air.

kansas <- get_acs(state = "KS", 
                  geography = "county", 
                  variables = vars_of_interest,
                  geometry = TRUE) %>%
                  rename(`Median Income` = estimate)
datatable(kansas)
GEOIDNAMEvariableMedian Incomemoegeometry
20099Labette County, Kansasmedian_income476432802MULTIPOLYGON (((-95.52265 3…
20203Wichita County, Kansasmedian_income579787215MULTIPOLYGON (((-101.5671 3…
20077Harper County, Kansasmedian_income498652945MULTIPOLYGON (((-98.3498 37…
20093Kearny County, Kansasmedian_income5259910953MULTIPOLYGON (((-101.5419 3…
20069Gray County, Kansasmedian_income649303363MULTIPOLYGON (((-100.665 37…
20161Riley County, Kansasmedian_income512082475MULTIPOLYGON (((-96.96095 3…
wes_palette("FantasticFox1")

make it continuous

ffox <- wes_palette("FantasticFox1",
                     max(kansas$`Median Income`, 
                         na.rm = TRUE), 
                     type = "continuous")

and plot it!

kansas %>%
  ggplot(aes(fill = `Median Income`)) + 
  geom_sf(color = NA) + 
  scale_fill_gradientn(colors = ffox,
                       label = scales::dollar) +
  theme_map() +
  theme(legend.position = "bottom",
        legend.direction = "horizontal",
        legend.title.align=0.5) +   
  guides(fill = guide_legend(title.position="top")) +
 theme(plot.title = element_text(size = 20,
                                  face = "bold",
                                  color = "#005b5b",
                                  hjust = 0.5)) +
  ggtitle("2016 ACS Median Income for the Counties in Kansas")

Tract Level Data

Tract level data is sometimes tricky to get, in that not all data sets with geographies have them. Sticking to the widespread surveys such as the decennial census and acs will always work, while others are up in the air.

doug <- get_acs(state = "KS", 
                county = "Douglas", 
                geography = "tract", 
                variables = vars_of_interest,
                geometry = TRUE) %>%
                rename(`Median Income` = estimate)
datatable(doug)
GEOIDNAMEvariableMedian Incomemoegeometry
20045000901Census Tract 9.01, Douglas County, Kansasmedian_income303535043POLYGON ((-95.26054 38.9359…
20045000502Census Tract 5.02, Douglas County, Kansasmedian_income5703511590POLYGON ((-95.2607 38.97286…
20045000501Census Tract 5.01, Douglas County, Kansasmedian_income469524297POLYGON ((-95.261 38.97609,…
20045000604Census Tract 6.04, Douglas County, Kansasmedian_income8453615693POLYGON ((-95.2792 38.97794…
20045000300Census Tract 3, Douglas County, Kansasmedian_income280244766POLYGON ((-95.25113 38.9513…
20045000797Census Tract 7.97, Douglas County, Kansasmedian_income6449612992POLYGON ((-95.30693 38.957,…
wes_palette("Cavalcanti1")

make it continuous

caval <- wes_palette("Cavalcanti1",
                     max(doug$`Median Income`, 
                         na.rm = TRUE), 
                     type = "continuous")

and plot it!

doug %>%
  ggplot(aes(fill = `Median Income`)) + 
  geom_sf(color = NA) + 
  scale_fill_gradientn(colors = caval,
                       label = scales::dollar) +
  theme_map() +
  theme(legend.position = "bottom",
        legend.key.width = unit(3, "line")) +   
  guides(fill = guide_legend(title.position="top", 
                              title.hjust =0.5))  +
 theme(plot.title = element_text(size = 20,
                                  face = "bold",
                                  color = "#005b5b",
                                  hjust = 0.5)) +
  ggtitle(stringr::str_wrap("2016 ACS Median Income for the Census Tracts in Kansas", 35))