如何使用rlang !!!运算符定义环绕ggplot调用的函数? (错误:不能在顶层使用`!!!`)

时间:2018-12-30 17:00:58

标签: r ggplot2 rlang

上下文

读取小插图Programming with dplyr时,我尝试使用List<String>...运算符来实现一个函数,该函数将环绕ggplot函数,并接受任意数量的参数来定义哪个参数数据框中的变量应映射到每种美感。

我的目标

我想定义一个函数!!!使得

  1. plot_points2()等同于plot_points2(df, x = x, y = y, color = z)
  2. df %>% ggplot( mapping = aes(x = x, y = y, color = z) ) + geom_point(alpha = 0.1)等同于plot_points2(df, x = x, y = z, color = y)
  3. df %>% ggplot( mapping = aes(x = x, y = z, color = y) ) + geom_point(alpha = 0.1)等同于plot_points2(df, x = x, y = z)

失败了

软件包

df %>% ggplot( mapping = aes(x = x, y = z) ) + geom_point(alpha = 0.1)

精简示例数据集

require(tidyverse)
require(rlang)

我的尝试

df <- tibble(g1= sample(x = c(1,2,3), replace = T, size = 10000),
             g2= sample(x = c("a","b","c"), replace = T, size = 10000),
             x = rnorm(10000, 50, 10),
             y = rnorm(10000, 0, 20) + x*2,
             z = rnorm(10000, 10, 5))
df

错误

plot_points2 <- function(d, ...){
  args <- quos(...)
  print(args)
  ggplot(data = d, mapping = aes(!!!args)) + geom_point(alpha = 0.1)
}
plot_points2(df, x = x, y = y, color = z)

为什么我认为它应该起作用

我认为我要完成的操作与vignette中的示例没有太大不同,该示例使用这些运算符来制作一个环绕Error: Can't use `!!!` at top level Call `rlang::last_error()` to see a backtrace 的函数,并传递定义分组的多个参数变量(实际上,我能够实现一个功能,对上面我作为示例发布的示例数据集执行此功能),但是后者以某种方式起作用而前者却不起作用:

这有效

mutate()

这不是

add_dif_to_group_mean <- function(df, ...) {
  groups <- quos(...)
  df %>% group_by(!!!groups) %>% mutate(x_dif = x-mean(x), 
                                        y_dif = y-mean(y), 
                                        z_dif = z-mean(z))
}

df %>% add_dif_to_group_mean(g1)
df %>% add_dif_to_group_mean(g1, g2)

我还读到,问题可能与plot_points2 <- function(d, ...){ args <- quos(...) print(args) ggplot(data = d, mapping = aes(!!!args)) + geom_point(alpha = 0.1) } plot_points2(df, x = x, y = y, color = z) 仅在打印打印图时才被评估有关,但在这种情况下,我认为手动使用aes()拆箱会产生相同的错误但不是

!!

实际上,如果绘制3个变量,则最后一个示例可以正常工作,但不允许绘制多个3个变量以外的变量

例如:plot_points2b <- function(d, ...){ args <- quos(...) print(args) ggplot(data = d, mapping = aes(x = !!args[[1]], y = !!args[[2]], color = !!args[[3]])) + geom_point(alpha = 0.1) } plot_points2b(df, x = x, y = y, color = z) 不等于

plot_points2b(df, x = x, y = z)

相反,它会引发错误:

df %>% ggplot( mapping = aes(x = x, y = z) ) + geom_point(alpha = 0.1)

有人知道我在这里想念什么概念吗?预先谢谢你!

2 个答案:

答案 0 :(得分:1)

您的特定用例是?aes中的一个示例。 aes自动引用其参数。可以简单地直接传递点。试试:

plot_points3 <- function(d, ...){
  print(aes(...))
  ggplot(d, aes(...)) + geom_point(alpha = 0.1)
}
plot_points3(df, x = x, y = y, color = z)

这很好地打印:

Aesthetic mapping: 
* `x`      -> `x`
* `y`      -> `y`
* `colour` -> `z`

并生成所需的图。

答案 1 :(得分:0)

如我的评论中所述,我认为您的环境中可能已经有x和y,这就是为什么某些代码可以正常工作的原因。我不确定您要实现的目标,但我认为您为使代码无错误运行而付出了太多努力。

例如:

plot_points <- function(d, ...){    
    ggplot(data = d, mapping = aes(x = x, y = y)) + 
     geom_point(alpha = 0.1)
}
plot_points (df, x, y)

将使您的绘图无任何理由增加!!!enquo()的开销和复杂性。

您也在这里,这条更简单的代码可以正常工作:

add_dif_to_group_mean <- function(., ...) {

    df %>% group_by(g1) %>% mutate(x_dif = x-mean(x), 
                                          y_dif = y-mean(y), 
                                          z_dif = z-mean(z))
}

df %>% add_dif_to_group_mean(g1)

类似:

plot_points2 <- function(d, ...){
    ggplot(data = d, mapping = aes(x=x, y=y, color=z)) + 
    geom_point(alpha = 0.1)
}
plot_points2(df, x = x, y = y, color = z)
据我所知

工作正常。

所以我知道也许您正在研究本书中的示例,这很棒。但是我认为某个地方存在一个遗漏的问题,因此您必须在现实世界中执行所有其他工作。例如,也许您想传递诸如"x""y"之类的字符串而不是xy