【问题标题】:Parallelization over for loop analyzing a data.frame并行化 for 循环分析 data.frame
【发布时间】:2018-03-08 15:49:45
【问题描述】:

这些天我一直在使用 8M 寄存器的 data.frame,我需要改进一个分析这些数据的循环。

我将描述我试图解决的问题的每个过程。 首先,我必须按 ClientID、Date 和 Time 三个字段按升序排列所有 data.frame。 (查看) 然后,使用那个安排好的data.frame,我必须操作每个观察之间的差异,只有当ClientID相同时才能做到这一点。例如:

ClientID|Date(YMD)|Time(HMS)
A|20120101|110000
A|20120101|111500
A|20120101|120000
B|20120202|010000
B|20120202|012030

根据上面的数据,我想要得到的结果如下:

ClientID|Date(YMD)|Time(HMS)|Difference(minutes)
A|20120101|110000|0.00
A|20120101|111500|15.00
A|20120101|120000|45.00
B|20120202|010000|0
B|20120202|012030|20.30

现在的问题是,使用包含 800 万个观测值的 data.frame 分析所有这些,大约需要 3 天时间。我希望我可以并行化这个过程。我的想法是 data.frame 可以按集群进行分割,但是这种分割可以是有序的而不是随机的,然后使用库 foreach 或其他库,可以通过集群进行分析并将其设置为可用的核心数.例如:

Cluster|ClientID|Date(YMD)|Time(HMS)
CORE 1|
1|A|20120101|110000
1|A|20120101|111500
1|A|20120101|120000
CORE 2|
2|B|20120202|010000
2|B|20120202|012030

【问题讨论】:

  • 按组计算时间差不应仅花费 3 天时间来进行 8m 次观察。你现在用什么代码?
  • R 是 。它在下面使用了一些惊人的快速库,但实际的 R 代码是您会发现的最慢的语言之一。为了使您的代码更快,请尽可能少使用 R,并像所有优秀的 R 包一样用 C、Fortran 等重写性能关键代码。
  • @Anony-Mousse -- 我并不是说你 100% 错了,但我强烈不同意你的建议 “尽可能少使用 R,并重写性能关键代码” 。借助利用 data.table 等包的高效 R 代码,您可以处理超过 1 亿行数据集,而速度不会成为限制因素。有一个非常好的理由存在高级脚本语言,而且我们并不是每次需要读取 CSV 和图形时都编写 Fortran 例程 - 易用性(稍微权衡一下性能)使 R 成为执行范围广泛的任务的绝佳选择。

标签: r parallel-processing data.table data-mining


【解决方案1】:

我不建议尝试并行化。使用data.table 包并处理以整数格式存储的时间,这将花费相当少的时间。

生成一些示例数据

library(data.table)

## Generate Data
RowCount <- 8e6
GroupCount <-1e4

DT <- data.table(ClientID = paste0("Client ",sample.int(GroupCount,size = RowCount, replace = TRUE)),
                 Time = sample.int(12,size = RowCount, replace = TRUE)*900)

DT[, Time := cumsum(Time), keyby = .(ClientID)]
DT[, Time := as.POSIXct(Time, tz = "UTC", origin = "1970-01-01 00:00:00")]

print(DT)

给了

            ClientID                Time
      1:    Client 1 1970-01-01 02:30:00
      2:    Client 1 1970-01-01 04:00:00
      3:    Client 1 1970-01-01 05:30:00
      4:    Client 1 1970-01-01 07:00:00
      5:    Client 1 1970-01-01 10:00:00
     ---                                
7999996: Client 9999 1970-02-20 18:15:00
7999997: Client 9999 1970-02-20 18:30:00
7999998: Client 9999 1970-02-20 21:00:00
7999999: Client 9999 1970-02-20 22:45:00
8000000: Client 9999 1970-02-21 00:30:00

计算时差

system.time({
  ## Create a integer column that stores time as the number of seconds midnight on 1970
  DT[,Time_Unix := as.integer(Time)]

  ## Order by ClientID then Time_Unix
  setkey(DT, ClientID, Time_Unix)

  ## Calculate Elapsed Time in minutes between rows, grouped by ClientID
  DT[, Elapsed_Minutes := (Time_Unix - shift(Time_Unix, n = 1L, type = "lag", fill = NA))/60L, keyby = .(ClientID)]

  ## Clean up the integer time
  DT[,Time_Unix := NULL]
})

...

   user  system elapsed 
  0.416   0.025   0.442 

结果:

print(DT)

...

            ClientID                Time Elapsed_Minutes
      1:    Client 1 1970-01-01 02:30:00              NA
      2:    Client 1 1970-01-01 04:00:00              90
      3:    Client 1 1970-01-01 05:30:00              90
      4:    Client 1 1970-01-01 07:00:00              90
      5:    Client 1 1970-01-01 10:00:00             180
     ---                                                
7999996: Client 9999 1970-02-20 18:15:00             135
7999997: Client 9999 1970-02-20 18:30:00              15
7999998: Client 9999 1970-02-20 21:00:00             150
7999999: Client 9999 1970-02-20 22:45:00             105
8000000: Client 9999 1970-02-21 00:30:00             105

【讨论】:

  • 太棒了!它工作得很好。从现在开始将在 data.tables 上使用 :)
  • 很高兴这对您有所帮助,如果您经常处理数百万行,您将永远不会回头!该软件包有一个 Getting Started page 和一些很好的资源。我还发现Advanced tips and tricks with data.table by Andrew Brooks 提供了一些不太常用的功能的很好示例。
  • 感谢您的知识!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-23
  • 2021-02-09
  • 2017-03-17
相关资源
最近更新 更多