Sliding window with higher precision than diff(cumsum(…))
在滑动窗口中计算元素总和的最佳R惯用法是什么?
从概念上讲,我需要以下内容:
1 2 | for (i in 1:(length(input) - lag + 1)) output[i] <- sum(input[i:(i + lag - 1)]) |
换句话说,每个输出元素应该是固定数量的输入元素的总和(此处称为
1 | output = diff(cumsum(c(0, input)), lag = lag) |
但是我担心这里的精度。我想到了一个设置,其中所有值都具有相同的符号,向量将非常大。预先汇总值可能会导致大量数字,因此不会有很多有效数字可用于处理个体差异。感觉不好。
我想至少在使用单个函数而不是两个函数时,应该可以做得更好。一个实现可以维持当前的总和,无需为每个迭代添加一个元素,然后减去另一个元素。由于这样做仍然会沿途累积舍入误差,因此可以从两端分别进行计算,并且如果中心处的结果相距太远,则可以从中心处计算出一个新的结果,从而提高除法和除法的精度征服方法。
您知道有什么实现这样的实现的吗?
还是有原因导致此功能无法正常运行?
也许是
编辑:我在上述公式中犯了一些错误,使它们不一致。现在他们似乎同意测试数据。
我在真实数据中看到了一些看起来很吵的东西,我认为这是由于此类数字问题造成的。由于使用答案和评论中的不同建议来计算这些值的几种不同方法仍然导致相似的结果,因此可能是我的数据的奇怪性实际上并非由于数字问题。
因此,为了评估答案,我使用了以下设置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | library(Rmpfr) library(caTools) len <- 1024*1024*8 lag <- 3 precBits <- 128 taillen <- 6 set.seed(42) # reproducible input <- runif(len) input <- input + runif(len, min=-1e-9, max=1e-9) # use >32 bits options(digits = 22) # Reference: sum everything separately using high precision. output <- mpfr(rep(0, taillen), precBits = precBits) for (i in 1:taillen) output[i] <- sum(mpfr(input[(len-taillen+i-lag+1):(len-taillen+i)], precBits=precBits)) output addResult <- function(data, name) { n <- c(rownames(resmat), name) r <- rbind(resmat, as.numeric(tail(data, taillen))) rownames(r) <- n assign("resmat", r, parent.frame()) } # reference solution, rounded to nearest double, assumed to be correct resmat <- matrix(as.numeric(output), nrow=1) rownames(resmat) <-"Reference" # my original solution addResult(diff(cumsum(c(0, input)), lag=lag),"diff+cumsum") # filter as suggested by Matthew Plourde addResult(filter(input, rep(1, lag), sides=1)[lag:length(input)],"filter") # caTools as suggested by Joshua Ulrich addResult(lag*runmean(input, lag, alg="exact", endrule="trim"),"caTools") |
结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | [,1] [,2] Reference 2.380384891521345469556 2.036472557725210297264 diff+cumsum 2.380384892225265502930 2.036472558043897151947 filter 2.380384891521345469556 2.036472557725210741353 caTools 2.380384891521345469556 2.036472557725210741353 [,3] [,4] Reference 1.999147923481302324689 1.998499369297661143463 diff+cumsum 1.999147923663258552551 1.998499369248747825623 filter 1.999147923481302324689 1.998499369297661143463 caTools 1.999147923481302324689 1.998499369297661143463 [,5] [,6] Reference 2.363071143676507723796 1.939272651346203080180 diff+cumsum 2.363071143627166748047 1.939272651448845863342 filter 2.363071143676507723796 1.939272651346203080180 caTools 2.363071143676507723796 1.939272651346203080180 |
结果表明
此答案基于约书亚·乌尔里希(Joshua Ulrich)的评论。
caTools软件包提供了一个函数
In case of
runmean(..., alg="exact") function a special algorithm is used (see references section) to ensure that round-off errors do not accumulate. As a resultrunmean is more accurate thanfilter(x, rep(1/k,k)) andrunmean(..., alg="C") functions.Note:
Function
runmean(..., alg="exact") is based by code by Vadim Ogranovich, which is based on Python code (see last reference), pointed out by Gabor Grothendieck.References:
- About round-off error correction used in
runmean : Shewchuk, Jonathan
Adaptive Precision Floating-Point Arithmetic and Fast Robust Geometric Predicates- More on round-off error correction can be found at:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/393090
代码使用一系列双精度浮点值存储当前窗口的总和,其中较小的值表示较大元素引起的舍入误差。因此,即使一次处理输入数据,在每个步骤中添加一个元素并删除另一个元素,也不应有任何舍入误差的累积。最终结果应与双精度算术可以表示的结果一样精确。
除
不幸的是,源代码包含一个
11) caTools-1.11 (Dec 2010)
- Fully retired runsum.exact, which was not working for a while, use runmean with"exact" option instead.
此刻(2012年5月22日的caTools版本1.14)该软件包似乎是孤立的。
我不能说这是否是一个实现,但是有
1 | filter(input, sides=2, filter=rep(1, lag+1)) |
看一下