在我的自己的包中使用data.table包

89

我想在自己的软件包中使用data.table包。以下是最小工作示例:

我创建了一个名为test.fun的函数,该函数简单地创建一个小数据表对象,然后通过"A"列对"Val"列进行分组求和。代码如下:

test.fun<-function ()
{
    library(data.table)
    testdata<-data.table(A=rep(seq(1,5), 5), Val=rnorm(25))
    setkey(testdata, A)
    res<-testdata[,{list(Ct=length(Val),Total=sum(Val),Avg=mean(Val))},"A"]
    return(res)
}

当我在常规的R会话中创建这个函数,然后运行该函数时,它按预期工作。

> res<-test.fun()
data.table 1.8.0  For help type: help("data.table")
> res
     A Ct      Total        Avg
[1,] 1  5 -0.5326444 -0.1065289
[2,] 2  5 -4.0832062 -0.8166412
[3,] 3  5  0.9458251  0.1891650
[4,] 4  5  2.0474791  0.4094958
[5,] 5  5  2.3609443  0.4721889

当我将这个函数放入一个包中,安装该包并加载该包后运行该函数时,我会收到一个错误信息。

> library(testpackage)
> res<-test.fun()
data.table 1.8.0  For help type: help("data.table")
Error in `[.data.frame`(x, i, j) : object 'Val' not found

可以有人解释一下为什么会发生这种情况,我该如何修复它。非常感谢任何帮助。


15
我的猜测是你没有声明依赖项。你应该从你的函数中移除 library(data.table),并在你的命名空间和 DESCRIPTION 中声明 depends:data.table。请注意不要改变原意。 - Andrie
1
现在也有.datatable.aware = TRUE选项来处理这个问题,如问题和下面链接的vignette中讨论的一样。声明Depends: data.table会将整个包附加到搜索路径中,这有时是不鼓励的。 - Niels
2个回答

99

Andrie的猜测是正确的,+1。有一个关于此的常见问题解答(见 vignette("datatable-faq")),以及一个关于导入 data.table 的新vignette

常见问题解答 6.9: 我创建了一个依赖于 data.table 的包。如何确保我的包具有 data.table 意识,以便继承自 data.frame 可正常工作?

要么 i)在您的 DESCRIPTION 文件的 Depends: 字段中包含 data.table,或 ii)在您的 DESCRIPTION 文件的 Imports: 字段中包含 data.table,并在您的 NAMESPACE 文件中包含 import(data.table)

进一步了解...在 [.data.table(和其他 data.table 函数)的顶部,您将看到一个 switch,取决于对 cedta() 的调用结果。这代表 Calling Environment Data Table Aware(调用环境数据表意识)。键入 data.table:::cedta 就会显示其实现方式。它依赖于调用包具有命名空间,并且该命名空间 Import 或 Depend on data.table。这就是为什么 data.table 可以传递给非 data.table 意识的包(例如在 base 中的函数),并且这些包可以对 data.table 使用绝对标准的 [.data.frame 语法,而不知道这个 data.frame 实际上也是一个data.table

这也是为什么data.table继承以前不兼容没有命名空间的包,并且在用户请求时,我们必须要求这些包的作者添加命名空间以实现兼容。值得庆幸的是,现在R在缺少命名空间的包中添加了默认命名空间(从v2.14.0开始),这个问题已经解决了:

R版本2.14.0中的更改:
* 所有包都必须有一个命名空间,并且在安装时如果源代码没有提供,则会创建一个。


1
@JeffAllen 这是一个新问题...不确定。如果你的程序包依赖于data.table,那么用户就会意识到data.table。也许导入data.table不会(也许这是你想要的)。 - Matt Dowle
4
@OskarHansson,如果你在DESCRIPTION文件中同时包含了两个Imports,并且在NAMESPACE文件中也包含了import(data.table),那么Import应该是可以起作用的,不过Depends也可以使用。 - Matt Dowle
3
@MattDowle 你是对的。当使用Depends时,我收到了一个NOTE。我改回了Imports +在代码中添加了@import data.table,这样Roxygen就会在NAMESPACE中添加import(data.table) - Oskar Hansson
我遇到了一个与上述情况完全相同的问题,但是上述方法并没有起作用。不知道为什么,但是 @import dtplyr 确实有帮助。很想知道原因。 - Jim
@Jim 如果我要猜的话,我会猜测是 roxygen2 的额外层 (@import) 没有完全转换为准确的上方。查看 roxygen2 的输出,看看它是否已经产生了完全相同的结果。 - Matt Dowle
显示剩余2条评论

39

以下是完整步骤:

  1. 在您的DESCRIPTION文件中的Imports中添加data.table

  2. 在相应的.R文件中(即包含出现错误Error in [.data.frame(x, i, j) : object 'Val' not found的函数所在的.R文件中)添加@import data.table

  3. 输入library(devtools)并将工作目录设置为指向R包的主目录。

  4. 输入document()。这将确保您的NAMESPACE文件包括一个import(data.table)行。

  5. 输入build()

  6. 输入install()

关于build()install()的详细介绍,请参见:http://kbroman.org/pkg_primer/

然后,一旦您关闭R会话并下次登录时,您可以立即执行以下操作:

  1. 输入library("my_R_package")

  2. 输入上述提到的.R文件中包含的函数的名称。

  3. 享受吧!您不应再收到可怕的Error in [.data.frame(x, i, j) : object 'Val' not found了。


我按照这些说明操作,但是出现了“找不到函数”的错误。我没有找到类似的内容,所以我创建了一个问题 https://stackoverflow.com/questions/56720520/creating-custom-r-package-with-data-table-custom-function - Kill3rbee Lee Mtoti
2
谢谢您。您帮助我以易于阅读的格式看到了该怎么做。 - Richard Erickson
这样的答案是最好的!然而,在我的情况下,我仍然需要在包中的某个地方添加.datatable.aware <- TRUE才能使其正常工作。 - Eden

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接