2009-05-14 8 views
3

У меня есть код, который работает, но это немного узкое место, и я застреваю, пытаясь понять, как ускорить его. Это в цикле, и я не могу понять, как его векторизовать.Bucketing Algorithm

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

allDts; %Dates/times for vals. Size is [size(vals, 1), 1] 
vals; 
[Y M] = datevec(allDts); 
fomDates = unique(datenum(Y, M, 1)); %first of the month dates 

[Y M] = datevec(fomDates); 
nextFomDates = datenum(Y, M, DateUtil.monthLength(Y, M)+1); 

newVals = nan(length(fomDates), size(vals, 2)); %preallocate for speed 

for k = 1:length(fomDates); 

Это следующая строка является узким местом, потому что я называю это так много раз (зацикливание)

idx = (allDts >= fomDates(k)) & (allDts < nextFomDates(k)); 
    bucketed = vals(idx, :); 
    newVals(k, :) = nansum(bucketed); 
end %for 

Любые идеи.? Заранее спасибо.

+0

Это должно быть сделано с помощью [ 'accumarray'] (https://www.mathworks.com/help/matlab/ref/accumarray.html) ... –

ответ

2

Это сложная задача для векторизации. Я могу предложить способ сделать это, используя CELLFUN, но я не могу гарантировать, что он будет быстрее для вашей проблемы (вам придется самому времени на конкретных наборах данных, которые вы используете). Как обсуждалось в this other SO question, векторизация не всегда работает быстрее, чем для циклов. Это может быть очень проблемным, что является лучшим вариантом. С этим отказом я предлагаю вам два решения: версию CELLFUN и модификацию вашей версии for-loop, которая может работать быстрее.

CELLFUN РЕШЕНИЕ:

[Y,M] = datevec(allDts); 
monthStart = datenum(Y,M,1); % Start date of each month 
[monthStart,sortIndex] = sort(monthStart); % Sort the start dates 
[uniqueStarts,uniqueIndex] = unique(monthStart); % Get unique start dates 

valCell = mat2cell(vals(sortIndex,:),diff([0 uniqueIndex])); 
newVals = cellfun(@nansum,valCell,'UniformOutput',false); 

Вызов к MAT2CELL группы ряды вальса, которые имеют ту же самую дату начала вместе в клетки массива клеток valCell. Переменная newVals будет массив ячеек длины numel (uniqueStarts), где каждая ячейка будет содержать результат выполнения nansum на соответствующей ячейке valCell.

FOR-LOOP РЕШЕНИЕ:

[Y,M] = datevec(allDts); 
monthStart = datenum(Y,M,1); % Start date of each month 
[monthStart,sortIndex] = sort(monthStart); % Sort the start dates 
[uniqueStarts,uniqueIndex] = unique(monthStart); % Get unique start dates 

vals = vals(sortIndex,:); % Sort the values according to start date 
nMonths = numel(uniqueStarts); 
uniqueIndex = [0 uniqueIndex]; 
newVals = nan(nMonths,size(vals,2)); % Preallocate 
for iMonth = 1:nMonths, 
    index = (uniqueIndex(iMonth)+1):uniqueIndex(iMonth+1); 
    newVals(iMonth,:) = nansum(vals(index,:)); 
end 
+0

Спасибо. Это ускоряет его примерно на 50%! Если я правильно понял код, эта строка: valCell = mat2cell (vals, diff ([0; uniqueIndex])); - это ключ - он разбивает значения на ячейки, которые являются длиной второго аргумента. (Не нужно сортировать - даты и связанные с ними значения гарантированно будут сортироваться – Marc

+0

Yup, похоже, что у вас есть это. Второй аргумент MAT2CELL - это вектор размеров, который будет содержать строки первого аргумента Например, если первым аргументом является матрица 6x3 (называемая A), а второй аргумент - [1 2 3], то MAT2CELL вернет 3-элементный массив ячеек (называемый B), равный следующему: B = {A (1, :); A (2: 3, :); A (4: 6, :)} – gnovice

0

Если все, что вам нужно сделать, это сформировать сумму или среднее значение по строкам матрицы, где строки суммируются в зависимости от другой переменной (дата), а затем использовать моя функция консолидатора. Он предназначен для выполнения именно этой операции, уменьшая данные на основе значений индикаторной серии. (На самом деле, консолидирующей также может работать на го данных, а также с допуском, но все, что вам нужно сделать, это передать его месяц и год информацию.)

Find consolidator on the file exchange on Matlab Central