I would like to zoom in on the GDP of Europe
throughout the years. The phantastic ggforce::facet_zoom
allows this for static plots (i.e., for one specific year) very easily.
Moving scales, however, prove harder than expected. gganimate
seems to take the x-axis limits from the first frame (year == 1952
) and continute until the end of the animation. This related, but code-wise outdated question did not yield an answer, unfortunately. Neither + coord_cartesian(xlim = c(from, to))
, nor facet_zoom(xlim = c(from, to))
seems to be able to influence the facet_zoom
window beyond static limits.
gganimate
'recalculate' the facet_zoom
scales for every frame?library(gapminder)
library(ggplot2)
library(gganimate)
library(ggforce)
p <- ggplot(gapminder, aes(gdpPercap, lifeExp, size = pop, color = continent)) +
geom_point() + scale_x_log10() +
facet_zoom(x = continent == "Europe") +
labs(title = "{frame_time}") +
transition_time(year)
animate(p, nframes = 30)
I don't think it's possible quite yet with the current dev version of gganimate as of Dec 2018; there seem to be some bugs which prevent facet_zoom
from playing nice with gganimate
. Fortunately, I don't think a workaround is too painful.
First, we can tween to fill in the intermediate years:
# Here I tween by fractional years for more smooth movement
years_all <- seq(min(gapminder$year),
max(gapminder$year),
by = 0.5)
gapminder_tweened <- gapminder %>%
tweenr::tween_components(time = year,
id = country,
ease = "linear",
nframes = length(years_all))
Then, adopting your code into a function that takes a year as input:
render_frame <- function(yr) {
p <- gapminder_tweened %>%
filter(year == yr) %>%
ggplot(aes(gdpPercap, lifeExp, size = pop, color = continent)) +
geom_point() +
scale_x_log10(labels = scales::dollar_format(largest_with_cents = 0)) +
scale_size_area(breaks = 1E7*10^0:3, labels = scales::comma) +
facet_zoom(x = continent == "Europe") +
labs(title = round(yr + 0.01) %>% as.integer)
# + 0.01 above is a hack to override R's default "0.5 rounds to the
# closest even" behavior, which in this case gives more frames
# (5 vs. 3) to the even years than the odd years
print(p)
}
Finally, we can save an animation by looping through through the years (which in this case include fractional years):
library(animation)
oopt = ani.options(interval = 1/10)
saveGIF({for (i in 1:length(years_all)) {
render_frame(years_all[i])
print(paste0(i, " out of ",length(years_all)))
ani.pause()}
},movie.name="facet_zoom.gif",ani.width = 400, ani.height = 300)
or, alternatively, using gifski
for a smaller file <2MB:
gifski::save_gif({ for (i in 1:length(years_all) {
render_frame(years_all[i])
print(paste0(i, " out of ",length(years_all)))
}
},gif_file ="facet_zoom.gif", width = 400, height = 300, delay = 1/10, progress = TRUE)
(When I have more time, I'll try to remove the distracting changes in the legends by using manually specified breaks.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With