MATLAB:计算时间序列的每1分钟间隔的平均值

时间:2010-02-24 01:19:07

标签: matlab time-series vectorization

我有一堆时间序列,每个时间序列由两个组件描述,一个时间戳向量(以秒为单位),以及一个测量值向量。时间矢量是不均匀的(即以非规则间隔采样)

我正在尝试计算每个1分钟间隔值的平均值/标准差(采用X分钟间隔,计算其平均值,采用下一个间隔,......)。

我当前的实现使用循环。这是我到目前为止的样本:

t = (100:999)' + rand(900,1);       %' non-uniform time
x = 5*rand(900,1) + 10;             % x(i) is the value at time t(i)

interval = 1;         % 1-min interval
tt = ( floor(t(1)):interval*60:ceil(t(end)) )';  %' stopping points of each interval
N = length(tt)-1;

mu = zeros(N,1);
sd = zeros(N,1);

for i=1:N
    indices = ( tt(i) <= t & t < tt(i+1) ); % find t between tt(i) and tt(i+1)
    mu(i) = mean( x(indices) );
    sd(i) = std( x(indices) );
end

我想知道是否有更快的矢量化解决方案。这很重要,因为我有大量的时间序列来处理每个时间序列的时间比上面显示的样本长得多。

欢迎任何帮助。


谢谢大家的反馈。

我纠正了生成t的方式总是单调增加(排序),这不是一个真正的问题..

另外,我可能没有明确说明这一点,但我的意图是在几分钟内找到任何间隔长度的解决方案(1分钟只是一个例子)

6 个答案:

答案 0 :(得分:11)

唯一合乎逻辑的解决方案似乎是......

确定。我觉得有趣的是,对我来说,只有一个逻辑解决方案,但许多其他人找到其他解决方案。无论如何,解决方案似乎很简单。给定向量x和t,以及一组等间隔的断点tt,

