2

这可能看起来像一个无害的简单问题,但执行起来需要很长时间。任何加快速度或矢量化等的想法将不胜感激。

我有一个包含 500 万行和 50 列的 R 数据框:OriginalDataFrame

该帧的索引列表:IndexList(55000 [ numIndex] 唯一索引)

它是一个时间序列,因此 55K 唯一索引有大约 500 万行。

OriginalDataFrame已由dataIndex订购。中的所有索引IndexList都不存在于 中OriginalDataFrame。任务是找到存在的索引,并构造一个新的数据框:FinalDataFrame

目前我正在使用以下代码运行此代码library(foreach)

FinalDataFrame <- foreach (i=1:numIndex, .combine="rbind") %dopar% {
  OriginalDataFrame[(OriginalDataFrame$dataIndex == IndexList[i]),]
}

我在一台 24 核和 128GB RAM 的机器上运行它,但这需要大约 6 个小时才能完成。

我是在做一些非常愚蠢的事情,还是在 R 中有更好的方法来做到这一点?

4

3 回答 3

3

这是一个比较 data.table 和 data.frame 的小基准。如果您知道这种情况下的特殊数据表调用,则速度大约快 7 倍,忽略设置索引的成本(相对较小,通常会在多次调用中分摊)。如果你不知道特殊的语法,它只会快一点。(注意问题的大小比原来的要小一些,以便于探索)

library(data.table)
library(microbenchmark)
options(digits = 3)

# Regular data frame
df <- data.frame(id = 1:1e5, x = runif(1e5), y = runif(1e5))

# Data table, with index
dt <- data.table(df)
setkey(dt, "id")

ids <- sample(1e5, 1e4)

microbenchmark(
  df[df$id %in% ids , ], # won't preserve order
  df[match(ids, df$id), ],
  dt[id %in% ids, ],
  dt[match(ids, id), ],
  dt[.(ids)]
)
# Unit: milliseconds
#                     expr   min    lq median    uq   max neval
#     df[df$id %in% ids, ] 13.61 13.99  14.69 17.26 53.81   100
#  df[match(ids, df$id), ] 16.62 17.03  17.36 18.10 21.22   100
#        dt[id %in% ids, ]  7.72  7.99   8.35  9.23 12.18   100
#     dt[match(ids, id), ] 16.44 17.03  17.36 17.77 61.57   100
#               dt[.(ids)]  1.93  2.16   2.27  2.43  5.77   100

我原本以为你也可以使用行名来做到这一点,我认为它建立了一个哈希表并有效地进行了索引。但显然情况并非如此:

df2 <- df
rownames(df2) <- as.character(df$id)
df2[as.character(ids), ],

microbenchmark(
  df[df$id %in% ids , ], # won't preserve order
  df2[as.character(ids), ],
  times = 1
)
# Unit: milliseconds
#                     expr    min     lq median     uq    max neval
#     df[df$id %in% ids, ]   15.3   15.3   15.3   15.3   15.3     1
# df2[as.character(ids), ] 3609.8 3609.8 3609.8 3609.8 3609.8     1
于 2013-08-07T13:44:29.267 回答
0

检查data.table包裹。它的工作原理相同data.frame但速度更快。

像这样(其中 df 是您的数据框):

table <- data.table(df)

并使用表

于 2013-08-07T11:19:27.760 回答
0

如果您有 5M 行并且您用于==标识要子集的行,那么对于循环的每一次通过,您将执行 5M 比较。如果您改为键入数据(因为它本来就是这样),那么您可以显着提高效率:

library(data.table)
OriginalDT <- as.data.table(OriginalDataFrame)
setkey(OriginalDT, dataIndex)

# Now inside your foreach:
OriginalDT[ .( IndexList[[i]] ) ]

请注意,该setkey函数使用非常快速的基数排序实现。但是,如果您的数据已经保证已排序,@eddi 或 @arun 发布了一个不错的技巧,只需将属性设置为 DT。(我现在找不到它,但也许有人可以编辑这个答案并链接到它)。

您可以尝试将所有结果收集到 data.tables 列表中,然后使用rbindlist并比较使用速度.combine=rbind(如果这样做,请随时发布基准测试结果)。我从未测试过.combine=rbindlist,但这可能也有效,尝试起来会很有趣。

编辑:

如果唯一的任务是索引 data.frame,那么只需使用:

dataIndex[ .( IndexList ) ]

没有foreach必要,您仍然可以利用密钥的 DT

于 2013-08-07T11:23:54.433 回答