如何(有效地)计算向量的移动平均值?

时间:2014-11-17 20:34:45

标签: matlab moving-average

我有一个矢量,我想计算它的移动平均值(使用宽度为5的窗口)。

例如,如果有问题的向量是[1,2,3,4,5,6,7,8],那么

  • 结果向量的第一个条目应该是[1,2,3,4,5]中所有条目的总和(即15);
  • 结果向量的第二个条目应该是[2,3,4,5,6]中所有条目的总和(即20);

最后,结果向量应为[15,20,25,30]。我怎么能这样做?

3 个答案:

答案 0 :(得分:18)

conv功能就在你的小巷里:

>> x = 1:8;
>> y = conv(x, ones(1,5), 'valid')

y =
    15    20    25    30

基准

三个答案,三种不同的方法......这是一个使用timeit的快速基准(不同的输入大小,固定窗口宽度为5);如果您认为需要改进,可以随意在其中挖洞(在评论中)。

conv成为最快的方法;它的速度约为coin's approach (using filter)的两倍,约为Luis Mendo's approach (using cumsum)的四倍。

enter image description here

这是另一个基准(固定输入大小1e4,不同的窗口宽度)。在这里,Luis Mendo's cumsum approach成为明显的赢家,因为它的复杂性主要取决于输入的长度,并且对窗口的宽度不敏感。

enter image description here

结论

总结一下,你应该

  • 如果您的窗口相对较小,请使用conv方法,
  • 如果您的窗口比较大,请使用cumsum方法。

代码(用于基准测试)

function benchmark

    clear all
    w = 5;                 % moving average window width
    u = ones(1, w); 
    n = logspace(2,6,60);  % vector of input sizes for benchmark
    t1 = zeros(size(n));   % preallocation of time vectors before the loop
    t2 = t1;
    th = t1;

    for k = 1 : numel(n)

        x = rand(1, round(n(k)));  % generate random row vector

        % Luis Mendo's approach (cumsum)
        f = @() luisMendo(w, x);
        tf(k) = timeit(f);

        % coin's approach (filter)
        g = @() coin(w, u, x);
        tg(k) = timeit(g);

        % Jubobs's approach (conv)
        h = @() jubobs(u, x);
        th(k) = timeit(h);
    end

    figure
    hold on
    plot(n, tf, 'bo')
    plot(n, tg, 'ro')
    plot(n, th, 'mo')
    hold off
    xlabel('input size')
    ylabel('time (s)')
    legend('cumsum', 'filter', 'conv')

end

function y = luisMendo(w,x)
    cs = cumsum(x);
    y(1,numel(x)-w+1) = 0; %// hackish way to preallocate result
    y(1) = cs(w);
    y(2:end) = cs(w+1:end) - cs(1:end-w);
end

function y = coin(w,u,x)
    y = filter(u, 1, x);
    y = y(w:end);
end

function jubobs(u,x)
    y = conv(x, u, 'valid');
end

function benchmark2

    clear all
    w = round(logspace(1,3,31));    % moving average window width 
    n = 1e4;  % vector of input sizes for benchmark
    t1 = zeros(size(n));   % preallocation of time vectors before the loop
    t2 = t1;
    th = t1;

    for k = 1 : numel(w)
        u = ones(1, w(k));
        x = rand(1, n);  % generate random row vector

        % Luis Mendo's approach (cumsum)
        f = @() luisMendo(w(k), x);
        tf(k) = timeit(f);

        % coin's approach (filter)
        g = @() coin(w(k), u, x);
        tg(k) = timeit(g);

        % Jubobs's approach (conv)
        h = @() jubobs(u, x);
        th(k) = timeit(h);
    end

    figure
    hold on
    plot(w, tf, 'bo')
    plot(w, tg, 'ro')
    plot(w, th, 'mo')
    hold off
    xlabel('window size')
    ylabel('time (s)')
    legend('cumsum', 'filter', 'conv')

end

function y = luisMendo(w,x)
    cs = cumsum(x);
    y(1,numel(x)-w+1) = 0; %// hackish way to preallocate result
    y(1) = cs(w);
    y(2:end) = cs(w+1:end) - cs(1:end-w);
end

function y = coin(w,u,x)
    y = filter(u, 1, x);
    y = y(w:end);
end

function jubobs(u,x)
    y = conv(x, u, 'valid');
end

答案 1 :(得分:3)

另一种可能性是使用cumsum。这种方法可能需要的操作少于conv

x = 1:8
n = 5;
cs = cumsum(x);
result = cs(n:end) - [0 cs(1:end-n)];

要节省一点时间,您可以用

替换最后一行
%// clear result
result(1,numel(x)-n+1) = 0; %// hackish way to preallocate result
result(1) = cs(n);
result(2:end) = cs(n+1:end) - cs(1:end-n);

答案 2 :(得分:2)

如果您想保留输入向量的大小,建议您使用filter

>> x = 1:8;
>> y = filter(ones(1,5), 1, x)

y =
     1     3     6    10    15    20    25     30

>> y = (5:end)

y =
     15    20    25    30