Mehrdad
Mehrdad

Reputation: 171

Adding up different number of elements from each row

I have a main matrix, say

A=magic(5);

and also a vector

v=[1;3;5;2;2];

I want to add up row-wise the elements of A in this way: add first row from the v(1)st element to the end, second row from the v(2)rd element to the end, third row from the v(3)th element to the end, and so on.

I know that I can do this using for-loop. But I want to know if there is a vectorized way to do it.

edit: Let me clarify my question with an example: Assume A and v as above.

A =

 17    24     1     8    15
 23     5     7    14    16
  4     6    13    20    22
 10    12    19    21     3
 11    18    25     2     9

and

v =

 1
 3
 5
 2
 2

Now I want a way to get the following results:

answer =
 65  % 17+24+1+8+15
 37  % 7+14+16
 22  % 22
 55  % 12+19+21+3
 54  % 18+25+2+9

Upvotes: 4

Views: 247

Answers (2)

Rody Oldenhuis
Rody Oldenhuis

Reputation: 38032

I know it's kinda cheating, but how about:

S = arrayfun(@(ii) sum(A(ii, v(ii):end)), 1:size(A,1)).';

Usually I'm a bit weary when using arrayfun, but when comparing:

% sample data
N = 5000;

A = magic(N);
v = randi(N, N,1);


% simple loop
tic
    S = zeros(N,1);
    for ii = 1:N
        S(ii) = sum(A(ii, v(ii):end));
    end
toc


% arrayfun solution    
tic
    S = arrayfun(@(ii) sum(A(ii, v(ii):end)), 1:N).';
toc

% Shai's solution 
tic
    [m n] = size(A);
    fA = fliplr(A);
    fv = n + 1 - v; 
    csA = cumsum( fA, 2 ); 
    res = csA( sub2ind( [m n], 1:m, fv.' ) ).';
toc

Results:

Elapsed time is 0.386280 seconds. % simple loop
Elapsed time is 0.473916 seconds. % arrayfun
Elapsed time is 0.495794 seconds. % Shai's solution

So, arrayfun's not too bad after all.

But there is an important point here: Just look at the implementations from a distance. How easy to read/understand was the loop solution? How about the vectorized solutions?

With this in mind, also look at the performances. That's for a 5000x5000 matrix...that's 25 million elements there...

Now, would you really want to avoid that loop?

Upvotes: 3

Shai
Shai

Reputation: 114796

You can use cumsum along the rows. The solution is a bit complex, so I'll start with a simpler example:

Suppose you want to sum all elements of the i-th row of A till (including) the v(i)-th place: res_i = \sum_{k=1..v(i)} A_ik

m = size(A,1); % num of rows
csA = cumsum(A, 2); % cumsum along rows
res = csA( sub2ind( size(A), 1:m, v ) ); % pick the vi-th column for the i-th row

Now, for your question, since you want the sum of all elements from v(i) to the end, we need to flip A and change v accordingly

[m n] = size(A);
fA = fliplr(A);
fv = n + 1 - v; % flip the meaning of v
csA = cumsum( fA, 2 ); 
res = csA( sub2ind( [m n], 1:m, fv ) ); % should do the trick...

Upvotes: 4

Related Questions