如果在使用data.table dplyr时在函数内定义,则找不到R对象

xia*_*dai 6 r dplyr data.table

注意所描述的行为已在dplyr的开发版本中得到修复.你可以使用devtools :: install_github("hadley/dplyr")安装dplyr

请看这个最小的例子; 我使用的是dplyr v0.3.0.2和data.table v1.9.4

library(dplyr)
library(data.table)
f <- function(x, y, bad) { 
  z <- data.table(x,y, key = "x")    
  z2 <- z %>% group_by(x) %>% summarise(sum.bad = sum(y == bad))
  z2
}

f(rnorm(100), rnorm(100) < 0, bad = FALSE) 
Run Code Online (Sandbox Code Playgroud)

当我跑上面的时候,我得到了

Error in `[.data.table`(dt, , list(sum.bad = sum(y == bad)), by = vars) : 
  object 'bad' not found
Run Code Online (Sandbox Code Playgroud)

然而,坏的是明确定义和范围.

如果我只是在函数之外运行它可以工作

  x <- rnorm(100)
  y <- rnorm(100) <0
  bad <- FALSE
  z <- data.table(x,y, key = "x")

  z2 <- z %>% group_by(x) %>% summarise(sum.bad = sum(y == bad))
  z2
Run Code Online (Sandbox Code Playgroud)

这是什么问题?这是data.table还是dplyr的错误?

MrF*_*ick 4

看来这是如何dplyr为 data.table 调用设置环境的问题。问题出现在dplyr:::summarise_.grouped_dt函数中。目前看起来像

function (.data, ..., .dots) 
{
    dots <- lazyeval::all_dots(.dots, ..., all_named = TRUE)
    for (i in seq_along(dots)) {
        if (identical(dots[[i]]$expr, quote(n()))) {
            dots[[i]]$expr <- quote(.N)
        }
    }
    list_call <- lazyeval::make_call(quote(list), dots)
    call <- substitute(dt[, list_call, by = vars], list(list_call = list_call$expr))
    env <- dt_env(.data, parent.frame())
    out <- eval(call, env)
    grouped_dt(out, drop_last(groups(.data)), copy = FALSE)
}
<environment: namespace:dplyr>
Run Code Online (Sandbox Code Playgroud)

如果我们调试该函数并查看调用时的跟踪,我们会看到

where 1: summarise_.grouped_dt(.data, .dots = lazyeval::lazy_dots(...))
where 2: summarise_(.data, .dots = lazyeval::lazy_dots(...))
where 3: summarise(., sum.bad = sum(y == bad))
where 4: function_list[[k]](value)
where 5: withVisible(function_list[[k]](value))
where 6: freduce(value, `_function_list`)
where 7: `_fseq`(`_lhs`)
where 8: eval(expr, envir, enclos)
where 9: eval(quote(`_fseq`(`_lhs`)), env, env)
where 10: withVisible(eval(quote(`_fseq`(`_lhs`)), env, env))
where 11 at #3: z %>% group_by(x) %>% summarise(sum.bad = sum(y == bad))
where 12: f(rnorm(100), rnorm(100) < 0, bad = FALSE)
Run Code Online (Sandbox Code Playgroud)

所以重要的一行是

env <- dt_env(.data, parent.frame())
Run Code Online (Sandbox Code Playgroud)

一。这里设置环境路径,指定在调用中查找所有变量的位置。这里它只是使用parent.frame来查找函数被调用的位置,但是由于您实际上跳过了一些环节才能从summarize内部调用到达这个函数f(),所以这似乎不是正确的父框架。如果你运行

env <- dt_env(.data, parent.frame(2))
Run Code Online (Sandbox Code Playgroud)

在调试模式下,这似乎实际上获得了正确的父框架。所以我认为问题是从summarize()到 的跳转summarize_(),因为这是

ff <- function(x, y, bad) { 
  z <- data.table(x,y, key = "x")    
  z2 <- z %>% group_by(x) %>% summarise_(.dots=list(sum.bad = quote(sum(y == bad))))
  z2
}

ff(rnorm(100), rnorm(100) < 0, bad = FALSE) 
Run Code Online (Sandbox Code Playgroud)

似乎有效。所以确实是 dplyr 需要设置正确的环境。summarize棘手的部分是,如果您致电或直接致电,情况似乎有所不同summarize_。当它通过调用具有相同的parent.frame时,也许summarise()可以改变环境。但我可能会将其作为错误报告提交,并让哈德利决定如何修复它。就像是summarise_eval()

summarise <- function(.data, ...) {
  call <- match.call()
  call <- as.call(c(as.list(call)[1:2], list(.dots=as.list(call)[-(1:2)])))
  call[[1]] <- quote(summarise_)
  eval(call, envir=parent.frame())
}
Run Code Online (Sandbox Code Playgroud)

将是一种“传统”方式。不确定lazyeval包是否有更好的方法来做到这一点。

测试data.table_1.9.2dplyr_0.3.0.2