可以`ddply`(或类似的)做一个滑动窗口?

Owe*_*wen 15 r plyr

就像是

sliding = function(df, n, f)
    ldply(1:(nrow(df) - n + 1), function(k)
        f(df[k:(k + n - 1), ])
    )
Run Code Online (Sandbox Code Playgroud)

就像那样使用

> df
  n         a
1 1 0.8021891
2 2 0.9446330
...

> sliding(df, 2, function(df) with(df,
+     data.frame(n = n[1], a = a[1], b = sum(n - a))
+ ))
  n         a        b
1 1 0.8021891 1.253178
...
Run Code Online (Sandbox Code Playgroud)

直接内部ddply,以便我可以得到它附带的漂亮的语法糖?

Joh*_*lby 9

由于这个问题没有找到答案,我认为我会提出一个问题,即实际上还有一种更好的方法可以解决这类问题 - 一个也可能有数千次问题更快.(如果这没用,请告诉我,但我认为这比没有更好)

每当我听到"移动平均"或"滑动窗口"时,立即就会出现FFT卷积.这是因为它能够以极其有效的方式处理这些类型的问题.由于所有的"滑动"都是在幕后完成的,我认为它也具有你可以要求的所有语法美.

(以下代码位于https://gist.github.com/1320175的一个文件中)

我们首先模拟一些数据(为了简单起见,我在这里使用整数,但当然你不需要).

require(plyr)
set.seed(12345)

n = 10
n.sum = 2
a = sample.int(10, n, replace=T)

df = data.frame(n=1:n, a)
Run Code Online (Sandbox Code Playgroud)
> df
    n  a
1   1  8
2   2  9
3   3  8
4   4  9
5   5  5
6   6  2
7   7  4
8   8  6
9   9  8
10 10 10
Run Code Online (Sandbox Code Playgroud)

现在,我们将n-a一次性预先计算.

n.minus.a = with(df, n - a)
Run Code Online (Sandbox Code Playgroud)

接下来,定义一个内核 k,当与我们的输入卷积时n.minus.a,它将对我们的数据进行求和(或平均/平滑/其他任何).

k = rep(0, n)
k[1:n.sum] = 1
Run Code Online (Sandbox Code Playgroud)

设置好所有内容后,我们可以定义一个函数,在频域中有效地进行卷积fft().

myConv <- function(x, k){
  Fx  = fft(x)
  Fk  = fft(k)
  Fxk = Fx * Fk
  xk  = fft(Fxk, inverse=T)
  (Re(xk) / n)[-(1:(n.sum-1))]
}
Run Code Online (Sandbox Code Playgroud)

执行此操作的语法很简单:

> myConv(n.minus.a, k)
[1] -14 -12 -10  -5   4   7   5   3   1
Run Code Online (Sandbox Code Playgroud)

当你convolve()在R中使用便利功能时,这一切也发生在引擎盖下.

> convolve(n.minus.a, k)[1:(length(n.minus.a)-n.sum+1)]
[1] -14 -12 -10  -5   4   7   5   3   1
Run Code Online (Sandbox Code Playgroud)

我们现在将其与手动方法进行比较,以显示结果都是等效的:

> sliding(df, 2, function(df) with(df, data.frame(n = n[1], a = a[1], b = sum(n - a))))
  n a   b
1 1 8 -14
2 2 9 -12
3 3 8 -10
4 4 9  -5
5 5 5   4
6 6 2   7
7 7 4   5
8 8 6   3
9 9 8   1
Run Code Online (Sandbox Code Playgroud)

最后,我们将制作n=10^4并测试所有这些方法的速度:

> system.time(myConv(n.minus.a, k))
   user  system elapsed 
  0.002   0.000   0.002 
> system.time(convolve(n.minus.a, k, type='circ')[1:(length(n.minus.a)-n.sum+1)])
   user  system elapsed 
  0.002   0.000   0.002 
> system.time(sliding(df, 2, function(df) with(df, data.frame(n = n[1], a = a[1], b = sum(n - a)))))
   user  system elapsed 
  7.944   0.018   7.962 
Run Code Online (Sandbox Code Playgroud)

FFT方法几乎瞬间恢复,即使在这个粗略的时间,也比手动方法快了近4000倍.

当然,并不是每种滑动问题都可以进入这种范式,但对于像这样的数值问题sum()(也就是加权平均值等),它可以完美地运行.无论如何,至少谷歌有点值得一看,看看是否有可用的过滤内核可以解决给定的问题.祝好运!