【问题标题】:Speeding up searching for indices within a Large R Data Frame加快在大型 R 数据框中搜索索引
【发布时间】:2013-08-10 02:27:10
【问题描述】:

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

我有一个包含 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 中有更好的方法来做到这一点?

【问题讨论】:

  • 您在寻找OriginalDataFrame[OriginalDataFrame$dataIndex %in% unlist(IndexList)),]吗?
  • stackoverflow.com/questions/1727772/… 或者在RCpp中写下你需要性能的部分
  • 您好 Roland,感谢您的回答。您的解决方案是我第一次尝试没有并行化。使用该代码完成此操作需要 26 多个小时。之后我使用了多核版本。
  • @Maximus:谢谢你的建议。我试图先探索 R 中的矢量化操作,而不求助于 Cpp。不幸的是,我们的 R 在 Windows 上运行,我无法使用可用于 Linux 版本 R 的 GPU 包。

标签: r search parallel-processing dataframe


【解决方案1】:

这是一个比较 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

【讨论】:

    【解决方案2】:

    如果您有 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

    【讨论】:

    • 似乎不需要foreach。使用并行化来加速循环而不是使用矢量化方法是一种错误的尝试。
    • @Roland 我推测(可能是错误的??)索引后会有一些操作,原因完全相同。
    • 感谢里卡多的建议。你对问题的分析是正确的。我还没有走 data.table 路线。我正在考虑使用sqldf 看看这是否会有所作为,但您的建议听起来更好。当我有基准测试结果时,我一定会发布它们。可能需要到周末才能让机器免费进行基准测试。
    • @user2660094 没有问题。请参阅两个编辑。 Sqldf 也是一个不错的选择
    【解决方案3】:

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

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

    table <- data.table(df)
    

    并使用表格

    【讨论】:

    • 我同意。但是,您应该对此进行扩展(例如,展示如何使用 data.table 进行操作)以使其成为正确的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多