错误:无法将 ggsave 添加到 ggplot 对象

Eud*_*ald 3 r ggplot2

我已经在 Windows 10 机器上设置了全新的 R 安装,并且无法运行像以下这样简单的东西:

data.frame(a = rnorm(100), b = rnorm(100)) |> 
  ggplot(aes(a, b)) +
  ggsave("temp.png")
Run Code Online (Sandbox Code Playgroud)

因为我收到以下错误:

Error: Can't add `ggsave("temp.png")` to a ggplot object.
Run Code Online (Sandbox Code Playgroud)

我的会话信息是:

R version 4.1.0 (2021-05-18)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)

Matrix products: default

locale:
[1] LC_COLLATE=Catalan_Spain.1252  LC_CTYPE=Catalan_Spain.1252    LC_MONETARY=Catalan_Spain.1252 LC_NUMERIC=C                  
[5] LC_TIME=Catalan_Spain.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] ggplot2_3.3.4 dplyr_1.0.6  

loaded via a namespace (and not attached):
 [1] magrittr_2.0.1    tidyselect_1.1.1  munsell_0.5.0     colorspace_2.0-1  R6_2.5.0          rlang_0.4.11      fansi_0.5.0       tools_4.1.0      
 [9] grid_4.1.0        data.table_1.14.0 gtable_0.3.0      utf8_1.2.1        withr_2.4.2       ellipsis_0.3.2    digest_0.6.27     tibble_3.1.2     
[17] lifecycle_1.0.0   crayon_1.4.1      purrr_0.3.4       farver_2.1.0      vctrs_0.3.8       glue_1.4.2        labeling_0.4.2    compiler_4.1.0   
[25] pillar_1.6.1      generics_0.1.0    scales_1.1.1      pkgconfig_2.0.3  

Run Code Online (Sandbox Code Playgroud)

我已授予我正在处理的目录的权限,并在不同的目录中尝试并使用 RScript、RStudio 和 Pycharm R 控制台运行,但始终遇到相同的问题。

提前致谢。

编辑:这曾经适用于 ggplot2 3.3.3,这是对 3.3.4 的更新破坏了事情。

r2e*_*ans 12

您不能“添加”ggsave到 ggplot 添加管道。

编辑:这是ggplot2-3.3.4. 如果您想解决新行为,则将在下面保留先前的答案。如果您对此特别恼火,您可能会提交一个新问题以ggplot2建议他们 (a) 撤消破坏性更改,或 (b) 更好地记录意外功能中的更改。

(实际上,我不记得看过表明+ ggsave(.)应该可行的文档,因此对新问题的回应可能是他们不想为了放弃其他一些优雅的完整性而保留意外的“功能”。)

3.3.3 到 3.3.4 (for save.R)的更改大多与保存文件的行为无关。但是,一项功能更改是来自 的返回值ggsave

@@ -90,5 +98,5 @@ ggsave <- function(filename, plot = last_plot(),
   grid.draw(plot)

-  invisible()
+  invisible(filename)
 }
Run Code Online (Sandbox Code Playgroud)

回想起来,这是有道理的:ggplot2使用+-pipes的能力在尝试添加 -like 对象时往往没问题NULL。也就是说,这可以正常工作

data.frame(a = rnorm(100), b = rnorm(100)) |>
  ggplot(aes(a, b)) +
  NULL
Run Code Online (Sandbox Code Playgroud)

为什么NULL在这里是相关的?因为之前的 (3.3.3) 版本以ggsave结束invisible(),无形中又返回了NULL。(在内部,ggplot2:::add_ggplot以 开头if (is.null(object)) return(p),这解释了为什么会这样。)

invisible(filename)然而,随着更改为(imo,实际上更好一点),这实际上与

data.frame(a = rnorm(100), b = rnorm(100)) |>
  ggplot(aes(a, b)) +
  "temp.png"
Run Code Online (Sandbox Code Playgroud)

这是没有意义的,所以+-piping 失败了。

在 中ggplot2-3.3.3,您可以使用黑客/丑陋的代码复制此错误:

data.frame(a = rnorm(100), b = rnorm(100)) |>
  ggplot(aes(a, b)) +
  { ggsave("temp.png"); "temp.png"; }
