为什么lapply()函数不能保留我的data.table键?

12

我有一个数据表列表,想对每个数据表应用unique()函数,但这样做会破坏所有的数据表键。

以下是一个例子:

A <- data.table(a = rep(c("a","b"), each = 3), b = runif(6), key = "a")
B <- data.table(x = runif(6), b = runif(6), key = "x")

blah <- unique(A)

在这里,blah仍然有一个键,世界上一切都正确:

key(blah)

# [1] "a"

但是,如果我将data.tables添加到列表中并使用lapply(),则键会被破坏:

dt.list <- list(A, B)

unique.list <- lapply(dt.list, unique) # Keys destroyed here

lapply(unique.list, key) 

# [[1]]
# NULL

# [[2]]
# NULL

我可能并不真正理解赋予键“通过引用”意味着什么,这也导致了其他键丢失的问题。

所以:

  • 为什么lapply不能保留我的键?
  • 说键是“通过引用”赋值,这是什么意思?
  • 我是否应该将data.table存储在列表中?
  • 如何安全地存储/操作数据表,而不必担心丢失我的键?

编辑:

值得一提的是,可怕的for循环也可以正常工作:

unique.list <- list()

for (i in 1:length(dt.list)) {
  unique.list[[i]] <- unique(dt.list[[i]])
}

lapply(unique.list, key)

# [[1]]
# [1] "a"

# [[2]]
# [1] "x"

但这是R语言,for循环是有害的


1
有趣的是,unique.list[[1]] != unique(A)。我的猜测是,也许在lapply语句中被调用的是{base}的unique而不是{data.table}的unique,虽然这只是一个猜测。 - Ricardo Saporta
我认为你是对的。我刚刚注意到,除了键被销毁之外,“unique”在通过“lapply”传递时甚至没有发挥作用。 - Paul Murray
1
@PaulMurray +1 好问题,但 _destroy_(销毁)似乎有点过于强烈了。在这种情况下它并不 _retain_(保留)它们。 - Matt Dowle
1
@MatthewDowle "语言已编辑" - Paul Murray
1
或者你应该像“data.table和lapply消除表键”一样走到底。 - statquant
2个回答

9
有趣的是,注意这两个不同结果之间的差异。
lapply(dt.list, unique) 
lapply(dt.list, function(x) unique(x)) 

如果您使用后者,结果将如您所预期。
看似令人意外的行为是由于第一个语句调用了(即来自),而第二个调用了造成的。

1
@Plmrry 我认为当你将data.table存储为列表时,你会失去data.table提供的一个重要优势,即通过避免不必要的复制来实现内存效率。个人而言,我会将我的data.table名称存储为列表,然后通过get(name)来获取它们。这样的代码可能有点丑陋,但速度更快。 - Ricardo Saporta
是的,看起来我应该放弃将data.table存储在列表中。我有几个函数可以抓取XML并输出关系表的列表,但我想我应该将它们保留在环境中,并将它们全部保存为RData... 当涉及到处理大量数据表(或框架),特别是当我想要将它们作为参数传递时,我仍在寻找“最佳实践”。 - Paul Murray
@MatthewDowle 好的,这些是独立的关系表,所以我不能使用rbind将它们合并。例如,我的函数可能会抓取“学校”、“学生”和“州”,每个都通过一个关系表与其他表相关联,例如“学校.学生”。 - Paul Murray
@MatthewDowle 我没意识到有一个 rbindlist - Ricardo Saporta
@RicardoSaporta 你还不知道什么是你不知道的吗?你见过unknownR::unk()吗? :-) 像data.table这样的软件包列表可以传递给它。 - Matt Dowle
显示剩余5条评论

6

很好的问题。事实证明,在?lapply(请参见注释部分)中有记录:

由于历史原因,lapply创建的调用未被评估,而且编写了代码(例如bquote)依赖此功能。这意味着记录的调用始终采用FUN(X [[0L]],...)的形式,其中0L替换为当前整数索引。这通常不是问题,但如果FUN使用sys.call或match.call或者它是使用调用的原始函数,则可能会出现问题。这意味着通常更安全地使用包装器调用原始函数,以便例如,在R 2.7.1中需要使用lapply(ll,function(x)is.numeric(x))来确保is.numeric的方法调度正确。


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