1. State Average Unemployment Rates

After reading the data into a data frame lausUS the unemployment rate for each state can be computed as

\[ \frac{\text{number of unemployed in the state}}{\text{size of the labor force in the state}}. \]

To see the difference we can compute both values:

unemp_by_state <- summarize(group_by(lausUS, State),
                            urate = 100 * sum(Unemployed) / sum(LaborForce),
                            urateNW = mean(UnempRate))

The map data is obtained with

gusa <- map_data("state")

To allow the map data to be merged with the unemployment data we can arrange that both data frames contain the state FIPS code in a variable named fips:

unemp_by_state <- rename(unemp_by_state, fips = State)
fips_idx <- match(gusa$region, sub(":.*", "", state.fips$polyname))
gusa$fips <- state.fips$fips[fips_idx]

A left join of the map and unemployment data is placed in gusa_unemp:

gusa_unemp <- left_join(gusa, unemp_by_state, "fips")
gusa_unemp <- arrange(gusa_unemp, order)

We need the polygon data to be in the right order; the arrange function from dplyr is used here to make sure it is.

Since the unemployment rate is a continuous variable, a sequential palette is most appropriate. The default palette does not work well; the "Reds" palette from RColorBrewer is a good choice:

ggplot(gusa_unemp) +
    geom_polygon(aes(long, lat, group = group, fill = urate)) +
    coord_map() +
    scale_fill_distiller(palette = "Reds", direction = 1) +

mapthm is a theme based on ggthemes::theme_map that keeps the guide on the right.

Using a faceted display we can look at the result for the incorrect unweighted computation of the state unemployment rate:

gusa_unemp_td <- gather(gusa_unemp, which, rate, urate, urateNW)
ggplot(gusa_unemp_td) +
    geom_polygon(aes(long, lat, group = group, fill = rate)) +
    coord_map() +
    scale_fill_distiller(palette = "Reds", direction = 1) +
    mapthm +
    facet_wrap(~ which, ncol = 1)

2. Iowa Monthly Unemployment Rates over Time

To create the four-month faceted plot it is useful to add county FIPS codes and to clean out the (p) from the final period.

lausUS <- mutate(lausUS,
                 Period = substr(Period, 1, 6),
                 fips = 1000 * State + County,
                 stringsAsFactors = FALSE)

The map data with county FIPS codes:

giowa <- map_data("county", "iowa")
fips_idx <- match(paste(giowa$region, giowa$subregion, sep = ","),
                  sub(":,*", "", county.fips$polyname))
giowa$fips <- county.fips$fips[fips_idx]

A subset of the data for the four months to be shown and the variables needed:

periods <- paste(c("Mar", "Jun", "Sep", "Dec"), 18, sep = "-")
sublaus <- filter(lausUS, Period %in% periods)
sublaus <- select(sublaus, Period, UnempRate, fips)

Make the Period into an ordered factor with levels in the right order:

sublaus <- mutate(sublaus,
                  Period = factor(Period, ordered = TRUE, levels = periods))

Left join the map data with the unemployment data

giowa_laus <- left_join(giowa, sublaus)

The faceted plot:

ggplot(giowa_laus) +
    geom_polygon(aes(long, lat, fill = UnempRate,group = group)) +
    coord_map() +
    scale_fill_distiller(palette = "Reds", direction = 1) +
    facet_wrap(~Period) + mapthm

3. Comparison of Iowa Unemployment Rates

Create plot data with the differences as Udiff:

lausDec18 <- select(filter(lausUS, Period == "Dec-18"), fips, UnempRate) 
lausDec17 <- select(filter(lausUS, Period == "Dec-17"), fips, UnempRate)

dlaus <- left_join(rename(lausDec18, U18 = UnempRate),
                   rename(lausDec17, U17 = UnempRate),
dlaus <- mutate(dlaus, Udiff = U18 - U17)

giowa_dlaus <- left_join(giowa, dlaus)
## Joining, by = "fips"

A diverging color scheme is most appropriate for a comparison.

A map using scale_fill_gratient2:

p <- ggplot(giowa_dlaus) +
    geom_polygon(aes(long, lat, fill = Udiff, group = group)) +
    coord_map() + mapthm
p + scale_fill_gradient2()

To use the same hues with red mapped to the high value you can use the muted function from the scales package:

p + scale_fill_gradient2(low = muted("blue"), high = muted("red"))

Using "RdBu" from RColorBrewer without adjustment places the neutral zero value in the wrong place:

p + scale_fill_distiller(palette = "RdBu")

Using the limits argument is one way to address this:

lim <- max(abs(range(giowa_dlaus$Udiff)))
p + scale_fill_distiller(palette = "RdBu", limits = c(-lim, lim))

An alternative is to provide a rescaler function:

rscl <- function(x, from) 0.5 + 0.495 * x / max(abs(from))
p + scale_fill_distiller(palette = "RdBu", rescaler = rscl)

For a discretized scale, use breaks that include the neutral value zero in the middle of the middle interval and make sure the mapping uses all the classes to keep the neutral color on the middle interval:

breaks <- seq(-2.25, 2.25, len = 10)
##  [1] -2.25 -1.75 -1.25 -0.75 -0.25  0.25  0.75  1.25  1.75  2.25

pd <- ggplot(giowa_dlaus) +
      geom_polygon(aes(long, lat, fill = cut(Udiff, breaks), group = group)) +
      coord_map() + mapthm
pd + scale_fill_brewer(palette = "RdBu", direction = -1, drop = FALSE)

It would be possible to drop the classes not represented on the map from the legend.

4. Optional: Animated Maps over Time

One possible approach is available here