t = sort((100:999)' + 3*rand(900,1));     % non-uniform time
x = 5*rand(900,1) + 10;             % x(i) is the value at time t(i)

tt = ( floor(t(1)):1*60:ceil(t(end)) )';

(注意我在上面排序了。)

我会在三个完全矢量化的代码行中执行此操作。首先,如果断点是任意的并且间距可能不相等,我会使用histc来确定数据序列属于哪个区间。如果它们是统一的,只需这样做:

int = 1 + floor((t - t(1))/60);

同样,如果不知道t的元素是否被排序,我会使用min(t)而不是t(1)。完成后,使用accumarray将结果降低到平均值和标准差。

mu = accumarray(int,x,[],@mean);
sd = accumarray(int,x,[],@std);

答案 1 :(得分:4)

您可以尝试创建一个单元格数组,并通过cellfun应用mean和std。它比900条目的解决方案慢了约10%,但90000条目的速度提高了约10倍。

[t,sortIdx]=sort(t); %# we only need to sort in case t is not monotonously increasing
x = x(sortIdx);

tIdx = floor(t/60); %# convert seconds to minutes - can also convert to 5 mins by dividing by 300
tIdx = tIdx - min(tIdx) + 1; %# tIdx now is a vector of indices - i.e. it starts at 1, and should go like your iteration variable.

%# the next few commands are to count how many 1's 2's 3's etc are in tIdx
dt = [tIdx(2:end)-tIdx(1:end-1);1]; 
stepIdx = [0;find(dt>0)];
nIdx = stepIdx(2:end) - stepIdx(1:end-1); %# number of times each index appears

%# convert to cell array
xCell = mat2cell(x,nIdx,1);

%# use cellfun to calculate the mean and sd
mu(tIdx(stepIdx+1)) = cellfun(@mean,xCell); %# the indexing is like that since there may be missing steps
sd(tIdx(stepIdx+1)) = cellfun(@mean,xCell);

注意:我的解决方案没有给出与你的完全相同的结果,因为你在最后跳过几个时间值(1:60:90是[1,61]),并且因为间隔的开始不是完全一样。

答案 2 :(得分:3)

这是一种使用binary search的方式。 9900元素的速度提高了6-10倍,99900元素的速度提高了64倍。仅使用900个元素很难获得可靠的时间,所以我不确定哪个更快。如果考虑直接从生成的数据中生成tx,它几乎不使用额外的内存。除此之外,它只有四个额外的浮点变量(prevind,first,mid和last)。

% Sort the data so that we can use binary search (takes O(N logN) time complexity).
tx = sortrows([t x]);

prevind = 1;

for i=1:N
    % First do a binary search to find the end of this section
    first = prevind;
    last = length(tx);
    while first ~= last
        mid = floor((first+last)/2);
        if tt(i+1) > tx(mid,1)
            first = mid+1;
        else
            last = mid;
        end;
    end;
    mu(i) = mean( tx(prevind:last-1,2) );
    sd(i) = std( tx(prevind:last-1,2) );
    prevind = last;
end;

它使用您最初的所有变量。我希望它符合您的需求。它更快,因为它需要O(log N)来查找带有二进制搜索的索引,但是O(N)可以按照您的方式找到它们。

答案 3 :(得分:2)

您可以使用bsxfun一次计算indices

indices = ( bsxfun(@ge, t, tt(1:end-1)') & bsxfun(@lt, t, tt(2:end)') );

这比循环更快但需要一次性存储它们(时间与空间权衡)..

答案 4 :(得分:2)

免责声明:我在纸上做了这个,但还没有机会在“电子计算机”中检查它......

您可以通过执行一些棘手的累积求和,索引以及自己计算均值和标准差来避免循环或使用单元格数组。这里有一些我认为可以使用的代码,虽然我不确定它如何快速地与其他解决方案叠加:

[t,sortIndex] = sort(t);  %# Sort the time points
x = x(sortIndex);         %# Sort the data values
interval = 60;            %# Interval size, in seconds

intervalIndex = floor((t-t(1))./interval)+1;  %# Collect t into intervals
nIntervals = max(intervalIndex);              %# The number of intervals
mu = zeros(nIntervals,1);                     %# Preallocate mu
sd = zeros(nIntervals,1);                     %# Preallocate sd

sumIndex = [find(diff(intervalIndex)) ...
            numel(intervalIndex)];  %# Find indices of the interval ends
n = diff([0 sumIndex]);             %# Number of samples per interval
xSum = cumsum(x);                   %# Cumulative sum of x
xSum = diff([0 xSum(sumIndex)]);    %# Sum per interval
xxSum = cumsum(x.^2);               %# Cumulative sum of x^2
xxSum = diff([0 xxSum(sumIndex)]);  %# Squared sum per interval

intervalIndex = intervalIndex(sumIndex);  %# Find index into mu and sd
mu(intervalIndex) = xSum./n;                             %# Compute mean
sd(intervalIndex) = sqrt((xxSum-xSum.*xSum./n)./(n-1));  %# Compute std dev

以上使用the simplification of the formula found on this Wikipedia page计算标准偏差。

答案 5 :(得分:0)

与上述答案相同,但参数区间(window_size)。 解决了矢量长度的问题。

window_size = 60; % but it can be any value 60 5 0.1, which wasn't described above

t = sort((100:999)' + 3*rand(900,1));     % non-uniform time
x = 5*rand(900,1) + 10;                   % x(i) is the value at time t(i)

int = 1 + floor((t - t(1))/window_size);
tt = ( floor(t(1)):window_size:ceil(t(end)) )';



% mean val and std dev of the accelerations at speed
mu = accumarray(int,x,[],@mean);
sd = accumarray(int,x,[],@std);

%resolving some issue with sizes (for i.e. window_size = 1 in stead of 60)
while ( sum(size(tt) > size(mu)) > 0 ) 
  tt(end)=[]; 
end

errorbar(tt,mu,sd);