Суммирование на основе уникальных записей двух массивов |Скорость выпуска - PullRequest
2 голосов
/ 01 ноября 2019

У меня есть 3 массива размером 803500 * 1 со следующими данными:

Rid: может содержать любое число
RidID: содержит элементы от 1 до 184 в случайном порядке. Каждый элемент появляется несколько раз.
r: Содержит элементы 0,1,2, ... 12. Все элементы (кроме нуля) встречаются почти от 3400 до 3700 раз при случайных индексах в этом массиве.

Для генерации выборочных данных может быть полезно следующее:

Rid = rand(803500,1);
RidID = randi(184,803500,1);
r = randi(13,803500,1)-1;  %This may not be a good sample for r as per previously mentioned details? 

Что я хочуdo? Я хочу вычислить сумму тех записей Rid, которые соответствуют каждой положительной уникальной записи r и каждой уникальной записи RidID. Это может быть понятнее с кодом, который я написал для этой проблемы:

RNum = numel(unique(RidID));
RSum = ones(RNum,12); %Preallocating for better speed
for i=1:12
    RperM = r ==i;
    for j = 1:RNum 
        RSum(j,i)  = sum(Rid(RperM & (RidID==j)));
    end
end

Проблема: Мой код работает, но на моем компьютере это занимает в среднем 5 секунд, и я должен это сделатьрасчет почти тысячу раз. Если это время сократится с 5 секунд до хотя бы половины, я буду очень счастлив. Но как мне это оптимизировать? Я не против, если это сделано лучше с векторизацией или любым лучшим написанным циклом.

Я использую MATLAB R2017b.

Ответы [ 2 ]

4 голосов
/ 01 ноября 2019

Вы можете использовать accumarray :

u  = unique(RidID);
A = accumarray([RidID r+1], Rid);
RSum = A(u, 2:13);
2 голосов
/ 01 ноября 2019

Это медленнее, чем accumarray, как предлагает от rahnema, но использование findgroups и splitapply может сэкономить память.

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

RidID    | r    | Rid_sum
-------------------------
1        | 1    | 100
2        | 1    | 200
4        | 2    | 85
...

Это может быть достигнуто с помощью следующего кода:

[ID, rn, RidIDn] = findgroups(r,RidID); % Get unique combo ID for 'r' and 'RidID'
RSum = splitapply( @sum, Rid, ID );     % Sum for each ID
output = table( RidIDn, rn, RSum );     % Nicely formatted table output
% Get rid of elements where r == 0
output( output.rn == 0, : ) = [];

Вы можете преобразовать это в тот же выводкак метод accumarray, но это уже более медленный метод ...

% Convert to 'unstacked' 2D matrix (optional)
RSum = full( sparse( 1:numel(Ridn), 1:numel(rn), RSum ) );
...