【问题标题】:data.table efficient recycling V2data.table高效回收V2
【发布时间】:2020-03-30 12:17:08
【问题描述】:

这是对这个问题的跟进:data.table efficient recycling

这里的区别在于每行的未来年数不一定相同..

我经常在 data.table 中使用回收,例如当我需要预测未来几年时。我在以后的每一年重复我的原始数据。

这可能会导致类似的情况:

library(data.table)
dt <- data.table(1:500000, 500000:1, rpois(500000, 240))
dt2 <- dt[, c(.SD, .(year = 1:V3)), by = 1:nrow(dt) ]

但我经常需要处理数百万行,列数远远超过这个玩具示例。时间增加..试试这个:

library(data.table)
dt <- data.table(1:5000000, 5000000:1, rpois(5000000, 240))
dt2 <- dt[, c(.SD, .(year = 1:V3)), by = 1:nrow(dt) ]

我的问题是:有没有更有效的方法来达到这个目的?

感谢您的帮助!

【问题讨论】:

  • 您需要使用多少内存?已经使用500000 我得到dt2 是2Gb
  • 我不太清楚.. 但我有一台不错的电脑
  • 我仍然被这个问题困扰..
  • @MrSmithGoesToWashington 如果您要对现有答案提供反馈,将会很有用,它是否解决了问题但效率不够?
  • @jangorecki 是的,你是对的!我会在一段时间内做(作为给定答案的评论)

标签: r data.table recycle


【解决方案1】:

这是另一个答案的略微改进版本。

  • unlist使用非默认值
  • rep.int 而不是 rep
  • seq_len 而不是 :
  • setDT 而不是 data.table()
  • @Cole 建议的 sequence 函数会更好
  • 以及内部vecseq 的进一步小改进

合起来似乎很重要。

时间...

library(data.table)
f0 = function(dt) {
  dt[, c(.SD, .(year = 1:V3)), by = 1:nrow(dt) ]
}
f1 = function(dt) {
  dt2 <- data.table(
    rep(dt$V1, dt$V3),
    rep(dt$V2, dt$V3),
    rep(dt$V3, dt$V3),
    unlist(lapply(dt$V3, function(x){1:x}))
  )
  dt2
}
f2 = function(dt) {
  dt2 = list(
    V1 = rep.int(dt$V1, dt$V3),
    V2 = rep.int(dt$V2, dt$V3),
    V3 = rep.int(dt$V3, dt$V3),
    year = unlist(lapply(dt$V3, seq_len), recursive=FALSE, use.names=FALSE)
  )
  setDT(dt2)
  dt2
}
f3 = function(dt) {
  ## even better with sequence function suggested by @Cole
  dt2 = list(
    V1 = rep.int(dt$V1, dt$V3),
    V2 = rep.int(dt$V2, dt$V3),
    V3 = rep.int(dt$V3, dt$V3),
    year = sequence(dt$V3)
  )
  setDT(dt2)
  dt2
}
f4 = function(dt) {
  dt[, c(lapply(.SD, rep.int, V3), year = .(sequence(V3)))]
}
f5 = function(dt) {
  dt2 = list(
    V1 = rep.int(dt$V1, dt$V3),
    V2 = rep.int(dt$V2, dt$V3),
    V3 = rep.int(dt$V3, dt$V3),
    year = data.table:::vecseq(rep.int(1L,length(dt$V3)), dt$V3, NULL)
  )
  setDT(dt2)
  dt2
}

关于“大”数据

dt <- data.table(1:5000000, 5000000:1, rpois(5000000, 240))
system.time(f0(dt))
#   user  system elapsed 
# 22.100  18.914  40.449 
system.time(f1(dt))
#   user  system elapsed 
# 35.866  15.607  51.475 
system.time(f2(dt))
#   user  system elapsed 
# 22.922   6.839  29.760 
system.time(f3(dt))
#   user  system elapsed 
#  6.509   6.723  13.233 
system.time(f4(dt))
#   user  system elapsed 
# 12.140  14.114  26.254 
system.time(f5(dt))
#   user  system elapsed 
#  6.448   4.057  10.506 

无论如何,您应该尝试改进您在扩展数据集上运行的流程,因为也许您一开始就不必扩展它。

例如,在frollmean 函数中,有一个参数adaptive 可以计算可变长度窗口上的滚动平均值,通常需要先扩展数据来计算。 V3 在你的数据中提醒了很多自适应移动平均的窗口长度。

【讨论】:

  • 感谢您的回答..关于您对扩展的评论:我需要扩展,因为我正在逐年进行预测..
  • 我已经在我的真实数据上测试了你的方式.. 非常非常好的改进(我不得不使用 rep 而不是 rep.int,因为我有很多种列)即使,列越多,编写代码的时间就越长(这使得阅读代码变得可怕..),但我认为我不可能从中制作更通用的功能..我会让其他人一个打败你的机会,如果没有人做得更好,我会奖励你的答案。非常感谢!
  • 将 lapply 迁移到 C/C++ 肯定会有所帮助,但需要编写已编译的代码。
  • 使用sequence(V3)。它几乎是用 C.dt[, c(lapply(.SD, rep.int, V3), year = .(sequence(V3)))] 编译的 unlist(lapply(...)) 代码。我也同意@jangorecki 的观点,即尝试看看您是否可以对数据做您需要做的事情,而不是一开始就将其全部扩展。
  • @Cole 我意识到我们有内部函数 :) 有趣的是,尽管必须实现 rep(1L, n),然后在创建序列时循环该向量,它仍然比基本 R sequence 快.
【解决方案2】:

这是一个更快的实现,但由于data.table 中的lapply 循环仍然很长

dt2 <- data.table(
  rep(dt$V1, dt$V3),
  rep(dt$V2, dt$V3),
  rep(dt$V3, dt$V3),
  unlist(lapply(dt$V3, function(x){1:x}))
)

希望对你有帮助!

【讨论】:

  • 感谢您的回答,但问题是给出了 V3 值(我仅使用 rpois 来在表中包含值)。所以你必须假设 dt 给出了三列,然后找到一种方法将 eahc 行复制到 V3 给出的次数..
  • @MrSmithGoesToWashington - 我根据您的评论编辑了我的答案。我知道它的处理时间仍然很长,但它比您的实施要快。真正的关键是使用lapply 改进线路。如果您找到一种无需循环即可对其进行矢量化的方法,那么效率会更高。我今天有点忙,但如果没有人给出更好的答案,我会在这个周末查看它。
【解决方案3】:

试试这个:

  dt2 <- dt[dt[,rep(1:nrow(dt),V3)],]
  dt2[,year:= dt[,sequence(V3)]] 

【讨论】:

  • 感谢您的回答。我所做的测试表明它并不比 jangorecki 的答案好。
猜你喜欢
  • 2020-03-28
  • 2018-08-27
  • 2018-04-20
  • 1970-01-01
  • 2013-03-27
  • 1970-01-01
  • 1970-01-01
  • 2021-10-26
  • 1970-01-01
相关资源
最近更新 更多