‘Can’t use `!!!` at top level.’ 的意思是什么,如何解决这个问题?

huangapple go评论66阅读模式
英文:

What does 'Can't use `!!!` at top level.' mean and how to resolve it?

问题

我正在尝试创建一个用于创建棒棒糖图的函数,使用ggplot2。我想要将所有参数传递给geom_point()中的aes(),但是,我想要排除size参数不传递给geom_segment()中的aes()(如果您查看下面的a()的输出,就会明显为什么)。因此,我使用rlang::enquos()来捕获...,而不是直接传递它。在函数a()中,我将dots传递给ggplot()中的aes(),这没有问题。但在函数b()中,我收到错误消息Can't use '!!!' at top level.

我在这一点上陷入困境,将感激任何解决此问题的输入。

library(ggplot2)
data("mtcars")

d <- dplyr::count(mtcars, cyl, am)

a <- function(data, x, y, ...) {
  x <- rlang::enquo(x)
  y <- rlang::enquo(y)
  dots <- rlang::enquos(...)
  
  ggplot(data, aes(!!x, !!y, !!!dots)) +
    geom_segment(aes(y = 0, xend = !!x, yend = !!y)) +
    geom_point()
}

b <- function(data, x, y, ...) {
  x <- rlang::enquo(x)
  y <- rlang::enquo(y)
  
  dots <- rlang::enquos(...)
  segment_args <- dots[names(dots) != "size"]

  ggplot(data, aes(!!x, !!y)) +
    geom_segment(aes(y = 0, xend = !!x, yend = !!y, !!!segment_args)) +
    geom_point(aes(!!!dots))
}

a(d, cyl, n, color = factor(am), size = am)

‘Can’t use `!!!` at top level.’ 的意思是什么,如何解决这个问题?

b(d, cyl, n, color = factor(am), size = am)
#> Error: Can't use `!!!` at top level.

这是我的sessionInfo()

R version 3.5.2 (2018-12-20)
Platform: x86_64-apple-darwin16.7.0 (64-bit)
Running under: macOS Sierra 10.12.1

Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /usr/local/Cellar/openblas/0.3.5/lib/libopenblasp-r0.3.5.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

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

other attached packages:
[1] ggplot2_3.2.1

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.3       digest_0.6.18    withr_2.1.2     
 [4] assertthat_0.2.0 crayon_1.3.4     dplyr_0.8.3     
 [7] grid_3.5.2       R6_2.3.0         gtable_0.2.0    
[10] magrittr_1.5     scales_1.0.0     pillar_1.4.2    
[13] rlang_0.4.2      lazyeval_0.2.1   rstudioapi_0.10 
[16] labeling_0.3     tools_3.5.2      glue_1.3.0      
[19] purrr_0.3.3      munsell_0.5.0    compiler_3.5.2  
[22] pkgconfig_2.0.2  colorspace_1.4-0 tidyselect_0.2.5
[25] tibble_2.1.3
英文:

I am trying to create a function for creating lollipop plots using ggplot2. I would like to pass all argument within ... to aes() within geom_point(). However, I'd like to exclude the size argument from passing onto aes() within geom_segment() (for obvious reasons if you look at the ouput of a() below). Therefore I capture ... using rlang::enquos() instead of just passing it on as is. In function a() where I pass the dots to aes() within ggplot() this works without a problem. But in function b() I get the error Can&#39;t use &#39;!!!&#39; at top level.

I am stuck at this point and would appreciate any input to solve this issue.

library(ggplot2)
data(&quot;mtcars&quot;)

d &lt;- dplyr::count(mtcars, cyl, am)

a &lt;- function(data, x, y, ...) {
  x &lt;- rlang::enquo(x)
  y &lt;- rlang::enquo(y)
  dots &lt;- rlang::enquos(...)
  
  ggplot(data, aes(!!x, !!y, !!!dots)) +
    geom_segment(aes(y = 0, xend = !!x, yend = !!y)) +
    geom_point()
}

