【问题标题】:data.table frollmean very slowdata.table 滚动意味着非常慢
【发布时间】:2020-05-31 23:51:45
【问题描述】:

我正在尝试计算由其他两列聚合的大型 data.table(约 30M 行)中列的滚动平均值。 滚动平均值应仅包括前面的 N 行值,而不是行值本身。 为此,我必须根据 frollmean 函数定义自己的滚动平均函数。 (N=3) 将函数应用于列真的很慢,变得相当无用。

这里是示例数据:

require(data.table)
DT <- data.table(ID=c('A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C')
               , value_type =c('type 1', 'type 1','type 2','type 1','type 2','type 2','type 1','type 1','type 2','type 1','type 1','type 1')
               , value=c(1,4,7,2,3,5,1,6,8,2,2,3))
    DT
         ID value_type value
     1:  A     type 1     1
     2:  A     type 1     4
     3:  A     type 2     7
     4:  A     type 1     2
     5:  A     type 2     3
     6:  A     type 2     5
     7:  B     type 1     1
     8:  B     type 1     6
     9:  B     type 2     8
    10:  C     type 1     2
    11:  C     type 1     2
    12:  C     type 1     3

#this is the customised rolling function
lrollmean<-function(x){

  head(frollmean(c(NA,NA,NA,x), n = 3, fill = NA, algo ="exact", align="right", na.rm = TRUE)[-(1:2)], -1)

}

> DT[, roll_mean := lrollmean(value), by=.(ID, value_type)]
> DT
    ID value_type value roll_mean
 1:  A     type 1     1       NaN
 2:  A     type 1     4       1.0
 3:  A     type 2     7       NaN
 4:  A     type 1     2       2.5
 5:  A     type 2     3       7.0
 6:  A     type 2     5       5.0
 7:  B     type 1     1       NaN
 8:  B     type 1     6       1.0
 9:  B     type 2     8       NaN
10:  C     type 1     2       NaN
11:  C     type 1     2       2.0
12:  C     type 1     3       2.0

此操作需要 30 多分钟!我有一台合理的机器,有足够的内存,我觉得长时间的操作与我的代码而不是机器有关。

【问题讨论】:

  • 你有很多组吗?例如在 30M 行中,有多少组?你真的需要algo="exact"吗?
  • 数据有大约 800,000 个唯一 ID。不,我不相信我真的需要 algo = "exact" :)
  • 我将 algo="exact" 更改为 algo="fast" 并且速度要快得多!
  • 如果您真的需要精确,那么在有很多组的情况下切换到 1 个线程可能会有所帮助。

标签: r data.table mean rolling-computation


【解决方案1】:

你可以试试看它是否足够快:

n <- 3L
DT[, roll_mean := {
        v <- if (.N - n >= 1L) c(seq.int(n), rep(n, .N-n)) else seq.int(min(n, .N))
        shift(frollmean(value, v, adaptive=TRUE))
    }, .(ID, value_type)]

但如果小团体人数较多,可以试试:

setorder(DT[, rn := .I], ID, value_type)
rid <- DT[, rowid(ID, value_type)]

DT[, roll_mean := shift(frollmean(value, n))]

ix <- DT[rid==3L, which=TRUE]
set(DT, ix, "roll_mean", DT[, shift(frollmean(value, n - 1L))][ix])

ix <- DT[rid==2L, which=TRUE]
set(DT, ix, "roll_mean", DT[, shift(value)][ix])

DT[rid==1L, roll_mean := NA_real_]
setorder(DT, rn)[]

