fredr is very neat

FRED via fredr

The Federal Reserve Economic Database [FRED] is a wonderful public resource for data and the r api that connects to it is very easy to use for the things that I have previously needed. For example, one of my students was interested in commercial credit default data. I used the FRED search instructions from the following vignette to find that data. My first step was the vignette for using fredr. Some library lines give me tools.

library(fredr); library(purrr)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)

A student wanted to find data on charge-offs. fredr has a search capability. Let’s see what we can find.

ChargeOff <- fredr_series_search_text(
  search_text = "real estate charge offs",
  order_by = "popularity",
  sort_order = "desc")
ChargeOff %>% select(title)
## # A tibble: 24 x 1
##    title                                                                        
##    <chr>                                                                        
##  1 Charge-Off Rate on Commercial Real Estate Loans (Excluding Farmland), Booked…
##  2 Charge-Off Rate on Loans Secured by Real Estate, All Commercial Banks        
##  3 Net Charge-Offs on All Loans and Leases, Secured by Real Estate, Commercial …
##  4 Charge-Off Rate on Commercial Real Estate Loans (Excluding Farmland), Booked…
##  5 Net Charge-Offs on All Loans and Leases, Secured by Real Estate, Single Fami…
##  6 Charge-Off Rate on Commercial Real Estate Loans (Excluding Farmland), Booked…
##  7 Charge-Off Rate on Commercial Real Estate Loans (Excluding Farmland), Booked…
##  8 Charge-Off Rate on Loans Secured by Real Estate, Banks Not Among the 100 Lar…
##  9 Charge-Off Rate on Loans Secured by Real Estate, All Commercial Banks        
## 10 Net Charge-Offs on All Loans and Leases, Secured by Real Estate, All Commerc…
## # … with 14 more rows

Wow, there is a rich array of data from that query. An even cooler feature is the ability to retrieve multiple at once in combination with the map_dfr command from the purrr library to make it tidy; that will go through the search results and stack the data that is returned [bind it together by rows]. These map commands are the tidy version of apply. In this case, binding the columns will likely fail because the times series are unlikely to be of the same length and that would create ragged columns. Better tidy from the start here.

library(stringr)
ChargeOff.Data <- map_dfr(ChargeOff$id, fredr) %>% left_join(ChargeOff, by=c("series_id" = "id"))

Just to finish one piece, let me show what these data look like for one series. Splitting the name into two parts gives us the title and subtitle but the split is inconsistent so the solution is not general.

ChargeOff.Short <- ChargeOff.Data %>% filter(series_id=="CORSREOBS") 
SeriesTitle <- ChargeOff.Short[1,"title"] %>% str_split(., "[,]") %>% unlist()
ChargeOff.Short %>% ggplot(data = ., mapping = aes(x = date, y = value)) +
    geom_line() +
    labs(x = "Date", y = "Rate", title=SeriesTitle[[1]], subtitle = SeriesTitle[[2]] )

Counties and Mapping

When I was setting up fredr, I came across some data on subprime credit percentages. The data that I need for what I want to do is not obvious from the vignette and it turns out that this data is stored in a rather inconvenient fashion.

## # A tibble: 1 x 1
##   id                
##   <chr>             
## 1 EQFXSUBPRIME036061

The way that these are organized in FRED is going to make this a mess. The series_id actually contains data. Every data vector is stored as the series name and a FIPS code embdeded. The FIPS code is likely to be the last five characters/numbers. Fun. My other limitation is that I am only seeing 1000 of 3000+ counties. That’s a limit of the API. I am going to have to do this a bit differently. I am going to work backward. I have access to the full set of county fips codes so I think that I can build the dataset to query. Here goes.

library(readr)
CFIPS <- read_delim(url("https://github.com/robertwwalker/academic-mymod/raw/master/data/COUNTYFIPS.txt"), "\t", escape_double = FALSE, trim_ws = TRUE)
## 
## ── Column specification ────────────────────────────────────────────────────────
## cols(
##   FIPS = col_character(),
##   Name = col_character(),
##   State = col_character()
## )
head(CFIPS)
## # A tibble: 6 x 3
##   FIPS  Name    State
##   <chr> <chr>   <chr>
## 1 01001 Autauga AL   
## 2 01003 Baldwin AL   
## 3 01005 Barbour AL   
## 4 01007 Bibb    AL   
## 5 01009 Blount  AL   
## 6 01011 Bullock AL

That is all the FIPS codes that I will need. To create a new character vector for the data, it should just be a new column. From above, I know the names. Let me just concatenate the names to the FIPS code.