b &lt;- function(data, x, y, ...) {
  x &lt;- rlang::enquo(x)
  y &lt;- rlang::enquo(y)
  
  dots &lt;- rlang::enquos(...)
  segment_args &lt;- dots[names(dots) != &quot;size&quot;]

  ggplot(data, aes(!!x, !!y)) +
    geom_segment(aes(y = 0, xend = !!x, yend = !!y, !!!segment_args)) +
    geom_point(aes(!!!dots))
}

a(d, cyl, n, color = factor(am), size = am)

‘Can’t use `!!!` at top level.’ 的意思是什么,如何解决这个问题?


b(d, cyl, n, color = factor(am), size = am)
#&gt; Error: Can&#39;t use `!!!` at top level.

Here is my sessionInfo():

R version 3.5.2 (2018-12-20)
Platform: x86_64-apple-darwin16.7.0 (64-bit)
Running under: macOS Sierra 10.12.1

Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /usr/local/Cellar/openblas/0.3.5/lib/libopenblasp-r0.3.5.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

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

other attached packages:
[1] ggplot2_3.2.1

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.3       digest_0.6.18    withr_2.1.2     
 [4] assertthat_0.2.0 crayon_1.3.4     dplyr_0.8.3     
 [7] grid_3.5.2       R6_2.3.0         gtable_0.2.0    
[10] magrittr_1.5     scales_1.0.0     pillar_1.4.2    
[13] rlang_0.4.2      lazyeval_0.2.1   rstudioapi_0.10 
[16] labeling_0.3     tools_3.5.2      glue_1.3.0      
[19] purrr_0.3.3      munsell_0.5.0    compiler_3.5.2  
[22] pkgconfig_2.0.2  colorspace_1.4-0 tidyselect_0.2.5
[25] tibble_2.1.3

答案1

得分: 10

显然,这是aes()的一个已知问题,你可以在这里验证。一个解决方法如下:

b <- function(data, x, y, ...) {
  x <- rlang::enquo(x)
  y <- rlang::enquo(y)
  
  dots <- rlang::enquos(...)
  segment_args <- dots[names(dots) != "size"]
  
  ggplot(data, aes(!!x, !!y)) +
    geom_segment(aes(, y = 0, xend = !!x, yend = !!y, !!!segment_args)) +
    geom_point(aes(, , !!!dots))
}

请注意,在geom_segment()中有一个单逗号,而在geom_point()中有两个逗号。

英文:

Apparently this is a known issue of aes() as you can verify here. A workaround is this:

b &lt;- function(data, x, y, ...) {
  x &lt;- rlang::enquo(x)
  y &lt;- rlang::enquo(y)
  
  dots &lt;- rlang::enquos(...)
  segment_args &lt;- dots[names(dots) != &quot;size&quot;]
  
  ggplot(data, aes(!!x, !!y)) +
    geom_segment(aes(, y = 0, xend = !!x, yend = !!y, !!!segment_args)) +
    geom_point(aes(, , !!!dots))
}

Notice the single comma in geom_segment() and the double comma in geom_point().

答案2

得分: 4

如果你按照rlang的指示操作,你会得到一些进一步的细节:

> rlang::last_error()
<error>
message: 无法在顶层使用 `!!!`。
class:   `rlang_error`
backtrace:
 1. global::b(d, cyl, n, color = factor(am), size = am)
 4. ggplot2::aes(y = 0, xend = !!x, yend = !!y, !!!segment_args)
 5. rlang::enquos(x = x, y = y, ..., .ignore_empty = "all")
 6. rlang:::endots(...)
 7. rlang:::map(...)
 8. base::lapply(.x, .f, ...)
 9. rlang:::FUN(X[[i]], ...)
调用 `rlang::last_trace()` 以查看完整的回溯

然后

> rlang::last_trace()
    █
 1. └─global::b(d, cyl, n, color = factor(am), size = am)
 2.   ├─ggplot2::geom_segment(aes(y = 0, xend = !!x, yend = !!y, !!!segment_args))
 3.   │ └─ggplot2::layer(...)
 4.   └─ggplot2::aes(y = 0, xend = !!x, yend = !!y, !!!segment_args)
 5.     └─rlang::enquos(x = x, y = y, ..., .ignore_empty = "all")
 6.       └─rlang:::endots(...)
 7.         └─rlang:::map(...)
 8.           └─base::lapply(.x, .f, ...)
 9.             └─rlang:::FUN(X[[i]], ...)

