【问题标题】:Recursive function in R to find unique rows of a list of data tablesR中的递归函数用于查找数据表列表的唯一行
【发布时间】:2013-06-19 16:03:44
【问题描述】:

我正在开发一个函数,该函数将具有相同列名的数据表列表作为输入,并返回单个数据表,其中包含使用连续 rbind 组合的每个数据帧的唯一行,如下所示。

该函数将应用于“非常”大的 data.table(数百万行),这就是为什么我必须将其拆分为几个较小的数据表并将它们分配到一个列表中以使用递归。在每一步,根据数据表列表的长度(奇数或偶数),我在该列表索引处找到 data.table 的唯一性,在列表索引 x - 1 处找到数据表,然后连续 rbind 2 并分配列出索引 x - 1,以及更多列出索引 x。

我一定遗漏了一些明显的东西,因为虽然我可以在打印时生成最终的 unique-d data.table(例如 print (listelement[[1]]),但当我返回时 (listelement[[1] ]) 我得到 NULL。如果有人能发现我遗漏的东西会有所帮助......或者建议是否有其他更有效的方法来执行此操作。

另外,不必将每个 data.table 添加到列表中,我可以将它们添加为列表中的“引用”吗?我相信像 list(datatable1, datatable2 ...) 这样的事情实际上会复制它们?

## CODE
returnUnique2 <- function (alist) {

if (length(alist) == 1) {
  z <- (alist[[1]])
  print (class(z))
  print (z)   ### This is the issue, if I change to return (z), I get NULL (?)
}

if (length(alist) %% 2 == 0) {
  alist[[length(alist) - 1]] <- unique(rbind(unique(alist[[length(alist)]]), unique(alist[[length(alist) - 1]])))
  alist[[length(alist)]] <- NULL
  returnUnique2(alist)
}

if (length(alist) %% 2 == 1 && length(alist) > 2) {
  alist[[length(alist) - 1]] <- unique(rbind(unique(alist[[length(alist)]]), unique(alist[[length(alist) - 1]])))
  alist[[length(alist)]] <- NULL
  returnUnique2(alist)
}  
}


## OUTPUT with print statement
t1 <- data.table(col1=rep("a",10), col2=round(runif(10,1,10)))
t2 <- data.table(col1=rep("a",10), col2=round(runif(10,1,10)))
t3 <- data.table(col1=rep("a",10), col2=round(runif(10,1,10)))
tempList <- list(t1, t2, t3)

returnUnique2(tempList)

[1] "list"
[[1]]
col1 col2
 1:    a    3
 2:    a    2
 3:    a    5
 4:    a    9
 5:    a   10
 6:    a    7
 7:    a    1
 8:    a    8
 9:    a    4
10:    a    6

更改以下内容,

print (z)   ### This is the issue, if I change to return (z), I get NULL (?)

阅读

return(z)

返回 NULL

提前致谢。

【问题讨论】:

  • 对于这种大小的数据使用sqlite 怎么样?
  • 嗨 Karsten,谢谢,我试过了,但还是太慢了……我正在使用 fread 读取 csv 格式的数据,而且似乎比 sqlite 快。另外,以这种方式使用它的动机是我有几个其他数据集,我使用 doParallel/multicore 包将它们卸载到服务器上的各个核心。
  • 下面的有用吗?

标签: r data.table


【解决方案1】:

如果我误解了你在做什么,请纠正我,但听起来你有一个大的 data.table 并试图将其拆分以在其上运行某些功能,然后将所有内容组合回来并运行一个独特的那。 data.table 的做法是使用 by,例如

fn = function(d) {
  # do whatever to the subset and return the resulting data.table
  # in this case, do nothing
  d
}

N = 10  # number of pieces you like
dt[, fn(.SD), by = (seq_len(nrow(dt)) - 1) %/% (nrow(dt)/N)][, seq_len := NULL]
dt = dt[!duplicated(dt)]

【讨论】:

  • 我认为他们确实只是想要获得data.table中的唯一行,即最后一行!
  • @SimonO101 好的,我认为 OP 的另一个问题是初始数据太大,他们决定将其拆分,将其放入列表中并对小块进行操作,这是如上所示,可以用更少的麻烦来完成
  • 嗨,Simon,Eddi,以上内容非常适合我打算做的事情。主要是我试图避免 for 循环,这非常优雅地实现了这一点。感谢大家对这个问题的帮助!有问题的数据表有超过 10 亿行,甚至 fread 甚至开始解析文件都需要很长时间。所以,我不得不使用 unix split 来分割这个文件,然后按顺序处理。否则,我可以一次在整个数据集上运行唯一的,但在这种情况下,这不是一个选择。
【解决方案2】:

似乎这对于for 循环来说可能是一个很好的用例。对于许多行,与计算时间相比,使用for 循环的开销应该相对较小。我会尝试将我的data.table 组合到一个列表中(在我的示例中称为ll),然后为每个删除重复的行,然后将rbind 与具有唯一行的前一个data.table 合并,然后按唯一行进行子集再次。

如果每个块中有很多重复的行,那么这可能会节省一些时间,总的来说我不确定它的效果如何,但值得一试吗?

#  Create empty data.table for results (I have columns x and y in this case)
res <- data.table( x= numeric(0),y=numeric(0))

#   loop over all data.tables in a list called 'll'
for( i in 1:length(ll) ){
    #  rbind the unique rows from the current list element to the results from all previous iterations
    res <- rbind( res , ll[[i]][ ! duplicated(ll[[i]]) , ] )
    #  Keep only unique records at each iteration
    res <- res[ ! duplicated(res) , ]
}

另一方面,您是否查看过data.table 的文档?它明确指出,

因为 data.tables 通常按 key 排序,所以测试重复 特别快。

所以你最好在整个 data.table 上运行?

DT[ ! duplicated(DT) , ]

【讨论】:

  • 另一种选择是将数据拆分为 2s 并递归地 rbind。例如,如果我有 8 个块 - 1,2,3,4,5,6,7,8 而不是增量执行唯一 rbind,我可以获得 (1,2), (3,4) 的唯一 rbind , (5,6), (7,8) 然后在 2s 内再次 rbind。可能比增量 rbind 更有效。
  • @xbsd 肯定会更有效率。我想这是一个设计决定。有效性将取决于连续块的“唯一性”与否。但是在实践中使用最后一行有多慢? 10毫秒? 10秒? 10 小时?
  • 嗨西蒙,我相信只使用最后一行可能会非常快,问题是加载整个数据集然后运行唯一的会占用大量内存并可能导致速度变慢,而不是由于操作,但更多的是在硬件负载级别。将它们分成块,在每次迭代时运行唯一的 gc(),并逐渐减少数据集的大小似乎是一个更快的选择。我已经在 Mac 上跨 4 个内核使用 foreach 和 dopar 进行了尝试,与单个唯一操作相比,使用 10m 行数据集要快得多。
  • @SimonO101 需要记住的一件小事(与 OP 无关)(这将减少一点混乱)是i-expression 之后的逗号在处理数据时不是必需的。没有j 的表格
  • @eddi 感谢您指出这一点。我不知道,干杯。
【解决方案3】:

为每个data.table添加一个id列

t1$id=1
t2$id=2
t3$id=3

然后将它们一次性组合起来,并使用by= 做一个独特的事情。 如果 data.tables 很大,您可以在调用 unique 之前使用 setkey(...) 在 id 上创建索引。

tall=rbind(t1,t2,t3)
tall[,unique(col1,col2),by=id]

【讨论】:

  • 我需要所有数据表组合的唯一性,...不是列表中的每个数据表,...从计算上讲,在具有 1 亿行的表上运行唯一性将永远进行比较到分块拆分-应用唯一-组合-重复类型策略。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-09
  • 2017-06-22
相关资源
最近更新 更多