我想在一个内部引用一个未知的列名summarise.dplyr 0.3允许使用变量引用列名称中引入的标准评估函数,但是当您base在例如a中调用R函数时,这似乎不起作用summarise.
library(dplyr)
key <- "v3"
val <- "v2"
drp <- "v1"
df <- data_frame(v1 = 1:5, v2 = 6:10, v3 = c(rep("A", 3), rep("B", 2)))
df看起来像这样:
> df
Source: local data frame [5 x 3]
  v1 v2 v3
1  1  6  A
2  2  7  A
3  3  8  A
4  4  9  B
5  5 10  B
我想放弃v1,按v3分组,并为每个组加上v2:
df %>% select(-matches(drp)) %>% group_by_(key) %>% summarise_(sum(val, na.rm = TRUE))
Error in sum(val, na.rm = TRUE) : invalid 'type' (character) of argument
NSE版本select()工作正常,因为它可以匹配字符串.SE版本的group_by()工作正常,因为它现在可以接受变量作为参数并对其进行评估.但是,在函数内部使用基本R函数时,我还没有找到实现类似结果的dplyr方法.
不起作用的事情:
df %>% group_by_(key) %>% summarise_(sum(get(val), na.rm = TRUE))
Error in get(val) : object 'v2' not found
df %>% group_by_(key) %>% summarise_(sum(eval(as.symbol(val)), na.rm = TRUE))
Error in eval(expr, envir, enclos) : object 'v2' not found
Hen*_*rik 53
请注意,这个答案并不能适用dplyr >= 0.7.0,但以前的版本.
[
dplyr 0.7.0]有一种称为tidyeval的非标准评估(NSE)的新方法.详细描述如下vignette("programming").
dplyr关于非标准评估的小插曲在这里很有帮助.检查"混合常量和变量"部分,您会发现可以使用interp包中的函数lazyeval,并且" as.name如果您有一个给出变量名称的字符串,则为[u] se ":
library(lazyeval)
df %>%
  select(-matches(drp)) %>%
  group_by_(key) %>%
  summarise_(sum_val = interp(~sum(var, na.rm = TRUE), var = as.name(val)))
#   v3 sum_val
# 1  A      21
# 2  B      19
bsc*_*idr 28
随着rlang软件包的发布和dplyr的0.7.0更新,现在相当简单.
如果要将字符串(例如"v1")用作变量名,只需:
sym()rlang包将字符串转换为符号!!符号前面例如,您将执行以下操作:
my_var <- "Sepal.Length"
my_sym <- sym(my_var)
summarize(iris, Mean = mean(!!my_sym))
更紧凑的是,您可以结合将字符串转换为符号的步骤,sym()并!!在编写函数调用时为其添加前缀.
例如,你可以写:
my_var <- "Sepal.Length"
summarize(iris, mean(!!sym(my_var)))
要返回原始示例,您可以执行以下操作:
library(rlang)
key <- "v3"
val <- "v2"
drp <- "v1"
df <- data_frame(v1 = 1:5, v2 = 6:10, v3 = c(rep("A", 3), rep("B", 2)))
df %>% 
  # NOTE: we don't have to do anything to `drp`
  # since the matches() function expects a character string
  select(-matches(drp)) %>% 
  group_by(!!sym(key)) %>% 
  summarise(sum(!!sym(val), na.rm = TRUE))
额外细节
在解释如何使用!!my_sym和{{ my_sym }}工作的所有官方文档中,这些似乎是最容易访问的:
将.dots参数传递给构造字符串的字符串列表paste,sprintf或者使用包gsubfn中的字符串插值来fn$list代替list我们这样做:
library(gsubfn)
df %>% 
   group_by_(key) %>% 
   summarise_(.dots = fn$list(mean = "mean($val)", sd = "sd($val)"))
赠送:
Source: local data frame [2 x 3]
  v3 mean        sd
1  A  7.0 1.0000000
2  B  9.5 0.7071068
新的dplyr更新:
dplyr的新功能可以帮助解决这个问题.我们使用quosures而不是需要非标准评估的变量的字符串quo().我们用另一个函数撤消引用!!.有关这些的更多信息,请参阅此插图.在完整版本发布之前,您将需要开发人员的dplyr版本.
library(dplyr) #0.5.0.9004+
key <- quo(v3)
val <- quo(v2)
drp <- "v1"
df <- data_frame(v1 = 1:5, v2 = 6:10, v3 = c(rep("A", 3), rep("B", 2)))
df %>% select(-matches("v1")) %>% 
  group_by(!!key) %>% 
  summarise(sum(!!val, na.rm = TRUE))
# # A tibble: 2 × 2
#      v3 `sum(v2, na.rm = TRUE)`
#   <chr>                   <int>
# 1     A                      21
# 2     B                      19
dplyr1.0 几乎改变了关于这个问题的所有内容以及所有答案。在dplyr此处查看编程小插图:
https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html
当列的标识符存储为字符向量时,引用列的新方法是使用.data代词 from rlang,然后像在基本 R 中那样使用子集。
library(dplyr)
key <- "v3"
val <- "v2"
drp <- "v1"
df <- tibble(v1 = 1:5, v2 = 6:10, v3 = c(rep("A", 3), rep("B", 2)))
df %>% 
    select(-matches(drp)) %>% 
    group_by(.data[[key]]) %>% 
    summarise(total = sum(.data[[val]], na.rm = TRUE))
#> `summarise()` ungrouping output (override with `.groups` argument)
#> # A tibble: 2 x 2
#>   v3    total
#>   <chr> <int>
#> 1 A        21
#> 2 B        19
如果您的代码在包函数中,您@importFrom rlang .data可以避免 R 检查有关未定义全局变量的注释。
| 归档时间: | 
 | 
| 查看次数: | 22063 次 | 
| 最近记录: |