将gtable对象与gganimate一起使用 [英] Using a gtable object with gganimate
问题描述
我正在用gganimate
生成gif,并且我想对绘图格式进行一些调整,这只能通过将ggplot
对象转换为gtable
来实现.例如,我想更改情节标题的位置,使其始终显示在情节的最左角.
I'm producing a gif with gganimate
, and I'd like to make some tweaks to the plot format that can only be accomplished by converting a ggplot
object to a gtable
. For instance, I'd like to change the position of the plot title so that it always appears in the far left corner of the plot.
下面是情节调整的示例:
Here's an example of what the plot tweak would look like:
library(ggplot2)
library(gganimate)
library(dplyr)
# Helper function to position plot title all the way to left of plot
align_titles_left <- function(p, newpage = TRUE) {
p_built <- invisible(ggplot2::ggplot_build(p))
gt <- invisible(ggplot2::ggplot_gtable(p_built))
gt$layout[which(gt$layout$name == "title"), c("l", "r")] <- c(2, max(gt$layout$r))
gt$layout[which(gt$layout$name == "subtitle"), c("l", "r")] <- c(2, max(gt$layout$r))
# Prints the plot to the current graphical device
# and invisibly return the object
gridExtra::grid.arrange(gt, newpage = newpage)
invisible(gt)
}
# Create an example plot
static_plot <- iris %>%
ggplot(aes(x = Sepal.Length, y = Sepal.Width,
color = Species)) +
geom_point() +
labs(title = "This title should appear in the far left.")
# Print the static plot using the adjustment function
align_titles_left(static_plot)
下面是一些示例gganimate
代码,用于将本示例中的情节转变为动画.
Here's some example gganimate
code to turn the plot in this example into an animation.
# Produce the animated plot
static_plot +
transition_states(Species,
transition_length = 3,
state_length = 1)
推荐答案
结果如下.以下说明:
在创建了动画情节的各个帧之后,但要在相关图形设备上绘制它们之前,都应 进行任何使用技巧的骇客操作.此窗口出现在 gganimate:::Scene
的plot_frame
函数中>.
Any hacking with grobs should done after the individual frames of the animated plot have been created, but before they get drawn on the relevant graphics device. This window occurs in the plot_frame
function of gganimate:::Scene
.
我们可以定义自己的Scene
版本,该版本继承原始版本,但使用修改后的plot_frame
函数并插入grob hack行:
We can define our own version of Scene
that inherits from the original, but uses a modified plot_frame
function with the grob hack lines inserted:
Scene2 <- ggproto(
"Scene2",
gganimate:::Scene,
plot_frame = function(self, plot, i, newpage = is.null(vp),
vp = NULL, widths = NULL, heights = NULL, ...) {
plot <- self$get_frame(plot, i)
plot <- ggplot_gtable(plot)
# insert changes here
plot$layout[which(plot$layout$name == "title"), c("l", "r")] <- c(2, max(plot$layout$r))
plot$layout[which(plot$layout$name == "subtitle"), c("l", "r")] <- c(2, max(plot$layout$r))
if (!is.null(widths)) plot$widths <- widths
if (!is.null(heights)) plot$heights <- heights
if (newpage) grid::grid.newpage()
grDevices::recordGraphics(
requireNamespace("gganimate", quietly = TRUE),
list(),
getNamespace("gganimate")
)
if (is.null(vp)) {
grid::grid.draw(plot)
} else {
if (is.character(vp)) seekViewport(vp)
else pushViewport(vp)
grid::grid.draw(plot)
upViewport()
}
invisible(NULL)
})
此后,我们必须在动画处理过程中将Scene
替换为版本Scene2
.我在下面列出了两种方法:
Thereafter, we have to replace Scene
with our version Scene2
in the animation process. I've listed two approaches below:
-
定义单独的动画功能
animate2
,以及根据需要使用Scene2
而不是Scene
的中间功能.我认为这更安全,因为它不会更改gganimate
包中的任何内容.但是,它确实涉及更多代码,&如果功能定义在源代码处发生更改,将来可能会中断.
Define a separate animation function,
animate2
, plus intermediate functions as required to useScene2
instead ofScene
. This is safer, in my opinion, as it doesn't change anything in thegganimate
package. However, it does involve more code, & could potentially break in the future if the function definitions change at the source.
Over-write existing functions in the gganimate
package for this session (based on the answer here). This requires manual effort each session, but the actual code changes required are very small, & probably won't break as easily. However, it also carries the risk of confusing the user, since the same function could lead to different results, depending on whether it's called before or after the change.
方法1
定义功能:
Approach 1
Define functions:
library(magrittr)
create_scene2 <- function(transition, view, shadow, ease, transmuters, nframes) {
if (is.null(nframes)) nframes <- 100
ggproto(NULL, Scene2, transition = transition,
view = view, shadow = shadow, ease = ease,
transmuters = transmuters, nframes = nframes)
}
ggplot_build2 <- gganimate:::ggplot_build.gganim
body(ggplot_build2) <- body(ggplot_build2) %>%
as.list() %>%
inset2(4,
quote(scene <- create_scene2(plot$transition, plot$view, plot$shadow,
plot$ease, plot$transmuters, plot$nframes))) %>%
as.call()
prerender2 <- gganimate:::prerender
body(prerender2) <- body(prerender2) %>%
as.list() %>%
inset2(3,
quote(ggplot_build2(plot))) %>%
as.call()
animate2 <- gganimate:::animate.gganim
body(animate2) <- body(animate2) %>%
as.list() %>%
inset2(7,
quote(plot <- prerender2(plot, nframes_total))) %>%
as.call()
用法:
animate2(static_plot +
transition_states(Species,
transition_length = 3,
state_length = 1))
方法2
在控制台中运行trace(gganimate:::create_scene, edit=TRUE)
,并在&在弹出的编辑窗口中将Scene
更改为Scene2
.
Approach 2
Run trace(gganimate:::create_scene, edit=TRUE)
in console, & change Scene
to Scene2
in the popup edit window.
用法:
animate(static_plot +
transition_states(Species,
transition_length = 3,
state_length = 1))
(两种方法的结果都相同.)
(Results from both approaches are the same.)
这篇关于将gtable对象与gganimate一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!