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);