user24007
user24007

Reputation: 107

Using the "find" command to accumulate values "without for loops"

I will use find to get the indices from a matrix (named lag) and then sum their corresponding values from another matrix (H). This will require a for loop. (The matrices are 2D.)

(max is used here to implying a generic example)

D=zeros(max, 1)
for j = 1:max
    ind = find(lag==j)
    D(j) = sum(H(ind))
end

This is a 4 point example. Points are at (1,1), (1,2), (2,1), (2,2). This is part of a geostatistical method. The distance between all points has been calculated in H. lags is H but rounded down and then a plus 1 to associate the distances with their closest integer (a classification called lag). I now want to sum the total distance for each lag into vector D.

lag (4x4) - stores the general lag interval

1  2  2  2
2  1  2  2
2  2  1  2
2  2  2  1

H (4x4) - stores the distance between points

0   1   1   1.414
1   0   1.414   1
1   1.414   0   1
1.414   1   1   0

Can I do this without for loops?

Upvotes: 2

Views: 93

Answers (3)

Luis Mendo
Luis Mendo

Reputation: 112659

This is easily done with accumarray:

D = accumarray(lag(:), H(:));

This accumulates (sums) values of the second argument (H linearized to a column vector) for each group defined by the values of the first argument (lag linearized to a column vector).

Upvotes: 3

You can do this with

ulag = unique(lag); %contains [1 2]
D = arrayfun(@(l)sum(H(lag==l)),ulag);

unique(lag) will distil lag into unique values, in your small example [1 2]. Then arrayfun will go over the array ulag, and for each element l it will perform sum(H(lag==l)), which does exactly what you want: sums those elements from H for which the corresponding element in lag is equal to l. Note that this only works fine if lag contains integers, otherwise unique might not find identical elements due to machine precision (but from your question I gathered that it is probably inherently an integer).

Also note that the above two lines could also have been written in one:

D = arrayfun(@(l)sum(H(lag==l)),unique(lag));

I just thought that after separation it might be easier to digest.

Also also note that the above makes use of that the values of lag start from 1 up to a maximal value without gaps. If this is not the case, then you should do something like

D(unique(lag)) = arrayfun(@(l)sum(H(lag==l)),unique(lag));

but in this case you have to make sure that lag doesn't contain non-positive values.

Upvotes: 3

Brian Lynch
Brian Lynch

Reputation: 542

There isn't a good way to do this without a for loop, but you can do it without using find.

D = zeros(N, 1)
for j = 1:N
   D(j) = sum(sum((lag == j).*H));
end

The result of lag == j will be a 4x4 (or rather NxN) matrix of boolean values such that (lag == j).*H will retain the distances for each lag but have zeros for all other elements. Summing all of the elements in (lag == j).*H will then give you the same answer for D(j).

Note I have switched max to be N since max is a built-in MATLAB function.

Upvotes: 0

Related Questions