【问题标题】:Can get non-standard evaluation to work in dplyr for filter_ and count_ but not distinct_可以让非标准评估在 dplyr 中为 filter_ 和 count_ 工作,但不是 distinct_
【发布时间】:2016-12-16 17:09:21
【问题描述】:

我正在尝试编写一个使用 dplyr 来计算 z 的所有唯一值的函数。当我将变量实际命名为 z 时,我的函数工作正常。但是,如果变量名为 x,我会收到错误消息(如下代码)。

test.data<-data.frame(y=c(1:10),
                  x=c(letters[1:10]))
test.data$x<-as.character(test.data$x)
obsfunction<-function(z,y,data){
filter_(data,
          !is.na(deparse(substitute(y))))%>%
    distinct_(., deparse(substitute(z)))%>% #the line that breaks it
    count_(.)
}
obsfunction(z=x,y,data=test.data)

所以,上面的代码不起作用并给出了这个错误:

 >Error in eval(substitute(expr), envir, enclos) : unknown column 'z'

在函数中将 z 更改为 x(或将 x 重命名为 z)使其工作,但我不想重命名所有内容,特别是考虑到 y 使用不同的名称。

我已经按照vignettethis questionthis question 尝试了lazyeval::interp 和quote()。

distinct_(lazyeval::interp(as.name(z)))%>%
>Error in as.name(z) : object 'x' not found 

distinct_(quote(z))%>%
>Error in eval(substitute(expr), envir, enclos) : unknown column 'z' 

我错过了什么?如何让 z 接受 x 作为列名?

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    作为 dplyr 标准评估理解字符串,我尝试了以下代码并使用额外的测试数据,它似乎有效。我先提取变量名,然后用字符串构造表达式:

    test.data<-data.frame(y=c(1:10),
                          x=c(letters[1:10]))
    test.data$x<-as.character(test.data$x)
    
    f <- function(z, y, data){
        z <- deparse(substitute(z))
        y <- deparse(substitute(y))
        res <- data %>% filter_(
            paste('!is.na(', y, ')', sep = '')) %>%
            distinct_(z) %>%
            count_(.)
    }
    
    
    x <- f(z = x, y, test.data)
    # # A tibble: 1 × 1
    #       n
    # <int>
    # 1    10
    
    
    
    test.data <- data.frame(
        y=c(1:4, NA, NA, 7:10),
        x=c(letters[c(1:8, 8, 8)]),
        stringsAsFactors = F)
    
    x <- f(z = x, y, test.data)
    # # A tibble: 1 × 1
    #       n
    # <int>
    # 1     6
    

    【讨论】:

      【解决方案2】:

      您可以使用match.call 捕获函数参数并将其转换为字符,然后再传递给dplyr SE 函数:

      obsfunction<-function(z, y, data){
          cl = match.call()
          y = as.character(cl['y'])
          z = as.character(cl['z'])
      
          data %>% filter_(paste('!is.na(', y, ')', sep = '')) %>%
                   distinct_(z) %>%
                   count_(.)
      }
      
      obsfunction(z = x, y = y, data = test.data)
      
      # A tibble: 1 × 1
      #      n
      #  <int>
      #1    10
      
      obsfunction(x, y, test.data)
      
      # A tibble: 1 × 1
      #      n
      #  <int>
      #1    10
      

      【讨论】:

      • 嗨。我注意到您的答案为我的答案中的额外test.data 给出了 8,而我的解决方案给出了 6。我错过了什么吗?
      • @mt1022 是的。我刚刚注意到filter_ 功能无法正常工作,您的paste() 方法是要走的路。感谢您的通知!
      【解决方案3】:

      另一个lazyeval/dplyr 变体,其中变量作为公式传递,f_interp 用传递给它的公式替换uq(x),类似于deparse(substitute(x))

      library(dplyr)
      library(lazyeval)
      
      test.data<-data.frame(y=c(1:10),
                        x=c(letters[1:10]))
      test.data$x<-as.character(test.data$x)
      
      
      obsfunction<-function(z, y, data){
        data %>% filter_(f_interp(~!is.na(uq(y)))) %>%
          distinct_(f_interp(~uq(z))) %>% count()
      }
      
      obsfunction(z=~x,~y,data=test.data)
      
       #A tibble: 1 × 1
       #     n
       #  <int>
       #1    10
      
      test.data.NA <- data.frame(
        y=c(1:4, NA, NA, 7:10),
        x=c(letters[c(1:8, 8, 8)]),
        stringsAsFactors = FALSE)
      
      
      obsfunction(z=~x,~y,data=test.data.NA)
       # # A tibble: 1 × 1
       #        n
       #      <int>
       # 1      6
      

      【讨论】:

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