所以看起来问题出在 !!!segment_args

编辑 1:只是随便尝试,但由于 segment_args 目前是一个单一的值,我尝试了以下操作,错误确实消失了:

b <- function(data, x, y, ...) {
  x <- rlang::enquo(x)
  y <- rlang::enquo(y)
  
  dots <- rlang::enquos(...)
  print(dots)
  segment_args <- dots[[setdiff(names(dots), "size")]]
  print(names(dots))

  print(segment_args)

  ggplot(data, aes(!!x, !!y)) +
    geom_segment(aes(y = 0, xend = !!x, yend = !!y, !!segment_args)) +
    geom_point(aes(!!!dots))
}

这只是确认问题在于使用 !!!,因为上面的代码现在为 aes(!!!dots) 提供了一个错误,这取决于示例中 segment_args 只有一个元素,但这可能为进一步的调查提供了一些线索。

英文:

If you follow the instructions of rlang, you get some further details:

&gt; rlang::last_error()
&lt;error&gt;
message: Can&#39;t use `!!!` at top level.
class:   `rlang_error`
backtrace:
 1. global::b(d, cyl, n, color = factor(am), size = am)
 4. ggplot2::aes(y = 0, xend = !!x, yend = !!y, !!!segment_args)
 5. rlang::enquos(x = x, y = y, ..., .ignore_empty = &quot;all&quot;)
 6. rlang:::endots(...)
 7. rlang:::map(...)
 8. base::lapply(.x, .f, ...)
 9. rlang:::FUN(X[[i]], ...)
Call `rlang::last_trace()` to see the full backtrace

Then

&gt; rlang::last_trace()
    █
 1. └─global::b(d, cyl, n, color = factor(am), size = am)
 2.   ├─ggplot2::geom_segment(aes(y = 0, xend = !!x, yend = !!y, !!!segment_args))
 3.   │ └─ggplot2::layer(...)
 4.   └─ggplot2::aes(y = 0, xend = !!x, yend = !!y, !!!segment_args)
 5.     └─rlang::enquos(x = x, y = y, ..., .ignore_empty = &quot;all&quot;)
 6.       └─rlang:::endots(...)
 7.         └─rlang:::map(...)
 8.           └─base::lapply(.x, .f, ...)
 9.             └─rlang:::FUN(X[[i]], ...)

So it appears the issue is with !!!segment_args

EDIT 1: just playing around but since segment_args is currently a single value, I tried the following and the error indeed disappears:

b &lt;- function(data, x, y, ...) {
  x &lt;- rlang::enquo(x)
  y &lt;- rlang::enquo(y)
  
  dots &lt;- rlang::enquos(...)
  print(dots)
  segment_args &lt;- dots[[setdiff(names(dots), &quot;size&quot;)]]
  print(names(dots))

  print(segment_args)

  ggplot(data, aes(!!x, !!y)) +
    geom_segment(aes(y = 0, xend = !!x, yend = !!y, !!segment_args)) +
    geom_point(aes(!!!dots))
}

This only confirms that the issue is with the usage of !!! since the above gives now an error for aes(!!!dots) instead and it depends on the fact that in the example there is only one element in segment_args, but it may give a handhold for further investigation

答案3

得分: 2

我认为你不需要再使用引号了。相反,你可以使用双大括号 {{ x }} 并保留点作为点 ...

以下内容可以正常工作,而且更容易理解:

b <- function(data, x, y, ...) {
  ggplot(data, aes( {{x}} , {{y}} )) +
    geom_segment(aes(y = 0, xend = {{x}}, yend = {{y}}, ...)) +
    geom_point(aes(...))
}
英文:

I don't think you need to quote / unquote anymore. Instead, you can use the double bracket {{ x }} and leave the dots as dots ...

