【问题标题】:standard evaluation in dplyr: summarise a variable given as a character stringdplyr 中的标准评估:总结作为字符串给出的变量
【发布时间】:2014-12-30 16:37:20
【问题描述】:

2020 年 7 月更新:

dplyr 1.0 几乎改变了这个问题的所有内容以及所有答案。在此处查看dplyr 编程小插曲:

https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html

当列的标识符存储为字符向量时,引用列的新方法是使用rlang 中的.data 代词,然后像在基本 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 检查未定义全局变量的注释。

原始问题:

我想引用 summarise 中的未知列名。 dplyr 0.3 中引入的标准评估函数允许使用变量引用列名,但是当您在例如内部调用 base 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

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

我已经查看了severalrelatedquestions,但到目前为止,没有一个建议的解决方案对我有用。

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    请注意,此答案不适用于dplyr &gt;= 0.7.0,但适用于以前的版本。

    [dplyr 0.7.0] 有一种新的非标准评估 (NSE) 方法,称为 tidyeval。在vignette("programming")中有详细描述。


    dplyr vignette on non-standard evalutation 在这里很有帮助。检查“混合常量和变量”部分,您会发现可以使用包lazyeval 中的函数interp,并且“[u]se 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
    

    【讨论】:

    • 我之前通读了这个小插图,但在我的测试中误用了interp。谢谢!
    • 尽管有这个答案和小插图,我仍然无法让它工作。我没有 MWE。我执行了一个汇总summarise(ng,median=median(total_length) ),这是因为“total_length”是我的data.frame“ng”中的一个列名。现在我想遍历存储在“特征”列表中的列名,在这个例子中是summarise(ng,median=median(features[8]) )。但这在摘要中给出了字符串'features[8]'。 interp summarise(ng,median= interp(~median(var), var = features[8]) ) 的方法给出错误:'错误:不是向量'。怎么办???
    【解决方案2】:

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

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

    1. 使用 rlang 包中的 sym() 将字符串转换为符号
    2. 在你的函数调用中,在符号前面写上!!

    例如,您将执行以下操作:

    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))
    


    替代语法

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

    my_var <- "Sepal.Length"
    my_sym <- sym(my_var)
    summarize(iris, Mean = mean({{ my_sym }}))
    

    你可以写{{ my_sym }},而不是写!!my_sym。这具有可以说更清晰的优点,但缺点是您必须在将字符串放入括号之前将其转换为符号。例如,你可以写!!sym(my_var),但你不能写{{sym(my_var)}}

    其他详情

    在所有解释 sym() 和 !! 用法的官方文档中,这些似乎是最容易理解的:

    1. dplyr vignette: Programming with dplyr

    2. The section of Hadley Wickham's book 'Advanced R' on metaprogramming

    【讨论】:

    【解决方案3】:

    向.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
    

    【讨论】:

      【解决方案4】:

      新的 dplyr 更新:

      dplyr 的新功能可以帮助解决这个问题。我们使用引号quo(),而不是需要非标准评估的变量的字符串。我们使用另一个函数!! 撤消引用。有关这些see this vignette 的更多信息。在完整版本发布之前,您将需要 developer's version of 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
      

      【讨论】:

      • 阅读programming with dplyr vignette 从根本上改变了我编写函数参数的方式。要提供不带引号的变量名称作为函数参数,另请参阅在该小插图中的 my_summarise 函数中使用 enquo() 和 !! 的示例。
      • 如果v3和v2是作为函数输入的字符向量,怎么办?
      【解决方案5】:

      dplyr 1.0 几乎改变了这个问题的所有内容以及所有答案。在此处查看dplyr 编程小插曲:

      https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html

      当列的标识符存储为字符向量时,引用列的新方法是使用来自rlang 的.data 代词,然后像在基础 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 检查未定义全局变量的注释。

      【讨论】:

        猜你喜欢
        • 2020-09-17
        • 2017-04-17
        • 1970-01-01
        • 2017-10-27
        • 2015-11-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多