4

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?

Robert Seifert
  • 25,078
  • 11
  • 68
  • 113
vaitas
  • 85
  • 1
  • 9
  • 2
    What's wrong with `sum(A(1:2:end,:)) for large matrices`? – Divakar May 21 '14 at 07:12
  • 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 May 21 '14 at 07:24
  • Just curious, how big could be the number of rows and columns in `A`? – Divakar May 21 '14 at 08:37
  • @vaitas Though the given solutions are fine, I have included one that is basically a generalization of what you already did. – Dennis Jaheruddin May 21 '14 at 09:14

6 Answers6

6

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,[]).'
Community
  • 1
  • 1
Robert Seifert
  • 25,078
  • 11
  • 68
  • 113
4

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
Divakar
  • 218,885
  • 19
  • 262
  • 358
4

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.

Community
  • 1
  • 1
Robert Seifert
  • 25,078
  • 11
  • 68
  • 113
  • I think this answer would be owned by community wiki. Beside that point, great work; I run myself another benchmark in the same time, and the conclusions are a bit different, I will add info to the post if you allow me to do so. – Bentoy13 May 21 '14 at 11:36
  • @Bentoy13 go ahead ;) – Robert Seifert May 21 '14 at 11:37
  • Thank you. I need a little more time, I rerun my benchmark to add the timing of the solution from Luis Mendo. – Bentoy13 May 21 '14 at 11:43
  • @thewaywewalk Thank you for the results!! I am also curious about if the number of columns are increased too, would it be possible? Or too much of work maybe with two variables? Nevertheless, great thanks! Oh you just did, Kool! – Divakar May 21 '14 at 12:04
  • @Divakar have you seen the last edit, or do you mean something different? – Robert Seifert May 21 '14 at 12:07
  • @thewaywewalk Yeah the last edit has the results that I was looking for, thanks again! – Divakar May 21 '14 at 12:09
  • Thanks @Bentoy13! Your results makes sense I guess! its great and thanks for your efforts too! Guess OP can use a switch based on the datasize :) – Divakar May 21 '14 at 12:46
  • 2
    @thewaywewalk Thanks for the effort! Answers like this make SO great – Luis Mendo May 21 '14 at 21:20
  • you're welcome. Was basically copy&paste :p and the chart is by bentoy! – Robert Seifert May 22 '14 at 08:05
3

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)';
Bentoy13
  • 4,886
  • 1
  • 20
  • 33
3

Permuting dimensions and reshaping:

B = squeeze(sum(reshape(permute(A, [1 3 2]), n,[],size(A,2)), 2));
Luis Mendo
  • 110,752
  • 13
  • 76
  • 147
1

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
Dennis Jaheruddin
  • 21,208
  • 8
  • 66
  • 122