Chris Su
Chris Su

Reputation: 543

Sum every n rows of matrix

Is there any way that I can sum up columns values for each group of three rows in a matrix?
I can sum three rows up in a manual way.

For example

% matrix is the one I wanna store the new data.
% data is the original dataset.
matrix(1,1:end) = sum(data(1:3, 1:end))
matrix(2,1:end) = sum(data(4:6, 1:end))
...

But if the dataset is huge, this wouldn't work.
Is there any way to do this automatically without loops?

Upvotes: 4

Views: 7011

Answers (4)

msysmilu
msysmilu

Reputation: 2034

Prashant answered nicely before but I would have a simple amendment:

fl = filterLength;
A = yourVector (where mod(A,fl)==0)
sum(reshape(A,fl,[]),1).'/fl;

There is the ",1" that makes the line run even when fl==1 (original values). I discovered this while running it in a for loop like so:

... read A ...
% Plot data
hold on;

averageFactors = [1 3 10 30 100 300 1000];
colors = hsv(length(averageFactors));
clear legendTxt;


for i=1:length(averageFactors)
% ------ FILTERING ----------
clear Atrunc;
clear ttrunc;
clear B;
fl = averageFactors(i); % filter length
Atrunc = A(1:L-mod(L,fl),:);
ttrunc = t(1:L-mod(L,fl),:);

B = sum(reshape(Atrunc,fl,[]),1).'/fl;
tB = sum(reshape(ttrunc,fl,[]),1).'/fl;
length(B)
plot(tB,B,'color',colors(i,:) )
%kbhit ()
endfor

Upvotes: 0

Amro
Amro

Reputation: 124563

Here are four other ways:

  1. The obligatory for-loop:

    % for-loop over each three rows
    matrix = zeros(size(data,1)/3, size(data,2));
    counter = 1;
    for i=1:3:size(data,1)
        matrix(counter,:) = sum(data(i:i+3-1,:));
        counter = counter + 1;
    end
    
  2. Using mat2cell for tiling:

    % divide each three rows into a cell
    matrix = mat2cell(data, ones(1,size(data,1)/3)*3);
    
    % compute the sum of rows in each cell
    matrix = cell2mat(cellfun(@sum, matrix, 'UniformOutput',false));
    
  3. Using third dimension (based on this):

    % put each three row into a separate 3rd dimension slice
    matrix = permute(reshape(data', [], 3, size(data,1)/3), [2 1 3]);
    
    % sum rows, and put back together
    matrix = permute(sum(matrix), [3 2 1]);
    
  4. Using accumarray:

    % build array of group indices [1,1,1,2,2,2,3,3,3,...]
    idx = floor(((1:size(data,1))' - 1)/3) + 1;
    
    % use it to accumulate rows (appliead to each column separately)
    matrix = cell2mat(arrayfun(@(i)accumarray(idx,data(:,i)), 1:size(data,2), ...
        'UniformOutput',false));
    

Of course all the solution so far assume that the number of rows is evenly divisble by 3.

Upvotes: 5

Prashant Kumar
Prashant Kumar

Reputation: 22669

This one-liner reshapes so that all the values needed for a particular cell are in a column, does the sum, and then reshapes the back to the expected shape.

reshape(sum(reshape(data, 3, [])), [], size(data, 2))

The naked 3 could be changed if you want to sum a different number of rows together. It's on you to make sure the number of rows in each group divides evenly.

Upvotes: 4

aschepler
aschepler

Reputation: 72473

Slice the matrix into three pieces and add them together:

matrix = data(1:3:end, :) + data(2:3:end, :) + data(3:3:end, :);

This will give an error if size(data,1) is not a multiple of three, since the three pieces wouldn't be the same size. If appropriate to your data, you might work around that by truncating data, or appending some zeros to the end.

You could also do something fancy with reshape and 3D arrays. But I would prefer the above (unless you need to replace 3 with a variable...)

Upvotes: 0

Related Questions