# Error: Can't add `{` to a ggplot object.
# * Can't add `    ggsave("temp.png")` to a ggplot object.
# * Can't add `    "temp.png"` to a ggplot object.
# * Can't add `}` to a ggplot object.
Run Code Online (Sandbox Code Playgroud)

这足够接近你看到的错误(我相信)证明了我的观点:新的和改进的 ggplot2-3.3.4 正在返回一个字符串,这足以打破你添加ggsave到 ggplot2 的习惯模式目的。

如果您要向 ggplot2 提交一个新问题,那么我建议您将其定义为“功能请求”:如果它invisible(filename)是一个适用的类对象+,则可以保留以前的行为,同时仍支持字符串-返回。例如(完全未经测试):

ggsave <- function(file, ...) {
  # .....
  class(filename) <- c("ggplot2_string", "character")
  invisible(filename)
}
Run Code Online (Sandbox Code Playgroud)

然后扩展+.gg-logic 以实际处理字符串,可能类似于

`+.gg` <- function (e1, e2) {
    if (missing(e2)) {
        abort("Cannot use `+.gg()` with a single argument. Did you accidentally put + on a new line?")
    }
    if (inherits(e2, "ggplot2_string")) {
      e2 <- NULL
      e2name <- "NULL"
    } else {
      e2name <- deparse(substitute(e2))
    }
    if (is.theme(e1)) 
        add_theme(e1, e2, e2name)
    else if (is.ggplot(e1)) 
        add_ggplot(e1, e2, e2name)
    else if (is.ggproto(e1)) {
        abort("Cannot add ggproto objects together. Did you forget to add this object to a ggplot object?")
    }
}
Run Code Online (Sandbox Code Playgroud)

不,我不认为这是最好的方式,但这是一种方式,可以讨论。


你可以做的四件事:

  1. 绘制它,然后保存它。它将显示在您的图形设备/窗格中。

    data.frame(a = rnorm(100), b = rnorm(100)) |>
      ggplot(aes(a, b))
    ggsave("temp.png")
    
    Run Code Online (Sandbox Code Playgroud)
  2. 保存到不渲染的中间对象,并保存:

    gg <- data.frame(a = rnorm(100), b = rnorm(100)) |>
      ggplot(aes(a, b))
    ggsave("temp.png", plot = gg)
    
    Run Code Online (Sandbox Code Playgroud)
  3. 如果 R-4.1,管道它做plot=参数。虽然我还没有 R-4.1,但根据评论,我相信 while|>总是将前一个结果作为下一个调用的第一个参数传递,您可以通过命名file=参数来解决这个问题,这意味着 R-4.1将传递给第一个可用参数,它(在这种情况下)恰好plot=是我们需要的。

    data.frame(a = rnorm(100), b = rnorm(100)) |>
      ggplot(aes(a, b)) |>
      ggsave(file = "temp.png")
    
    Run Code Online (Sandbox Code Playgroud)
  4. 如果您使用的是 magrittr 管道,那么您可以更简洁地做同样的事情:

    library(magrittr) # or dplyr, if you're using it for other things
    data.frame(a = rnorm(100), b = rnorm(100)) %>% # or |> here
      ggplot(aes(a, b)) %>%                        # but not |> here
      ggsave("temp.png", plot = .)
    
    Run Code Online (Sandbox Code Playgroud)

  • `|&gt; {\(p) ggsave("temp.png",plot = p)}()` 也可以,但是太多了。 (2认同)
  • 有趣的。坦率地说,我从来没有假设“ggsave”在“+”管道中工作,假设它不在该构造中,但你是对的,你的代码在 ggplot2-3.3.3 中工作,我从来没有试图这样做。不确认你的代码对我来说失败了(事实并非如此),这对我来说很糟糕。我看到了真正的罪魁祸首,等待编辑...... (2认同)
  • 我认为说“它破坏了所有向后兼容性”有点苛刻(关键字是:“全部”);充其量你可能会说它可能会破坏一些意想不到的和无记录的行为。我认为 r2evans 的前三个建议是“ggsave()”的优秀规范用法(我不熟悉第四个)。 (2认同)

Ped*_*car 7

只需删除加号即可。

data.frame(a = rnorm(100), b = rnorm(100)) |> 
  ggplot(aes(a, b))

ggsave("temp.png")
Run Code Online (Sandbox Code Playgroud)

ggsave 有一个默认输入last_plot()