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

时间:2015-01-05 05:28:16

标签: r data.table dplyr

注意已在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) 

当我运行以上内容时,我得到了

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

然而,明确界定了不好的范围。

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

  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

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

1 个答案:

答案 0 :(得分: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>

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

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)

所以重要的一行是

env <- dt_env(.data, parent.frame())

之一。这里设置环境路径,指定在调用中查找所有变量的位置。这里只是使用parent.frame来查看调用函数的位置,但是因为你实际上跳过了几个箍来从summarize f()内的env <- dt_env(.data, parent.frame(2)) 调用中获取此函数,所以似乎是正确的父框架。如果,而不是你运行

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) 因为这个

summarize

似乎有效。因此,需要建立正确的环境才是真正的dplyr。如果您直接致电summarize_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()) } 来拥有相同的parent.frame时,data.table_1.9.2可能会改变环境。但我可能会将此作为错误报告提交,让Hadley决定如何修复它。像

这样的东西
dplyr_0.3.0.2

将是一种“传统”的方式。不确定lazyeval包是否有更好的方法来执行此操作。

使用{{1}}和{{1}}

进行测试