什么是“不能在顶层使用 `!!!`。”是什么意思,如何解决呢?
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)
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
我认为您不再需要引用/取消引用。相反,您可以使用双括号 {{ x }}
并将点保留为点 ...
以下内容有效并且更容易理解:
b <- function(data, x, y, ...) {
ggplot(data, aes( {{x}} , {{y}} )) +
geom_segment(aes(y = 0, xend = {{x}}, yend = {{y}}, ...)) +
geom_point(aes(...))
}
如果您按照 rlang 的说明进行操作,您将获得更多详细信息:
> rlang::last_error()
<error>
message: Can'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 = "all")
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
然后
> 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 中只有一个元素这一事实,但它可能为进一步调查
提供了依据
编辑 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)
编辑:根据我关于提供明确论证的评论,我尝试了这个并且它似乎有效
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))
}
根据您的示例,我建议采用以下变通方法,其中需要的变量是在函数内创建的,而不是从 ...
传递的,这样您就不必在对 [= 的调用中取消引用16=].
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)
显然这是 aes()
的一个已知问题,您可以验证 here。解决方法是:
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()
中的双逗号。
我正在尝试使用 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)
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
我认为您不再需要引用/取消引用。相反,您可以使用双括号 {{ x }}
并将点保留为点 ...
以下内容有效并且更容易理解:
b <- function(data, x, y, ...) {
ggplot(data, aes( {{x}} , {{y}} )) +
geom_segment(aes(y = 0, xend = {{x}}, yend = {{y}}, ...)) +
geom_point(aes(...))
}
如果您按照 rlang 的说明进行操作,您将获得更多详细信息:
> rlang::last_error()
<error>
message: Can'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 = "all")
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
然后
> 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 中只有一个元素这一事实,但它可能为进一步调查
编辑 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)
编辑:根据我关于提供明确论证的评论,我尝试了这个并且它似乎有效
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))
}
根据您的示例,我建议采用以下变通方法,其中需要的变量是在函数内创建的,而不是从 ...
传递的,这样您就不必在对 [= 的调用中取消引用16=].
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)
显然这是 aes()
的一个已知问题,您可以验证 here。解决方法是:
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()
中的双逗号。