Search code examples
rggplot2gganimate

is there any way to create an animation (gganimate) with ranges as transition times?


Is there any way to make gganimate work for transition times that are ranges of years? In my data I have three time points, two of which are ranges as shown below.

data:

Year   rate   group
2012-2014   7   Other CT, White 
2015-2017   11  Other CT, White 
2018    3   Fairfield, Black    
2018    2   Fairfield, Hispanic

here's an example of the code for the ggplot that I'd like to animate

data %>% ggplot(aes(y = rate, x = group)) +
  geom_col() +
  coord_flip() +
  labs(title = "Year: {frame_time}") +
  transition_time(Year)

when I enter the transition time as "Year" I get an error because my Year variable is a character to accommodate the ranges. This is the error I get:

Error: time data must either be integer, numeric, POSIXct, Date, difftime, orhms

is there anything I can do to bypass this error and continue with the ranges as they are?


Solution

  • I'd suggest either using transition_manual and treat the years like categories (which loses a smooth transition), or convert the year range to numbers.

    library(tidyverse); library(gganimate)
    df1 <- tribble(~Year, ~rate, ~group,
                   "2012-2014", 7, "grp1",
                   "2015-2017", 11, "grp1",
                   "2018", 3, "grp1")
    

    First approach, keeping Year as-is as character:

    df1 %>% 
      ggplot(aes(y = rate, x = group)) +
      geom_col() +
      coord_flip() +
      labs(title = "Year: {current_frame}") +
      transition_manual(Year)
    

    enter image description here

    Second approach, converting year to numeric. In this case, I just used the first year, but you could alternatively assign the value to the average year, or add rows with the value for each year in the range.

    df1 %>% 
      mutate(Year_numeric = parse_number(Year)) %>%
      ggplot(aes(y = rate, x = group)) +
      geom_col() +
      coord_flip() +
      labs(title = "Year: {round(frame_time)}") +
      transition_time(Year_numeric)
    

    enter image description here

    Finally, if you want to represent all the ranged years at the given level, you can create rows for all the component years. But this takes some elbow grease:

    df1 %>% 
      # For ranged years, find how many in range:
      mutate(year_num = 1 + if_else(Year %>% str_detect("-"), 
                                str_sub(Year, start = 6) %>% as.numeric() - 
                                  str_sub(Year, end = 4) %>% as.numeric(), 
                                0)) %>%
      # ... and use that to make a row for each year in the range
      tidyr::uncount(year_num) %>%
      group_by(Year) %>%
      mutate(Year2 = str_sub(Year, end = 4) %>% as.numeric() + 
                     row_number() - 1) %>%
      ungroup() %>%
    
    # FYI at this point it looks like:
    # A tibble: 7 x 4
    #  Year       rate group Year2
    #  <chr>     <dbl> <chr> <dbl>
    #1 2012-2014     7 grp1   2012
    #2 2012-2014     7 grp1   2013
    #3 2012-2014     7 grp1   2014
    #4 2015-2017    11 grp1   2015
    #5 2015-2017    11 grp1   2016
    #6 2015-2017    11 grp1   2017
    #7 2018          3 grp1   2018
    
    ggplot(aes(y = rate, x = group)) +
      geom_col() +
      coord_flip() +
      labs(title = "Year: {round(frame_time)}") +
      transition_time(Year2)
    

    enter image description here