2013-05-30 13 views
1

在滑动窗口内计算元素总和的最佳R语言是什么?滑动窗口的精度高于diff(cumsum(...))

概念欲以下:

for (i in 1:(length(input) - lag + 1)) 
    output[i] <- sum(input[i:(i + lag - 1)]) 

换言之,每个输出元件应该是一个固定数量的输入元件(称为lag这里)的总和,从而产生适当短结果向量。我知道我理论上可以这样写:

output = diff(cumsum(c(0, input)), lag = lag) 

但我很担心这里的精度。我有一个设置记住所有的值都具有相同的符号,并且向量会很大。预先总结数值可能会导致数量偏大,因此个体差异不会留下许多有效数字。这感觉很糟糕。

我会想,应该可以做得比这更好,至少在使用单个函数而不是两个时。一个实现可以保持当前的总和,每个迭代添加一个元素并减去另一个元素。既然这样会一直累积舍入误差,我们可以从两端分别进行计算,如果中心的结果太远,则从中心计算一个新的结果,从而提高分水岭的精度,征服的方法。

你知道任何类似的东西吗?
或者有没有理由不这样做,因为我认为它应该?
或者为什么diff(cumsum(…))的方法不像看起来那么糟糕?


编辑:我有一些关闭的情况的一个错误在我上面的配方,使他们不一致。现在他们似乎同意测试数据。 lag应该是总结的元素的数量,因此我期望一个更短的向量。我不处理时间序列对象,所以绝对时间对齐与我无关。

我在我的真实数据中看到过一些看起来很嘈杂的东西,我认为这是由于这样的数字问题。由于使用不同的方法来计算这些值,使用不同的答案和评论的建议,仍然导致类似的结果,可能我的数据的奇怪实际上不是由于数字问题。

所以为了评价答案,我用下面的设置:

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] 
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 

结果表明:diff + cumsum仍然是令人惊讶的准确。 (在我想加入第二个runif载体之前,它显得更加准确。)filtercaTools两者几乎都没有完美的结果。至于表现,我还没有测试(尚)。我只知道有128位的Rmpfrcumsum很慢,我不想等到它完成。如果您有性能基准或新的建议添加到比较中,请随时编辑此问题。

+2

[caTools](http://cran.r-project.org/web/packages/caTools/)包有一些扩展的精度和。 –

+1

Rmpfr或gmp是否有'cumsum'方法? –

+0

@JoshuaUlrich:'caTools'看起来像一个有价值的参考资料,这是因为它的实现使得某些声明是正确的,并且因为它有一个相当大的“另请参见”列表,因此它是查找其他实现的好起点。你会做出答案吗? 'lag * runmean(input,lag,alg =“exact”,endrule =“trim”)'看起来与我所期望的相符。 'alg =“fast”似乎给了'diff' +'cumsum'更糟糕的结果。 – MvG

回答

1

此答案是根据the commentJoshua Ulrich

封装caTools提供了一个函数runmean,其计算我的部分和,由窗口大小(或更确切地说不可─NA元件在所讨论的窗口的数量)划分。从其文档引用:

runmean(..., alg="exact")函数的情况下,使用特殊算法(请参阅参考资料部分)以确保舍入误差不会累积。因此runmeanfilter(x, rep(1/k,k))runmean(..., alg="C")功能更准确。

功能runmean(..., alg="exact")通过由瓦迪姆Ogranovich,其基于Python代码(见last reference)基于代码,由伽柏格罗滕迪克指出。

参考

:Shewchuk,乔纳森 Adaptive Precision Floating-Point Arithmetic and Fast Robust Geometric Predicates
  • 更多四舍五入误差校正,可以发现
  • 代码存储了cu rrent窗口使用双精度浮点值序列,其中较小的值表示较大元素引起的舍入误差。因此,即使输入数据一次处理,添加一个元素并在每个步骤移除另一个元素,也不应该有舍入错误的累积。最终的结果应该和双精度算法一样精确。

    虽然exact以外的算法似乎产生了不同的结果,所以我可能不会建议这些。

    这似乎有点不幸,源代码包含一个runsum_exact函数,但它被注释掉了。求平均值的分割,再加上乘法以回到总和,将引入本来可以避免的舍入误差。为了这个CHANGES文件说:

    11)caTools 1.11(2010年12月)

    • 完全退休runsum.exact,这是不工作了一段时间,使用runmean以 “精确” 的选项,而不是。

    目前(caTools版本1.14从2012-05-22)出现在包被孤立。

    1

    我不能到这是否是这样的实现说话,但

    filter(input, sides=2, filter=rep(1, lag+1)) 
    

    看着身体filter,它看起来像辛勤工作得到将传递给C例程,C_rfilter,所以也许你可以检查它是否满足你的精度要求。否则,@ JoshuaUlrich的建议听起来很有希望。

    +0

    如果C例程不包含扩展精度算法,则这仍然可能存在精度问题。 –

    +0

    好吧,如果C代码会简单地总结每个结果的“滞后”个体值,这是人们可能天真地期待的方式,那么这会比基于diff的方法好很多。在实践中,我发现'filter(input,rep(1,lag),sides = 1)[lag:length(input)]'给出了我期望的格式的结果。我现在看到,我的描述在两种情况下都有一种情况,将不得不编辑我的问题。对不起,详细信息。至于数值,在我的测试数据中,结果看起来与'diff'相当,这可能表明我奇怪的结果并不是由于我假设的数字原因。查看编辑的问题。 – MvG