加快在大R数据框内搜索索引

时间:2013-08-07 10:48:27

标签: r search parallel-processing dataframe

这可能看起来像一个无关紧要的简单问题,但执行需要很长时间。任何加速它或矢量化等的想法都将非常感激。

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

该框架中的指数列表:IndexList(55000 [numIndex]个唯一索引)

它是一个时间序列,因此有5500个独特索引的约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有更好的方法来做这件事吗?

3 个答案:

答案 0 :(得分: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

我原本以为你也可以这样做 rownames,我认为它构建了一个哈希表并进行了索引 有效率的。但事实显然并非如此:

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

答案 1 :(得分:0)

检查data.table包裹。它的工作方式与data.frame相似,但更快。

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

table <- data.table(df)

并使用表格

答案 2 :(得分: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