【问题标题】:R object not found if defined within a function when using data.table dplyr如果在使用 data.table dplyr 时在函数中定义,则找不到 R 对象
【发布时间】:2015-01-05 05:28:16
【问题描述】:

注意 所描述的行为已在 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 的错误?

【问题讨论】:

  • dplyr 0.4.0, data.table 1.9.4 不会抛出错误。
  • 我得到了最新的 CRAN 版本,它是 0.3.0.2。
  • 如果您将括号包裹在 summarise 周围,则将错误更改为 .data not found in summarise_
  • 我认为由于非标准评估,bad 在您要评估时已超出范围。尝试在那里拨打force
  • @RichardScriven 你能举个例子吗?我真的不明白你的意思。

标签: r data.table dplyr


【解决方案1】:

似乎这是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 内部的 summarize 调用中获取此函数@,这似乎不是成为正确的父框架。如果你改为运行

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) 

似乎有效。所以真正需要设置正确环境的是dplyr。棘手的部分是,如果您直接调用summarizesummarize_,这似乎会有所不同。当summarise() 通过eval() 调用summarise_ 以拥有相同的parent.frame 时,也许summarise() 可以改变环境。但我可能会将其作为错误报告提交,并让 Hadley 决定如何修复它。类似的东西

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

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

使用data.table_1.9.2dplyr_0.3.0.2 测试

【讨论】:

  • 取决于您所说的“更好”的含义,但函数中的&lt;&lt;- 会将变量放入将要签入的范围内。确保每次只使用一个命名的临时全局变量函数(即不要将它们都称为tempvar,否则您可能会遇到一些难以调试的问题)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-13
  • 1970-01-01
  • 2020-04-04
相关资源
最近更新 更多