将gtable对象与gganimate一起使用 [英] Using a gtable object with gganimate

查看:107
本文介绍了将gtable对象与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:

  1. 定义单独的动画功能animate2,以及根据需要使用Scene2而不是Scene的中间功能.我认为这更安全,因为它不会更改gganimate包中的任何内容.但是,它确实涉及更多代码,&如果功能定义在源代码处发生更改,将来可能会中断.

  1. Define a separate animation function, animate2, plus intermediate functions as required to use Scene2 instead of Scene. This is safer, in my opinion, as it doesn't change anything in the gganimate package. However, it does involve more code, & could potentially break in the future if the function definitions change at the source.

gganimate中为此会话覆盖中的现有功能(基于答案

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