【问题标题】:How to pipe forward a ggplot object?如何通过管道转发 ggplot 对象?
【发布时间】:2017-03-30 22:39:17
【问题描述】:

我想使用在带有magrittr 管道的ggplot 对象上定义的自定义函数。但是,我无法将 ggplot 对象通过管道传输到此函数中。

这是一个简单的例子:

library(ggplot2)
library(magrittr)

my_plot_function <- function(plot) {
    plot + geom_hline(yintercept = 3, linetype = 'dashed')
}

data(mtcars)
p <- mtcars %>%
    ggplot() +
    geom_point(aes(mpg, wt))
my_plot_function(p)

如果我可以在链中使用my_plot_function(),那就太好了:

mtcars %>%
    ggplot() +
    geom_point(aes(mpg, wt)) %>%
    my_plot_function()

但是,它给出了一个错误,因为只有图层被传递给my_plot_function() 而不是绘图本身。我怎么能用管道传递情节?

【问题讨论】:

  • 您无法更改 R 中的运算符优先级,因此唯一的方法是将 ggplotting 内容放在括号中的 %&gt;%s 之间。不过,这可能不是您想要的。
  • @KonradRudolph 你的意思是mtcars %&gt;% (ggplot() + geom_point(aes(mpg, wt))) %&gt;% my_plot_function()?它似乎不起作用。
  • 我的意思是(mtcars %&gt;% ggplot() + geom_point(aes(mpg, wt))) %&gt;% my_plot_function()(有效),但我的第一条评论中的描述是错误的,并且暗示了您编写的代码。
  • @Konrad 您无法更改运算符优先级,但您可以使用具有正确优先级的运算符,方法是定义 -.gg 或覆盖 +.gg
  • @Moody_Mudskipper 当然可以。但是定义一个新的操作符并不能完全使代码直观易懂,而用不同的语义覆盖+.gg 会更糟,因为它会主动做一些与预期不同的事情。

标签: r ggplot2 pipe


【解决方案1】:

您可以尝试定义一个不需要绘图对象的函数,然后像往常一样在ggplot 中添加它。

my_plot_function <- function() {
    geom_hline(yintercept = 3, linetype = 'dashed')
}


mtcars %>%
    ggplot() +
    geom_point(aes(mpg, wt)) + my_plot_function()

【讨论】:

  • 哦,没想到解决办法这么简单:)
  • 您甚至可以通过返回 geom 或其他 ggplot 函数列表从 my_plot() 获得多个 geom。
【解决方案2】:

对于一个参数函数,例如您建议的函数,您可以通过定义 -.gg 来使用以下技巧:

`-.gg` <- function(e1, e2) e2(e1)

my_plot_function <- function(plot) {
  plot + geom_hline(yintercept = 3, linetype = 'dashed')
}

mtcars %>%
  ggplot() +
  geom_point(aes(mpg, wt)) -
  my_plot_function

绘制图表


我经常使用这个技巧来使用ggplotly

library(plotly)
mtcars %>%
  ggplot() +
  geom_point(aes(mpg, wt)) -
  ggplotly

绘制交互式图表


或绘制中间图表

mtcars %>%
  ggplot() +
  geom_point(aes(mpg, wt)) -
  plot +
  facet_wrap(~cyl)

绘制 2 张图表


对于多参数函数,您可以使用purrr::partial

my_plot_function2 <- function(plot, msg) {
  print(msg)
  plot + geom_hline(yintercept = 3, linetype = 'dashed')
}

library(purrr)
mtcars %>%
  ggplot() +
  geom_point(aes(mpg, wt)) -
  partial(my_plot_function2, msg="hello")
# [1] "hello"

...并绘制图表


动态定义函数,在本例中用于编辑绘图数据:

mtcars %>%
  ggplot() +
  geom_point(aes(mpg, wt)) -
  as_mapper(~{.$data <- head(.$data);.})

用 6 个点绘制图表

【讨论】:

    【解决方案3】:

    您需要按照评论建议将整个链包装在() 中。以下是接受答案的模板失败的示例:

    library(ggplot2)
    library(magrittr)
    
    my_plot_function <- function() {
        geom_hline(yintercept = 3, linetype = 'dashed') +
        geom_vline(xintercept = 20, linetype = 'dotted')
    }
    my_plot_function2 <- function(plot) {
        plot + geom_hline(yintercept = 3, linetype = 'dashed') +
        geom_vline(xintercept = 20, linetype = 'dotted')
    }
    data(mtcars)
    

    下面会报错:

    mtcars %>%
        ggplot() +
        geom_point(aes(mpg, wt)) + my_plot_function()
    

    以下将起作用:

    (mtcars %>%
        ggplot() + geom_point(aes(mpg, wt))) %>%
        my_plot_function2()
    

    如果你想保持数据链分离,你可以在 ggplot() 调用周围使用匿名函数:

    mtcars %>%
        (function(dat) ggplot(dat) + geom_point(aes(mpg, wt))) %>%
        my_plot_function2()
    

    【讨论】:

      【解决方案4】:

      如果您确实需要一个必须有参数的函数,那么wrapr 的点箭头管道%.&gt;% 可以完美地替代+。然后你必须指定 . 作为你的函数的输入。

      Documentation


      另一个可能更简单的选择是在函数中返回一个列表。示例:

      foo <- function() {
        Out <- list(
          theme(
            panel.grid.major = element_blank(),
            panel.grid.minor = element_blank(),
            axis.line = element_line(colour="black"),
          expand_limits(x = 0, y = 0),
          scale_x_continuous(expand = c(0, 0)),
          scale_y_continuous(expand = c(0, 0))
        )
      
        return(Out)
      }
      

      后一种选择的用处在于,标准化您的图形变得更加容易,例如定制主题还需要对非主题功能进行一些更改(如上例所示)

      【讨论】:

        猜你喜欢
        • 2014-09-25
        • 1970-01-01
        • 1970-01-01
        • 2021-04-23
        • 2017-12-19
        • 2010-11-29
        • 1970-01-01
        • 1970-01-01
        • 2021-09-26
        相关资源
        最近更新 更多