CFIPS$series_id <- as.character(paste0("EQFXSUBPRIME0",CFIPS$FIPS))

Now for the moment of truth; I will only try this for Oregon.

SubPrime.OR <- CFIPS %>% filter(State=="OR") %>% select(series_id) %>% unlist() %>% map_dfr(., fredr)
SubPrime.OR <- left_join(SubPrime.OR, CFIPS, by="series_id")
SubPrime.OR %>% ggplot(aes(x=date, y=value, colour=series_id)) +
  geom_line() + theme(legend.position="none") + ggtitle("The Subprime Credit Rate Across Oregon Counties")

Now I have something to work with. Just to show what gganimate can do; I will animate that.

library(gganimate)
library(ggrepel)
library(tidyr)
SubPrime.OR %>% ggplot(aes(x=date, y=value, colour=series_id)) +
  geom_line() + 
  geom_point(aes(group=series_id)) +
  geom_text_repel(aes(y = value, x = as.Date("2019-01-01"), label = Name), hjust = 1, nudge_x = 8) + theme(legend.position="none") + labs(title='The Subprime Credit Rate Across Oregon Counties: {frame_along}', y ="Subprime Rate") +
  transition_reveal(date)
## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 28 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 30 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 30 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 29 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 28 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 23 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 27 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 23 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 23 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 23 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 22 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 22 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 28 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 27 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 21 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 20 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 20 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 19 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 19 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 20 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 20 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 19 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 19 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 21 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 20 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 20 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 27 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 23 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 27 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 27 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 27 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 27 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 22 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 22 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 23 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 23 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 23 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 23 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 24 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 26 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 25 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 29 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
## Warning: ggrepel: 27 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

My goal here is to build an animation of the map of this. So here goes.

states <- map_data("state")
OR.df <- subset(states, region == "oregon")
OR_base <- ggplot(data = OR.df, mapping = aes(x = long, y = lat, group = group)) +
geom_polygon(color = "black", fill = "gray")+ theme_minimal() 
OR_base + geom_point(aes(x=-123.0433, y=44.925167))

library(emoGG)
counties <- map_data("county")
OR.county <- subset(counties, region == "oregon")
OR.Map <- ggplot(data = OR.df, mapping = aes(x = long, y = lat, group = group)) + 
    geom_polygon(color = "black", fill = "gray")+ theme_minimal() 
OR_emoj <- OR_base +  add_emoji("1f352", x=-123.0433, y=44.925167, ysize=0.5)
OR_emoj

Now to build a first map to make sure that it works.

library(maps)
## 
## Attaching package: 'maps'
## The following object is masked from 'package:purrr':
## 
##     map
county.fips$fips <- as.character(county.fips$fips)
SubPrime.M <- left_join(SubPrime.OR, county.fips, by=c("FIPS"="fips"))
SubPrime.M <- SubPrime.M %>% separate(., polyname, c("region","subregion"), sep=",")
SubPrime.M %>% filter(date=="1999-01-01") -> OneYear
OR.MD <- inner_join(OR.county, OneYear, by = "subregion")

One of my favortite bits of code from I do not know where:

ditch_the_axes <- theme(
  axis.text = element_blank(),
  axis.line = element_blank(),
  axis.ticks = element_blank(),
  panel.border = element_blank(),
  panel.grid = element_blank(),
  axis.title = element_blank()
  )

Now to a single map.

Polt1 <- OR.Map + 
      geom_polygon(data = OR.MD, aes(fill = value), color="white") +
#      geom_polygon(color = "black", fill = NA) +
      theme_bw() +
      ditch_the_axes +
      ggtitle("Subprime Credit Percentages by County")
Polt1

Now for the general case. Same merge but with all the data.

OR.MD2 <- left_join(SubPrime.M, OR.county, by = "subregion")
OR.MD2 <- OR.MD2 %>% mutate(Subprime.Pct = value)

Here goes.

library(viridis)
## Loading required package: viridisLite
# ggplot(data = OR.MD2, mapping = aes(x = long, y = lat, group = group)) +
p <- OR.Map +
  geom_polygon(data=OR.MD2, aes(x = long, y = lat, group = subregion, fill = Subprime.Pct), color="white") +
  viridis::scale_fill_viridis(option="C") +   
  ditch_the_axes +
  labs(title = 'Subprime Credit Percentage by Quarter: {closest_state}') +
  transition_states(date, transition_length = 3, state_length = 3)
animate(p, nframes = 300)