将环境中的所有数据框转换为数据表

7
当我将所有的data.frame转换为data.table后,使用:=时会收到一个警告:
library(data.table) #Win R-3.5.1 x64 data.table_1.12.2
df1 <- data.frame(A=1, B=2)
df2 <- data.frame(D=3)
lapply(mget(ls()), function(x) {
    if (is.data.frame(x)) {
        setDT(x)
    }
})
df1[, rn:=.I]

警告信息: 在[.data.table(df1, , :=(rn, .I))中检测到无效的.internal.selfref并通过对数据表进行(浅层)复制来修复,以便:=可以通过引用添加此新列。 在早期的某个时刻,这个数据表已经被R复制(或者是使用structure()或类似方法手动创建的)。 避免使用names<-和attr<-,因为它们在R中当前(奇怪地)可能会复制整个数据表。 使用set*语法来避免复制:?set、?setnames和?setattr。 如果这个消息没有帮助,请将您的用例报告给data.table问题跟踪器,以便修复根本原因或改进此消息。
下面也会生成相同的警告:
df3 <- data.frame(E=3)
df4 <- data.frame(FF=4)
for (l in list(df3, df4)) setDT(l)
df3[, rn:=.I]

一个一个打很繁琐,但有效。
df5 <- data.frame(G=5)
setDT(df5)
df[, rn := .I]    #no warning

如何习惯性地将所有的data.frame转换为data.table?

相关:

  1. 在函数内使用setDT
  2. data.table中的无效.internal.selfref
4个回答

5

setDT 操作名称/符号,而 get 返回对象的值。您可以构建 setDT 表达式并对其求值:

library(data.table) 
df1 <- data.frame(A=1, B=2)
df2 <- data.frame(D=3)
for(x in ls()){
  if (is.data.frame(get(x))) {
    eval(substitute(setDT(x), list(x=as.name(x))))
  }
}
rm(x)
df1[, rn:=.I]

我会使用循环而不是lapply来避免复杂性(例如,与评估环境有关)。

3
有点晚了,但这似乎是一个很好的 - 也很少见的 - 使用eapply()(以及list2env())的例子。当然,这是另一种选择,肯定不能断言它是惯用方式。
library(data.table)
df1 <- data.frame(A=1, B=2)
df2 <- data.frame(D=3)

list2env(eapply(.GlobalEnv, function(x) {if(is.data.frame(x)) {setDT(x)} else {x}}), .GlobalEnv)

df1[, rn:=.I]
df1
   A B rn
1: 1 2  1

一些时间和内存使用情况:
set.seed(0L)
sz <- 1e7
df1 <- data.frame(A=rnorm(sz))
df2 <- data.frame(B=rnorm(sz))
df3 <- copy(df1)
df4 <- copy(df2)

microbenchmark::microbenchmark(unit="ms", times=1L,
    assign_mtd = {
        for (x in ls()) {
            if (is.data.frame(get(x))) {
                assign(x, as.data.table(get(x)))
            }
        }
    },
    eval_sub_mtd = {
        for(x in ls()){
            if (is.data.frame(get(x))) {
                eval(substitute(setDT(x), list(x=as.name(x))))
            }
        }
    },
    eapply_mtd = {
        list2env(eapply(.GlobalEnv, function(x) {
                if (is.data.frame(x)) setDT(x) else x
            }), .GlobalEnv)
    }
)

时间:

Unit: milliseconds
         expr        min         lq       mean     median         uq        max neval
   assign_mtd 115.922802 115.922802 115.922802 115.922802 115.922802 115.922802     1
 eval_sub_mtd   3.293358   3.293358   3.293358   3.293358   3.293358   3.293358     1
   eapply_mtd   1.913802   1.913802   1.913802   1.913802   1.913802   1.913802     1

1
@Frank和Andrew,我不确定更改标记解决方案的礼仪。由于这个更快,我想改为选择这个答案。可以吗? - chinsoon12
1
很酷的加速!我不认为我曾经使用过eapply,如果周围有很多其他对象,我想else x可能会很昂贵(不确定)?@chinsoon12 听起来不错,没问题。顺便说一句,我不会从@中看到你的消息 - 我只是在看到你编辑我的帖子后浏览到这里的。 - Frank

3
这应该可以解决问题:
library(data.table) #Win R-3.5.1 x64 data.table_1.12.2
df1 <- data.frame(A=1, B=2)
df2 <- data.frame(D=3)
for (x in ls()) {
    if (is.data.frame(get(x))) {
        assign(x, as.data.table(get(x)))
    }
}
df1[, rn:=.I]

我猜(不确定)for/lapply循环使用了一种自己的环境,这会干扰data.table的按引用语义。

谢谢你,thothal。如果除了 assignas.data.table 以外没有其他内容出现,我会等待24小时后再接受你的回答。 - chinsoon12
谢谢,Thothal。我选择了另一个答案,因为它更快且内存使用更少。 - chinsoon12

1

这不是我的答案。 我记得在某个地方看到过,但找不到原始帖子,因此无法链接到它。

x <- Filter(\(i) is.data.frame(eval(as.name(i))), ls())
lapply(x, \(i) setDT(get(i)))

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