通过 gganimate 和 ggforce 进行移动面缩放的动画图?
Animated plot with a moving facet zoom via gganimate and ggforce?
目标
我想放大 Europe
这些年来的 GDP。梦幻般的 ggforce::facet_zoom
允许非常容易地用于静态图(即特定年份)。
然而,移动天平比预期的要难。 gganimate
似乎从第一帧 (year == 1952
) 开始采用 x 轴限制并一直持续到动画结束。 This related, but code-wise outdated question did not yield an answer, unfortunately。 + coord_cartesian(xlim = c(from, to))
和 facet_zoom(xlim = c(from, to))
似乎都无法影响超出静态限制的 facet_zoom
window。
- 有什么方法可以让
gganimate
'recalculate'每一帧的facet_zoom
比例?
理想结果
第一帧
最后一帧
当前代码
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)
我认为截至 2018 年 12 月,gganimate 的当前开发版本还不太可能;似乎有一些错误阻止 facet_zoom
与 gganimate
一起玩。幸运的是,我认为解决方法不会太痛苦。
首先,我们可以补间填写中间年份:
# 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))
然后,将您的代码应用到一个需要一年作为输入的函数中:
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)
}
最后,我们可以通过遍历年份(在本例中包括小数年份)来保存动画:
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)
或者,对于较小的文件 <2MB:
使用 gifski
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)
(当我有更多时间时,我会尝试使用手动指定的中断来消除图例中分散注意力的变化。)
目标
我想放大 Europe
这些年来的 GDP。梦幻般的 ggforce::facet_zoom
允许非常容易地用于静态图(即特定年份)。
然而,移动天平比预期的要难。 gganimate
似乎从第一帧 (year == 1952
) 开始采用 x 轴限制并一直持续到动画结束。 This related, but code-wise outdated question did not yield an answer, unfortunately。 + coord_cartesian(xlim = c(from, to))
和 facet_zoom(xlim = c(from, to))
似乎都无法影响超出静态限制的 facet_zoom
window。
- 有什么方法可以让
gganimate
'recalculate'每一帧的facet_zoom
比例?
理想结果
第一帧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)
我认为截至 2018 年 12 月,gganimate 的当前开发版本还不太可能;似乎有一些错误阻止 facet_zoom
与 gganimate
一起玩。幸运的是,我认为解决方法不会太痛苦。
首先,我们可以补间填写中间年份:
# 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))
然后,将您的代码应用到一个需要一年作为输入的函数中:
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)
}
最后,我们可以通过遍历年份(在本例中包括小数年份)来保存动画:
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)
或者,对于较小的文件 <2MB:
使用gifski
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)
(当我有更多时间时,我会尝试使用手动指定的中断来消除图例中分散注意力的变化。)