Data visualization animations and interactivity

Deepsha Menghani

How much do data visualizations add to story telling?


A. Little BAM.

B. BAM!

C. Double BAM!!

D. Triple BAM!!!


(Psssss: BAMs are courtesy Josh Stramer)

Triple BAM!!!

But why do we care about animated visualizations?


  • To create Involuntary Shifts of Attention

  • Drive your point across impactfully

  • And sometimes, because it looks pretty

Let’s jump into an example

Package 1: GGAnimate

US population dataset


Single state plot

density_rank_ggplot <- plot_density_vs_rank(
  data = population_dataset_clean, 
  state_input = c('California')
)

ggplotly(density_rank_ggplot, tooltip = "text")

Single state plot

Multiple state plot

## Plot for multiple states ----
density_rank_plot_multistate <- plot_density_vs_rank(
  data = population_dataset_clean,
  state_input = c('California', 'Washington', 'Alabama', 'Pennsylvania')
)

ggplotly(density_rank_plot_multistate, tooltip = "text")

Multiple state plot

Let’s get animating!!!

A single line of code!

density_rank_plot_multistate_animated <- density_rank_plot_multistate +
    transition_reveal(Date) + 
    ease_aes('linear')

animate(density_rank_plot_multistate_animated, duration =10, fps = 10, width = 900, height = 600, renderer = gifski_renderer())

A single line of code!

One more example - US state population map

Population across states in 2022

## Plot for single year ----
usa_population_map_single_year <- plot_usa_population_map(data = population_dataset_lat_long %>% filter(Year %in% (2020))) + theme(legend.position = "none")

ggplotly(usa_population_map_single_year, tooltip = "text")

Population across states in 2022

1990 vs 2020

usa_population_map_multiyear <- plot_usa_population_map(data = population_dataset_lat_long %>% filter(Year %in% c(1910,2020)))

usa_population_map_animated <- usa_population_map_multiyear +
    labs(subtitle = 'Year: {closest_state}') +
    transition_states(Year) + 
    ease_aes('linear')

animate(usa_population_map_animated, duration =2, fps = 10, width = 900, height = 600, renderer = gifski_renderer())

1990 vs 2020

Let’s make that slower

animate(usa_population_map_animated, duration =10, fps = 10, width = 900, height = 600, renderer = gifski_renderer())

Let’s make that slower

Not always rainbows and butterflies

Pros

  • Easily animate almost any plot

  • Make it part of your report or save as a gif

Cons

  • No interactivity to pause and play

  • Very slow rendering of gif

  • Cannot focus on only part of the plot

Package 2: Plotly

Let’s add interactivity to the plot

## Plotting function ----
# Select states to plot

state_selected <- c('California', 'Washington', 'Alabama', 'Pennsylvania')
data <- population_dataset_clean %>% filter(Name %in% state_selected)

## Convert to frames 

data_med <- data %>%
  arrange(Year, Name) %>% 
  split(.$Year) %>%
  accumulate(~bind_rows(.x, .y)) %>%
  bind_rows(.id = "frame") %>%
  group_by(frame) %>% 
  arrange(Rank_Population_Density)

Plot the frames

data_med %>%
  plot_ly(x = ~Resident_Population_Density, y = ~Rank_Population_Density, color = ~Name,
    hoverinfo = "text", text = ~paste0(Name,"\n",Year,"\n",Rank_Population_Density))  %>%
  add_text(x = 250, y = 18, text = ~Year, frame = ~Year,
           textfont = list(color = toRGB("gray80"), size = 40)) %>%
  add_lines(frame = ~frame) %>%
  add_markers(frame = ~frame) %>%
  animation_opts(
    frame = 1000, 
    transition = 0, 
    easing = "bounce"
  ) %>% 
  hide_legend()

Plot the frames

Highlight specific selections with Crosstalk

# Create a shared data object keyed by state
state_data <- SharedData$new(data_med, key = ~Name, group = "Select a state")

Relative to GGAnimate

Pros

  • Addition of interactivity

  • Much faster rendering for live reports

Cons

  • Can’t save as a gif and send it over

  • Requires frame creation