4
votes

Is there any easy way to sum up every nth row in Matlab? Lets say I have:

A =

 1     2     3
 4     5     6
 7     8     9
10    11    12
13    14    15
16    17    18

I wish to add every 2 rows, like this: row1+row3+row5, and then row2+row4+row6, so my output is:

B =

21    24    27
30    33    36

I know that it is possible to do it by sum(A(1:2:end,:)) but this is not helpful if you have large matrix and many nth rows, for loop is another option but I couldn't get it working so far. Do you have any suggestions/solutions how this could be solved with a for loop, or maybe there is a built-in function?

6
What's wrong with sum(A(1:2:end,:)) for large matrices? - Divakar
from my example sum(A(1:2:end,:) gives me 21 24 27 whereas to get 30 33 36 i have to use sum(A(2:2:end,:) and if you have many of these nth rows then it becomes a bit tricky. Maybe I was unclear in my question about it. - vaitas
Just curious, how big could be the number of rows and columns in A? - Divakar
@vaitas Though the given solutions are fine, I have included one that is basically a generalization of what you already did. - Dennis Jaheruddin

6 Answers

6
votes

How about that?

B = cell2mat(arrayfun(@(x) sum(A(x:n:end,:)),1:n,'uni',0)')

I first thought about using accumarray but it requires a vector as input. It's still possible, if you follow this answer.

Another accumarray alternative:

[a,b] = size(A);
idx = bsxfun(@plus, 1:b,repmat([0:b:b*n-1]',a/n,1)) 
B = reshape(accumarray(idx(:),A(:)),b,[]).'
4
votes

If you want to avoid arrayfun for a general n case, you can use some reshaping methods. One such approach could be this -

[M,N] = size(A); %// Get the size of A for later usage
rowd = reshape(1:M,n,M/n)'; %// Get new row indices based on every nth selection
A1 = A(rowd(:),:); %// Reshaped A that has all the nth rows packed up consecutively for easing summing up
A2 = reshape(A1,M/n,[]); %// Reshape into a matrix with the number of rows equal to number of rows in each nth grouping
out = reshape(sum(A2),[],N); %// Get the final output by summing across columns and reshaping into the N-column format as with A
4
votes

Benchmark for all solutions

(Thanks to Amro for the Benchmark-code)

function [t,v] = nthSum()
    n = 3; 
    A = randi(100,10000*n,3);

    % functions to compare
    fcns = {
        @() sum1(A,n);
        @() sum2(A,n);
        @() sum3(A,n);
        @() sum4(A,n);
        @() sum5(A,n);
        @() sum6(A,n);
    };

    % timeit
    t = zeros(6,1);
    for ii = 1:100;
        t = t + cellfun(@timeit, fcns);
    end

    % check results
    v = cellfun(@feval, fcns, 'UniformOutput',false);
    assert(isequal(v{:}));
end

function B = sum1(A,n)   %thewaywewalk#1
    [a,b] = size(A);
    idx = bsxfun(@plus, 1:b,repmat([0:b:b*n-1]',a/n,1)); 
    B = reshape(accumarray(idx(:),A(:)),b,[]).';
end

function B = sum2(A,n)  %thewaywewalk#2
    B = cell2mat(arrayfun(@(x) sum(A(x:n:end,:)),1:n,'uni',0)');
end

function B = sum3(A,n) %Dennis Jaheruddin
    B=zeros(n,size(A,2));

    for k=1:n
        B(k,:)=sum(A(k:n:end,:),1);
    end
end

function B = sum4(A,n) %Luis Mendo
    B = squeeze(sum(reshape(permute(A, [1 3 2]), n,[] ,size(A,2)), 2));
end

function B = sum5(A,n) % Bentoy13
    [k,l] = size(A);
    B = sum(reshape([A',zeros(l,mod(-k,n))],l,n,ceil(k/n)),3)';
end

function B = sum6(A,n) % Divakar
    [M,N] = size(A); 
    rowd = reshape(1:M,n,M/n)'; 
    A1 = A(rowd(:),:);
    A2 = reshape(A1,M/n,[]); 
    B = reshape(sum(A2),[],N);
end

Results for 100 runs each and a 30000x3 Matrix A

0.1616s   %// thewaywewalk#1
0.0667s   %// thewaywewalk#2
0.0499s   %// Dennis Jaheruddin
0.0211s   %// Luis Mendo
0.0791s   %// Bentoy13
0.0784s   %// Divakar   

Clear winner & loser, the rest is very close. Especially the most simple solution (for loop) by Dennis is top notch ;)

Interesting how everything changes for large row lengths

Results for 100 runs each and a 3000x1000 Matrix A

6.5646s   %// thewaywewalk#1
2.6314s   %// thewaywewalk#2
2.5939s   %// Dennis Jaheruddin
0.6412s   %// Luis Mendo
4.1996s   %// Bentoy13
1.9975s   %// Divakar

Performance along number of rows

Results averaged on 1000 runs, for input matrix size from 10 * 25 to 1e6 * 25 elements.

enter image description here

Data:

N               10          20          50          100         200         500         1e+03       2e+03       5e+03       1e+04       2e+04       5e+04   1e+05   2e+05   5e+05   1e+06
thewaywewalk #1     0.000282    0.000401    0.000399    0.000341    0.000276    0.000306    0.000358    0.000538    0.00109     0.0015      0.00283     0.0111  0.021   0.0427  0.112   0.224
thewaywewalk #2     7.15e-05    0.000106    0.000129    0.000137    0.000149    0.000262    0.000433    0.000929    0.00313     0.00581     0.0122      0.0289  0.0567  0.121   0.327   0.635
Divakar             3.21e-05    4.36e-05    4.65e-05    4.63e-05    4.52e-05    6.86e-05    0.000116    0.00024     0.000668    0.00179     0.00378     0.00962 0.0193  0.0442  0.116   0.245
Bentoy13            4.58e-05    6.48e-05    7.43e-05    7.31e-05    7.16e-05    0.000103    0.000192    0.000387    0.00115     0.0028      0.00585     0.015   0.0303  0.0623  0.158   0.298
Dennis Jaheruddin   3.76e-05    5.54e-05    6.07e-05    6.25e-05    6.47e-05    0.000114    0.000183    0.000376    0.000999    0.00165     0.00345     0.0162  0.0318  0.0657  0.163   0.326
Luis Mendo          7.44e-05    0.000108    0.00011     9.45e-05    7.83e-05    8.56e-05    0.000102    0.000154    0.000323    0.000452    0.000892    0.00203 0.00374 0.00741 0.0186  0.0368

For small matrices (below 1000 rows), the solution from Divakar is the fastest; for large matrices, the solution from Luis Mendo is clearly the best one.

3
votes

Even if there is already an accepted answer, let's try to do this differently.

The idea here is to reshape the original matrix into a 3D array in order to sum up along one dimension. But we have to take care about the fact that the size of A may not be a multiple of n.

Let's begin with some notations:

[k,l] = size(A);

In order to reshape, we must have k=n*x (x integer). If it's not the case, we must pad A with rows of 0. If k=n*x+y, y being the reminder of the euclidian division of k by n, we have to concatenate n-y rows of 0 to A (or 0 is y==0). Using Matlab mod function, it translates to:

A1 = [A;zeros(mod(-k,n),l)];

Recall that mod(-k,n) is positive.

Now, you want to sum rows and not columns: we want to get a 3D array with rows kept intact, and columns distributed on rows. So I must transpose the array, and reshape it:

A2 = reshape(A1',l,n,ceil(k/n));

Your result is then simply the sum along the 3rd dimension, with transposing back:

B = sum(A2,3)';

As a conclusion, let's build a nearly one-liner (I left k and l apart, for clarity ... ok, for conciseness):

[k,l] = size(A);
B = sum(reshape([A',zeros(l,mod(-k,n))],l,n,ceil(k/n)),3)';
3
votes

Permuting dimensions and reshaping:

B = squeeze(sum(reshape(permute(A, [1 3 2]), n,[],size(A,2)), 2));
1
votes

I still think a for loop would be simple here, and probably reasonably efficient if you have long matrices:

A = [ 1     2     3
 4     5     6
 7     8     9
10    11    12
13    14    15
16    17    18];

n=2;
result=zeros(n,size(A,2));

for k=1:n
   result(k,:)=sum(A(k:n:end,:),1);
end