dplyr中的标准计算:将给定的变量汇总为字符串

dplyr中的标准计算:将给定的变量汇总为字符串,r,dplyr,R,Dplyr,2020年7月更新: dplyr 1.0几乎改变了关于这个问题以及所有答案的一切。请参见此处的dplyr编程示意图: 当列的标识符存储为字符向量时,引用列的新方法是使用rlang中的.data代词,然后使用base R中的子集 library(dplyr) key <- "v3" val <- "v2" drp <- "v1" df <- tibble(v1 = 1:5, v2 = 6:10, v3 = c(

2020年7月更新:

dplyr 1.0几乎改变了关于这个问题以及所有答案的一切。请参见此处的dplyr编程示意图:

当列的标识符存储为字符向量时,引用列的新方法是使用rlang中的.data代词,然后使用base 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

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

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
select的NSE版本工作正常,因为它可以匹配字符串。group_by的SE版本运行良好,因为它现在可以接受变量作为参数并对其求值。然而,在dplyr函数中使用基R函数时,我还没有找到一种方法来实现类似的结果

不起作用的事情:

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

我已经签出,但到目前为止,没有一个建议的解决方案对我有效。

请注意,这个答案不适用于dplyr>=0.7.0,而是适用于以前的版本

有一种新的非标准评估方法称为tidyeval。它在vignetteprogramming中有详细描述

这一点很有帮助。检查“混合常量和变量”一节,您会发现可以使用lazyeval软件包中的函数interp,如果您有一个给出变量名称的字符串,请使用as.name:

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
向.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代替字符串。我们用另一个函数撤消报价!!。更多关于这些。在完全发布之前,您将需要

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

随着rlang包的发布和dplyr的0.7.0更新,这一点现在相当简单

当您想使用字符串(例如v1)作为变量名时,您只需:

使用rlang包中的sym将字符串转换为符号 在函数调用中,写入!!在符号前面 例如,您可以执行以下操作:

my_var <- "Sepal.Length"
my_sym <- sym(my_var)
summarize(iris, Mean = mean(!!my_sym))
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_var <- "Sepal.Length"
my_sym <- sym(my_var)
summarize(iris, Mean = mean(!!my_sym))
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))
替代语法

随着rlang版本0.4.0的发布,您可以使用以下语法:

my_var <- "Sepal.Length"
my_sym <- sym(my_var)
summarize(iris, Mean = mean({{ my_sym }}))
而不是写作!!我的{u-sym},你可以写{{我的{u-sym}}。这样做的优点是可以说更清晰,但缺点是必须先将字符串转换为符号,然后再将其放入括号中。例如,你可以写!!但是你不能写{{symmy\u var}

其他细节

所有解释sym和!!工作,这些似乎是最容易接近的:

dplyr 1.0几乎改变了关于这个问题以及所有答案的一切。请参见此处的dplyr编程示意图:

当列的标识符存储为字符向量时,引用列的新方法是使用rlang中的.data代词,然后使用base 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

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检查有关未定义全局变量的注释。

我之前阅读了该小插曲,但在测试中误用了interp。非常感谢。尽管有这个答案和小插曲,我仍然无法让它工作。我没有MWE。我执行了一个summary summarseng,median=mediantotal_length,这是因为“total_length”是我的data.frame“ng”中的一个列名。现在我想循环使用存储在“features”列表中的列名,在这个示例中,它将是summarseng,median=medianfeatures[8]。但这在摘要中给出了字符串“features[8]”。interp summarseng,median=interp~medianvar,var=features[8]的方法给出了一个错误:“错误:不是向量”。怎么办???阅读已经从根本上改变了我编程函数参数的方式。要提供不带引号的变量名作为函数参数,请参见使用enquo和!!在该vignette中的my_summary函数中。如果v3和v2是作为函数输入的字符向量,如何执行此操作?如果有多个变量怎么办?在一个相关的问题中。