The following works and is much easier to understand:

b &lt;- function(data, x, y, ...) {
  ggplot(data, aes( {{x}} , {{y}} )) +
    geom_segment(aes(y = 0, xend = {{x}}, yend = {{y}}, ...)) +
    geom_point(aes(...))
}

答案4

得分: 1

EDIT 2:

你可以重写 geom_segment 中的 size 值,这样你就不必在之前处理带引号的点了:

b <- function(data, x, y, ...) {
  x <- enquo(x)
  y <- enquo(y)
  dots <- enquos(...)

  ggplot(data, aes(!!x, !!y, !!!dots)) +
    geom_segment(aes(y = 0, xend = !!x, yend = !!y), size = 1) +
    geom_point(aes())
}

b(d, cyl, n)
b(d, cyl, n, color = factor(am))
b(d, cyl, n, color = factor(am), size = am)

EDIT:考虑到我关于提供显式参数的评论,我尝试了这个方法,似乎可以工作:

b <- function(data, x, y, color, size) {
  x <- enquo(x)
  y <- enquo(y)
  color <- enquo(color)
  size <- enquo(size)
  
  ggplot(data, aes(!!x, !!y, color = !!color)) +
    geom_segment(aes(y = 0, xend = !!x, yend = !!y)) +
    geom_point(aes(size=!!size))
}

鉴于你的示例,我建议以下解决方案,其中所需的变量是在函数内部创建的,而不是从 ... 传递,这样你就不必在调用 geom_xxx 时取消引用。

library(dplyr)
library(rlang)
library(ggplot2)

data("mtcars")
d <- dplyr::count(mtcars, cyl, am)

b <- function(data, x, y, aspect) {
  x <- enquo(x)
  y <- enquo(y)
  aspect <- enquo(aspect)
  
  data <- data %>% mutate(
    color = factor(!!aspect),
    size = !!aspect
  )
  
  ggplot(data, aes(!!x, !!y, color = color)) +
    geom_segment(aes(y = 0, xend = !!x, yend = !!y)) +
    geom_point(aes(size=size))
}

b(d, cyl, n, am)
英文:

EDIT 2 :

You could override the size value for geom_segment, so that you don't have to manipulate the quoted dots before :

b &lt;- function(data, x, y, ...) {
  x &lt;- enquo(x)
  y &lt;- enquo(y)
  dots &lt;- enquos(...)

  ggplot(data, aes(!!x, !!y, !!!dots)) +
    geom_segment(aes(y = 0, xend = !!x, yend = !!y), size = 1) +
    geom_point(aes())
}

b(d, cyl, n)
b(d, cyl, n, color = factor(am))
b(d, cyl, n, color = factor(am), size = am)

EDIT : given my commentary about providing explicit argument, I tried this and it seems to work

b &lt;- function(data, x, y, color, size) {
  x &lt;- enquo(x)
  y &lt;- enquo(y)
  color &lt;- enquo(color)
  size &lt;- enquo(size)
  
  ggplot(data, aes(!!x, !!y, color = !!color)) +
    geom_segment(aes(y = 0, xend = !!x, yend = !!y)) +
    geom_point(aes(size=!!size))
}

Given your example, I'd suggest the following workaround where the needed variables are created within the function rather than being passed from ..., so that you don't have to unquote within calls to geom_xxx.

library(dplyr)
library(rlang)
library(ggplot2)

data(&quot;mtcars&quot;)
d &lt;- dplyr::count(mtcars, cyl, am)

b &lt;- function(data, x, y, aspect) {
  x &lt;- enquo(x)
  y &lt;- enquo(y)
  aspect &lt;- enquo(aspect)
  
  data &lt;- data %&gt;% mutate(
    color = factor(!!aspect),
    size = !!aspect
  )
  
  ggplot(data, aes(!!x, !!y, color = color)) +
    geom_segment(aes(y = 0, xend = !!x, yend = !!y)) +
    geom_point(aes(size=size))
}

b(d, cyl, n, am)

huangapple
  • 本文由 发表于 2020年1月3日 20:07:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/59578369.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定