【问题标题】:R - data table rolling window- customized functionR - 数据表滚动窗口 - 自定义功能
【发布时间】:2018-01-24 20:39:15
【问题描述】:

如果我正在使用的值是与前 30 个值相比的异常值,我需要计算 ts。 我正在使用的数据的维度为 600 列 x 200000 行。所以我想利用数据表速度的好处。

我的功能是:

es_outlier<-function(vect){
  qq =quantile(vect, prob=c(0.25,0.75), na.rm=T)
  q3=qq[2]
  IC=q3-qq[1]
  limSup=q3+IC*1.5
  vector_final=abs(vect)>limSup
  return(vector_final[length(vect)] )
}

一个示例表是:

library(data.table)

dt<-data.table(x1=runif(50000), x2=runif(50000))
dt$x1[555]<-2000
dt$x2[556]<-2000

我可以用 zoo 包解决这个问题:

zoo::rollapply(dt,30,es_outlier, fill=NA,align='right')

但这需要很多时间,而且比我的真实数据要少。

我想要类似的东西:

dt[, (nom):=lapply(.SD,function, n=30)]

我尝试使用 Rcpp,但它没有分位数功能。

有没有更快的方法来应用我的功能?

PS:对于一个小表,函数返回:

x<-data.frame(x1=1:8, x2=c(1:7,2000))
x_dt<-data.table(x)
zoo::rollapply(x_dt,5,es_outlier, fill=NA,align='right')

 x1    x2
 NA    NA
 NA    NA
 NA    NA
 NA    NA
 FALSE FALSE
 FALSE FALSE
 FALSE FALSE
 FALSE  TRUE

【问题讨论】:

  • 能否复制行并分配组,即g1=1:30、g2=2:31等,然后在数据表中按组执行es_outlier
  • @rawr,我猜......它需要更少的时间吗?我是数据表的新手,所以我不知道该怎么写。
  • 我不确定,你有几个瓶颈:你的函数、数据的大小和rollapply。 data table 应该可以减少数百万行的大小,并且函数没有改变,所以如果你堆叠数据,似乎可以消除 rollapply 的一个瓶颈?
  • 你也可以通过只调用一次 quantile 来稍微提高你的函数的速度(并且你不使用中值,所以你也可以摆脱它)eso2 &lt;- function(v) {x &lt;- quantile(v, c(.25, .75), na.rm = TRUE); l &lt;- x[2L] + (x[2L] - x[1L]) * 1.5; (abs(v) &gt; l)[length(v)]}跨度>
  • 分析您的代码并检查大部分时间是否在es_outlier。如果是这样,那么将其包装在 data.table 之类的其他内容中将无济于事。

标签: r apply zoo


【解决方案1】:

建议存储已排序的向量,以便在从一个窗口移动到另一个窗口时,只需添加 1 个新元素。虽然仍然不是一个很好的加速..

set.seed(25L)
N <- 50000
dt <- data.frame(x1=runif(N), x2=runif(N))
dt$x1[555] <- 2000
dt$x2[556] <- 2000
wl <- 30

####################################################################################################
#' Calculate IQR for a sorted vector with 30 observations
#' 
#' @details assume that sorted is sorted. using type 7 in ?quantile.
#' 
#' @param sorted sorted numeric vector
#' 
#' @return the interquartile range
#' 
iqr30obs <- function(sorted) {
    c(sorted[8] + 0.25 * (sorted[9] - sorted[8]), sorted[22] + 0.75 * (sorted[23] - sorted[22]))
} #iqr30obs


es_outlier2 <- function(vect) {
    start <- 1
    end <- start + wl - 1
    sorted <- sort(structure(vect[start:end], names=start:end))
    i <- 0
    res <- rep(NA, nrow(dt))
    while (end < nrow(dt)) {  
        locFirstObs <- which(names(sorted)==start)

        if (!(i > 9 && i < 22 && locFirstObs > 9 && locFirstObs < 22)) {
            #changes in the 8th. 9th, 22th and 23th positions after removing first obs 
            #and adding new observation            
            qt <- iqr30obs(sorted)
            iqr1.5 <- 1.5 * (qt[2] - qt[1])
        }
        res[end] <- sorted[as.character(end)] < qt[1] - iqr1.5 |
               sorted[as.character(end)] > qt[2] + iqr1.5

        #moving to next window ----
        #remove the first observation in the window
        sorted <- sorted[-locFirstObs]

        #create the new observation to add to window
        toAdd <- structure(vect[end+1], names=end+1)

        #insert this new observation into the sorted vector while maintaining order
        for (i in seq_along(sorted)) {
            if (toAdd < sorted[i]) {
                sorted <- c(sorted[seq_len(i-1)], toAdd, sorted[i:(wl-1)])
                break
            }
        }
        if (i == length(sorted)) {
            sorted <- c(sorted, toAdd)
        }

        #increment indices
        start <- start + 1
        end <- end + 1
    } #while

    res
} #es_outlier2

es_outlier<-function(vect){
    qq =quantile(vect, prob=c(0.25,0.75), na.rm=T)
    q3=qq[2]
    IC=q3-qq[1]
    limSup=q3+IC*1.5
    vector_final=abs(vect)>limSup
    return(vector_final[length(vect)] )
}

结果:

system.time(es_outlier2(dt$x1))
# user  system elapsed 
# 4.62    0.00    4.67 
system.time(es_outlier2(dt$x2))
# user  system elapsed 
# 4.56    0.00    4.83 

system.time(zoo::rollapply(dt, 30, es_outlier, fill=NA, align='right'))
#   user  system elapsed 
#  17.59    0.01   17.69 

【讨论】:

  • 从不永远发布rm(list=ls())
  • 快速问题:为什么使用 0.25 和 0.75 来获取 c(sorted[8] + 0.25 * (sorted[9] - sorted[8]), sorted[ 22] + 0.75 * (排序[23] - 排序[22]))。我只会做常规平均值。
  • 对于 type = 7,第 25 个分位数是具有 30 个观测值的样本的第 8 阶统计量和 9 阶统计量之间的线性插值。同样适用于第 75 个分位数。或利用计算,查看quantile(1:30, c(0.25,0.75))。常规平均值会为您带来相同的结果吗?如果您更喜欢其他类型,请随时更改iqr30obs 函数
猜你喜欢
  • 1970-01-01
  • 2019-09-11
  • 2019-09-28
  • 1970-01-01
  • 2015-08-28
  • 2019-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多