【问题标题】:R data.table breaks in exported functionsR data.table 在导出的函数中中断
【发布时间】:2014-06-08 18:06:26
【问题描述】:

我在让 data.table 在 roxygen2 导出函数中工作时遇到问题。

这是一个名为 foo.R(位于我的包的 R 目录中)的文件中的一个简单的假函数,它使用 data.table:

#' Data.table test function
#' @export
foo <- function() {
  m <- data.table(c1 = c(1,2,3))
  print(is.data.table(m))
  m[,sum(c1)]
}

如果我将此函数复制并粘贴到 R 中,则此函数可以正常工作:

> foo <- function() {
+   m <- data.table(c1 = c(1,2,3))
+   print(is.data.table(m))
+   m[,sum(c1)]
+ }
> foo()
[1] TRUE
[1] 6

但如果我只是简单地加载导出的函数,R 认为 data.table 是一个 data.frame 并中断:

> rm(foo)
> load_all()
Loading test_package
> foo
function() {
  m <- data.table(c1 = c(1,2,3))
  print(is.data.table(m))
  m[,sum(c1)]
}
<environment: namespace:test_package>
> foo()
[1] TRUE
Error in `[.data.frame`(x, i, j) : object 'c1' not found

怎么了?

更新

感谢@GSee 的帮助。看起来这实际上是一个 devtools 问题。查看下面的交互式命令行代码。

加载 test_package 库后,foo 运行正常:

> foo
function ()
{
    m <- data.table(c1 = c(1, 2, 3))
    print(is.data.table(m))
    m[, sum(c1)]
}
<environment: namespace:test_package>
> foo()
[1] TRUE
[1] 6

运行 load_all() 会破坏 foo:

> load_all()
Loading test_package
> foo()
[1] TRUE
Error in `[.data.frame`(x, i, j) : object 'c1' not found

不知何故 source('R/foo.R') 恢复了 foo 功能:

> source('R/foo.R')
> foo
function() {
  m <- data.table(c1 = c(1,2,3))
  print(is.data.table(m))
  m[,sum(c1)]
}
> foo()
[1] TRUE
[1] 6

以后对load_all() 的调用不会再次中断foo

> load_all()
Loading test_package
> foo
function() {
  m <- data.table(c1 = c(1,2,3))
  print(is.data.table(m))
  m[,sum(c1)]
}
> foo()
[1] TRUE
[1] 6

另外,我更新到 devtools 1.5 并尝试添加 .datatable.aware=TRUE,但这似乎没有任何作用。

【问题讨论】:

  • 你确定你的包中的foo是完全一样的(即你没有漏掉x[col3之间的逗号吗?
  • 它们是一样的。我将文件 foo.R(如上所示)中的函数复制并粘贴到 R 命令行中。
  • 在命令行中检查yourpackagename::foo 以确保它们确实相同。仅仅因为您在编辑器上看到的文件看起来像 X 并不意味着在您的 R 会话中加载的包中的函数是相同的。
  • 如果您构建和加载包,或者仅当您使用load_all() 时,您是否有此问题?
  • 尝试将.datatable.aware=TRUE 放在R 文件中的一行,看看load_all() 是否有效。我认为这个LINK 可能是相关的——更新devtools 可能会解决问题。

标签: r data.table devtools


【解决方案1】:

正如@GSee 指出的(在 cmets 下),问题似乎仍然是this issue

为了判断一个包是否data.table 感知data.table 调用函数cedta(),即:

> data.table:::cedta
function (n = 2L) 
{
    te = topenv(parent.frame(n))
    if (!isNamespace(te)) 
        return(TRUE)
    nsname = getNamespaceName(te)
    ans = nsname == "data.table" || "data.table" %chin% names(getNamespaceImports(te)) || 
        "data.table" %chin% tryCatch(get(".Depends", paste("package", 
            nsname, sep = ":"), inherits = FALSE), error = function(e) NULL) || 
        (nsname == "utils" && exists("debugger.look", parent.frame(n + 
            1L))) || nsname %chin% cedta.override || identical(TRUE, 
        tryCatch(get(".datatable.aware", asNamespace(nsname), 
            inherits = FALSE), error = function(e) NULL))
    if (!ans && getOption("datatable.verbose")) 
        cat("cedta decided '", nsname, "' wasn't data.table aware\n", 
            sep = "")
    ans
}
<bytecode: 0x7ff67b9ca190>
<environment: namespace:data.table>

这里的相关检查是:

"data.table" %chin% get(".Depends", paste("package", nsname, sep=":"), inherits=FALSE)

当一个包依赖data.table,上面的命令应该返回TRUE——也就是说,如果你通过R CMD INSTALL安装了这个包,然后又加载了这个包。这是因为,当您加载包时,R 默认情况下也会在命名空间中创建一个“.Depends”变量。如果你这样做了:

ls("package:test", all=TRUE)
# [1] ".Depends" "foo"     

但是,当您执行devtools:::load_all() 时,似乎没有设置此变量。

# new session + set path to package's dir
devtools:::load_all()
ls("package:test", all=TRUE)
# [1] "foo"

所以,cedta() 并不知道这个包确实依赖 data.table。但是,当您手动设置.datatable.aware=TRUE 时,该行:

identical(TRUE, get(".datatable.aware", asNamespace(nsname), inherits = FALSE))

被执行,这将返回 TRUE,从而解决了这个问题。但是devtools 没有将.Depends 变量放在包的命名空间中的事实仍然存在。

总而言之,这真的不是data.table 的问题。

【讨论】:

    猜你喜欢
    • 2017-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-04
    • 1970-01-01
    • 2015-12-30
    相关资源
    最近更新 更多