Converting a Tableau dashboard to a Flexdashboard

Edited on 2017-01-27 to correct typos and fix tootip in dashboard

IN THIS POST WE WILL CONVERT a data visualization dashboard I made some time ago using Tableau into a flexdashboard using R.

On Monday, the Census posted a blog summarizing recent mobility trends. According to the CPS ASEC, 11.2 percent of the U.S. population age 1 and over moved between 2015 and 2016, the lowest since the CPS ASEC began in 1948. This post reminded me a visualization I made some time ago showing state-to-state migration using Census data.

As I have been on a kick making R flexdashboards (see this, this, this and this for examples and this post for a guide on building a flexdashboard). I figured it would be fun to convert this viz into a flexdashboard.

This would also give me a chance to explore maps with plotly.

The orginal

First take a look at the original viz:

This dashboard is relatively simple. It’s a single page with two linked maps and two linked tables.

The maps depict state-to-state migration in 2014. For any selected state the top map shows outmigration, the number of people (over age 1) who migrated out of the selected state to another state in 20114. The bottom map shows in migration, or the number of people who migrated into each state from another state. There’s also two tables showing stats for the top 10 states in terms of in migration and out migration respectively.

There’s a filter box to select states. Go ahead and give it a try if you haven’t.

A Flexdashboard version

We can recreate many of these features in a flexdashboard.

The data

The data for this dashboard come from the U.S. Census Bureau, available here. The data show state-to-state migration. I’ve saved the data in a text file called mig2015.txt (click here to download).

I organized these data (mainly deleting irrelevant columns and unmerging cells) in Excel. Let’s take a look:

## Loading tidyverse: ggplot2
## Loading tidyverse: tibble
## Loading tidyverse: tidyr
## Loading tidyverse: readr
## Loading tidyverse: purrr
## Loading tidyverse: dplyr
## Conflicts with tidy packages ----------------------------------------------
## between():   dplyr, data.table
## filter():    dplyr, stats
## first():     dplyr, data.table
## lag():       dplyr, stats
## last():      dplyr, data.table
## transpose(): purrr, data.table
## Attaching package: 'lubridate'
## The following objects are masked from 'package:data.table':
##     hour, isoweek, mday, minute, month, quarter, second, wday,
##     week, yday, year
## The following object is masked from 'package:base':
##     date

htmlTable(head(df %>% map_if(is.Date, as.character,format="%b %d,%Y") %>% map_if(is.numeric, round,2) %>% ,5), col.rgroup = c("none", "#F7F7F7"),caption="2015 State to State Migration Data",
          tfoot="Source: U.S. Census Bureau")
2015 State to State Migration Data state.from total statecode.from
1 Alabama Alabama 0 AL AL
2 Alabama Alaska 767 AL AK
3 Alabama Arizona 1865 AL AZ
4 Alabama Arkansas 2329 AL AR
5 Alabama California 3397 AL CA
Source: U.S. Census Bureau

These data show migration across states based on the Census tables. The variable is the destination state while state.from is the state of residence one year prior. I’ve excluded migration from outside the U.S. and from Puerto Rico and U.S. island territories. Also, migration within the same state is set equal to zero for this exercise (e.g. from Alabama to Alabama set equal to zero).

Getting the data to talk

Like in my guide on building a flexdashboard we are going to use crosstalk to get the graphs to respond to a filter select box. And like before, we’ll use plotly to make the graphs. We’ll also use DT to add an interactive data table.

Our plan is to be able to use a single filter box to filter two maps and a data table. The tricky part is that in one map we want to filter on to show all the in migration by state, while on another map we want to filter on state.from to show out migration. We can do this by setting using ShareData with groups:

sd.from <- SharedData$new(df.from, ~state.from,group="state") <- SharedData$new(df,,group="state")

We use SharedData$new to create both sd.from and and use group="state" to indicate that these are part of a common group. Then, when we filter on state using the state group, we’ll filter both sd.from and

Then we us the bs_cols function from crosstalk to set up a filter:

  filter_select("state", "Select State",,,multiple=F)

We only want to filter on one state at a time so we set multiple=FALSE.

Then, it’s just a matter of laying out the flexdashboard and setting up our widgets. For comparison, we embedded the original Tableau dashboard in our flexdashboard. How meta!

You can see a fullscreen version here.

You can get the code by clicking on the source button on the top right.