MATLAB: среднее значение для каждого 1-минутного интервала временного ряда

У меня есть серия временных рядов, каждая из которых описывается двумя компонентами, вектором временной метки (в секундах) и измеренным вектором значений. Вектор времени является неоднородным (т.е. Отбирается с нерегулярными интервалами)

Я пытаюсь вычислить среднее значение /SD для каждого интервала значений в 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 минута была всего лишь примером)

Ответ 1

Единственное логическое решение похоже...

Ok. Мне смешно, что для меня существует только одно логическое решение, но многие другие находят другие решения. Несмотря на это, решение кажется простым. Учитывая векторы 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)) )';

(Обратите внимание, что я отсортировал t выше.)

Я бы сделал это в трех полностью векторизованных строках кода. Во-первых, если разрывы были произвольными и потенциально неравными по интервалу, я бы использовал histc, чтобы определить, какие интервалы попадают в ряды данных. Учитывая, что они однородны, просто сделайте следующее:

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

Опять же, если элементы t не были известны для сортировки, я бы использовал min (t) вместо t (1). Сделав это, используйте накопитель, чтобы уменьшить результаты до среднего и стандартного отклонения.

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

Ответ 2

Вы можете попробовать создать массив ячеек и применить средние значения и std через cellfun. Это на 10% медленнее, чем ваше решение для 900 записей, но ~ 10 раз быстрее для 90000 записей.

[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 2 3 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]), а так как начало интервала не является точно так же.

Ответ 3

Здесь используется способ, который использует двоичный поиск. Это на 6-10 раз быстрее для 9900 элементов и примерно в 64 раза быстрее для 99900 элементов. Было сложно получить надежные времена, используя только 900 элементов, поэтому я не уверен, что быстрее такого размера. При использовании tx непосредственно из сгенерированных данных он почти не использует дополнительную память. Кроме того, у него есть только четыре дополнительные переменные float (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), чтобы найти их так, как вы это делали.

Ответ 4

Вы можете вычислить indices все сразу, используя bsxfun:

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

Это быстрее, чем цикл, но требует сохранения их всех сразу (время против компромиссов пространства).

Ответ 5

Отказ от ответственности: я работал над этим на бумаге, но еще не имел возможности проверить его "в силиконе"...

Возможно, вы сможете избежать циклов или использовать массивы ячеек, выполняя некоторые сложные кумулятивные суммы, индексирование и вычисление средств и стандартных отклонений самостоятельно. Вот какой код, который, как я считаю, будет работать, хотя я не уверен, как он быстро сопоставляется с другими решениями:

[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

Вышеуказанное вычисляет стандартное отклонение, используя упрощение формулы, найденной на этой странице Википедии.

Ответ 6

Тот же ответ, что и выше, но с параметрическим интервалом (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);