Reputation: 21625
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.
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
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
)
How can I make this work so that every desired state is shown for say, 0.25 seconds?
Upvotes: 8
Views: 2471
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
)
Upvotes: 9
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
)
Upvotes: 5