【讨论】:

  • 感谢您的解决方案。为了比较,我在我的数据集上尝试了第一个解决方案,我得到了这个错误 Error in rep(k, .N - k) : invalid 'times' argument。另外,虽然我们正在讨论这个问题,但由于我过度简化了帖子的问题,实际上应该计算两列平均值。 ID 和另一个名为 value_type 的列。您的解决方案是否可以包含两个聚合列,或者这样做真的很糟糕?非常感谢
  • 您能否更新您的数据集以确保没有歧义?
  • 感谢@chinsoon12 我已经编辑了我的帖子以匹配我的真实数据集,并在 data.table 中添加了 value_type。 k 与代码中的 n 相同。我不得不在我的代码中使用 k 而不是 n 以免它与另一个变量混淆。我不认为错误出在语法上,因为我尝试使用 n 并得到相同的错误。
  • @Ankhnesmerira,更新了代码以解决少于 3 行和再添加一个分组变量时的错误
  • 非常感谢@chinsoon12。目前我正在调查我在尝试第一个解决方案时遇到的这个错误:froll中的错误(fun =“mean”,x = x,n = n,fill = fill,algo = algo,align = align,:长度作为“n”参数列表提供的整数向量必须等于“x”中提供的观察数。
【解决方案2】:

您可以尝试frollapply,因为frollmean 并不完全适合您的需求。您还可以优化应用于窗口的功能,因为您不需要非常复杂的操作。我已经尝试对您的函数进行一些修改,这些修改应该可以将您的时间减少大约 50%。

library(data.table)
library(stringi)
N=1e6
set.seed(123)
DT <- data.table(ID=stri_rand_strings(N,3),
                 value=rnorm(N,5,5))
head(DT)
#>     ID      value
#> 1: HmP 12.2667538
#> 2: sw2 -2.2397053
#> 3: WtY  7.0911933
#> 4: SxS  0.4029431
#> 5: gZ6  8.6800795
#> 6: tF2  0.8228594
DT[,.(.N),by=ID][order(N)]
#>          ID  N
#>      1: HoR  1
#>      2: eNM  1
#>      3: I9h  1
#>      4: xjb  1
#>      5: eFH  1
#>     ---       
#> 234823: 34Y 15
#> 234824: Xcm 15
#> 234825: IOu 15
#> 234826: tob 16
#> 234827: f70 16

# Your function
lrollmean<-function(x){ 
  head(frollmean(c(NA,NA,NA,x), n = 3, fill = NA, algo ="exact", align="right", na.rm = TRUE)[-(1:2)], -1)
}

#Possible modifications:
lrollmean1<-function(x,n){
  frollapply(c(rep(NA,n),x),n+1,weighted.mean,c(rep(1,n),0),na.rm=T)[-(1:3)]
}
lrollmean2<-function(x,n){
  frollapply(c(rep(NA,n),x),n+1,function(x) sum(x*c(rep(1,n),0)/n,na.rm = T))[-(1:3)]
}
lrollmean3<-function(x){ # More optimized assuming n=3
  frollapply(c(NA,NA,NA,x),4,function(x) sum(x[1:3]/3,na.rm = T))[-(1:3)]
}


library(rbenchmark)

benchmark(original={DT[, roll_mean := lrollmean1(value,3), by=.(ID)]},
          a={DT[, roll_mean := lrollmean1(value,3), by=.(ID)]},
          b={DT[, roll_mean := lrollmean2(value,3), by=.(ID)]},
          c={DT[, roll_mean := lrollmean3(value), by=.(ID)]}
          ,replications = 1,order = 'relative')
#>       test replications elapsed relative user.self sys.self user.child
#> 4        c            1   6.740    1.000     6.829    0.000          0
#> 3        b            1   8.038    1.193     8.085    0.012          0
#> 1 original            1  13.599    2.018    13.692    0.000          0
#> 2        a            1  14.180    2.104    14.233    0.008          0
#>   sys.child
#> 4         0
#> 3         0
#> 1         0
#> 2         0

reprex package (v0.3.0) 于 2020-02-17 创建

【讨论】:

    猜你喜欢
    • 2012-08-16
    • 2021-09-05
    • 2022-12-18
    • 2021-09-19
    • 2019-01-25
    • 1970-01-01
    • 2021-12-22
    • 1970-01-01
    • 2011-04-20
    相关资源
    最近更新 更多