【问题标题】:Why does lapply() not retain my data.table keys?为什么 lapply() 不保留我的 data.table 键?
【发布时间】:2013-02-02 09:50:26
【问题描述】:

我在一个列表中有一堆 data.tables。我想对列表中的每个 data.table 应用 unique(),但这样做会破坏我所有的 data.table 键。

这是一个例子:

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.tables 存储在列表中吗?
  • 如何安全地存储/操作 data.tables 而不必担心丢失密钥?

编辑:

不管怎样,可怕的 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 循环是邪恶的。

【问题讨论】:

  • 有趣的是,unique.list[[1]] != unique(A)。我的猜测虽然只是一个猜测,但也许 lapply 语句中调用的是 {base} 唯一而不是 {data.table} unique。
  • 我认为你是对的。我刚刚注意到 - 随着密钥被销毁 - unique 在通过 lapply 时甚至没有完成它的工作
  • @PaulMurray +1 好问题,但 destroy 似乎有点强。在这种情况下,它不会保留它们。
  • @MatthewDowle“为语言编辑”
  • 或者你应该像“data.table and lapply annihilate table keys”一样一路走下去

标签: r data.table lapply


【解决方案1】:

有趣的是,注意这两个不同结果之间的区别

lapply(dt.list, unique) 
lapply(dt.list, function(x) unique(x)) 

如果您使用后者,结果将如您所愿。


看似意外的行为是由于第一个lapply 语句是 调用unique.data.frame(即来自{base}),而第二个调用unique.data.table

【讨论】:

  • 哇,谢谢。而且奇怪。这是一个解决方案,但我仍然想知道发生了什么。我是唯一一个想要将 data.tables 存储在列表中的人吗?我将把它作为bug report 提交——你认为这公平吗?
  • @Plmrry 我认为当您将data.tables 存储为列表时,您会失去 data.table 提供的一大好处,即通过避免不必要的重复来提高内存效率。就个人而言,我存储了我的 data.tables 的 names 列表,然后通过 get( name ) 获取它们。有点难看的代码,但速度更快。
  • @Plmrry 至于错误报告,它可能不是错误,而只是match.fun() 的工作方式。虽然我不能说
  • 是的,看起来我应该放弃将data.tables 存储在列表中。我有几个函数可以抓取 XML 并输出关系表列表,但我想我应该将它们原样保存在环境中并将它们全部保存为 RData... 我仍在寻找“最佳在处理大量数据表(或框架)时,尤其是当我希望能够将它们作为参数传递时。
  • @PaulMurray 我倾向于有一张大桌子而不是许多小桌子的列表。对于像刮擦这样的事情,我可能会刮到data.table 的list,是的,但是然后使用rbindlist 来创建大的。 insert() 引用的行在愿望清单上。
【解决方案2】:

好问题。事实证明,它记录在 ?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 的该方法调度正确发生。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-06
    • 2013-10-06
    相关资源
    最近更新 更多