Search code examples
rggplot2gganimate

gganimate: make points stay several frames before and after


I have a data.frame containing timestamped events of different kinds, geolocated. I know how to plot an animation of each event as a point, hour by hour, with gganimate (*). It would be something like:

df = data.frame("id"  = runif(500, 1e6, 1e7),
           'lat' = runif(500, 45, 45.1),
           'long'= runif(500, 45, 45.1),
           'datetime'= seq.POSIXt(from=Sys.time(), to=Sys.time()+1e5, length.out=500),
           'hour'=format(seq.POSIXt(from=Sys.time(), to=Sys.time()+1e5, length.out=500), "%H"),
           'event'=paste0("type", rpois(500, 1)))

ggplot(data=df) + 
  aes(x=long, y=lat, color=factor(event)) + 
  geom_point() +
  transition_states(hour, state_length = 1, transition_length = 0)

Now I would like to make points "stay" longer on screen, for instance if an event is at 5:00pm, i want it to be displayed on the animation from 2pm until 8pm (3 frames before and after his position, and if possible fade in and out). I don't know how to do that with gganimate, I tried to use transition_length but it's making the points "move" and that makes no sense for me!

Thanks,

(*) Edit: I thought of adding 6 duplicated rows for each row, and modifying the hour by -1 to +3, but it's a lot heavier and can't deal with fade in/out

library(magrittr)

df %<>% mutate(hour = hour + 2) %>% bind_rows(df)
df %<>% mutate(hour = hour + 1) %>% bind_rows(df)
df %<>% mutate(hour = hour - 4) %>% bind_rows(df)
df %<>% mutate(hour = hour %% 24 )

Solution

  • You can use transition_components and specify 3 hours as the enter / exit length for each point.

    Data:

    set.seed(123)
    n <- 50 # 500 in question
    df = data.frame(
      id       = runif(n, 1e6, 1e7),
      lat      = runif(n, 45, 45.1),
      long     = runif(n, 45, 45.1),
      datetime = seq.POSIXt(from=Sys.time(), to=Sys.time()+1e5, length.out=n),
      hour     = format(seq.POSIXt(from=Sys.time(), to=Sys.time()+1e5, length.out=n), "%H"),
      event    = paste0("type", rpois(n, 1)))
    

    Code:

    df %>%
      mutate(hour = as.numeric(as.character(hour))) %>%
    
      ggplot() +
      aes(x=long, y=lat, group = id, color=factor(event)) + 
    
      # I'm using geom_label to indicate the time associated with each
      # point & highlight the transition times before / afterwards.
      # replace with geom_point() as needed
      geom_label(aes(label = as.character(hour))) +
      # geom_point() +
    
      transition_components(hour, 
                            enter_length = 3, 
                            exit_length = 3) +
      enter_fade() +
      exit_fade() +
      ggtitle("Time: {round(frame_time)}")
    

    plot 1

    This approach works with a datetime variable as well:

    df %>%
      ggplot() +
      aes(x = long, y = lat, group = id, color = factor(event)) +
      geom_label(aes(label = format(datetime, "%H:%M"))) +
      transition_components(datetime,
                            enter_length = as_datetime(hm("3:0")),
                            exit_length = as_datetime(hm("3:0"))) +
      enter_fade() +
      exit_fade() +
      ggtitle("Time: {format(frame_time, '%H:%M')}")
    

    plot