Ben
Ben

Reputation: 21625

gganimate not showing all frames

I'd like to make a simple gif with 138 states. I don't need any fancy transitions between each state, but I do need every state to appear in my gif. Currently, gganimate is cutting my gif short, not revealing the last ~30ish states.

Example

df <- lapply(1:138, function(t){
  data.frame(
    DT = as.POSIXct("2019-01-01 12:00:00", tz = "UTC") + t*24*60*60,
    x = seq(-3, 3, length.out = 100),
    y = t * seq(-3, 3, length.out = 100)
  )
})
df <- do.call(rbind, df)
range(df$DT)  # "2019-01-02 12:00:00 UTC" "2019-05-19 12:00:00 UTC"

p <- ggplot(data = df, mapping = aes(x = x, y = y)) + 
  geom_point()+ 
  transition_states(states = DT, transition_length = 0, state_length = 1)+
  labs(title = 'ScrapeDT: {previous_state}')
p

enter image description here

As you can see, the gif plays until about 2019-04-10, instead of 2019-05-19. I've also tried tinkering with animate() with no success. For example,

animate(
  plot = p, 
  nframes = length(unique(df$DT)), 
  fps = 8, 
  end_pause = 8
)

enter image description here

How can I make this work so that every desired state is shown for say, 0.25 seconds?

Upvotes: 8

Views: 2471

Answers (2)

Benjamin
Benjamin

Reputation: 896

As stated in the docs, transition_manual() is exactly what you need:

This transition allows you to map a variable in your data to a specific frame in the animation. No tweening of data will be made and the number of frames in the animation will be decided by the number of levels in the frame variable.

Notice what I did below with transition_manual() and labs().

library(tidyverse)
library(gganimate)

df <- lapply(1:138, function(t){
  data.frame(
    DT = as.POSIXct("2019-01-01 12:00:00", tz = "UTC") + t * 24 * 60 * 60,
    x = seq(-3, 3, length.out = 100),
    y = t * seq(-3, 3, length.out = 100)
  )
})
df <- do.call(rbind, df)

p <- ggplot(data = df, mapping = aes(x = x, y = y)) + 
  geom_point()+ 
  transition_manual(frames = DT)+
  labs(title = 'ScrapeDT: {current_frame}')

animate(
  plot = p, 
  nframes = length(unique(df$DT)), 
  fps = 4, 
  end_pause = 8
)

working-animated-gif

Upvotes: 9

asachet
asachet

Reputation: 6921

Looks like you are getting bitten by the frame sampling. From ?animate:

The length and framerate is decided on render time and can be any two combination of nframes, fps, and duration.

Rendering is happening in discrete time units. This means that any event in the animation is rounded of to the nearest frame (e.g. entering will always take a whole number of frames). This means that rounding artifacts are possible when only rendering few frames.

To avoid this you can increase the detail argument. detail will get multiplied to nframes and the resulting number of frames will get calculated, but only nframes evenly spaced frames are rendered.

An easy solution to make sure every state is printed is to set detail > 1. Typically detail = fps will guarantee every frame is used.

animate(
  plot = p, 
  nframes = length(unique(df$DT)), 
  detail = 8,
  fps = 8, 
  end_pause = 8
)

with all frames

Upvotes: 5

Related Questions