how to efficiently find the indices?

조회 수: 13 (최근 30일)
Pinpress
Pinpress 2012년 5월 2일
Hi,
I have a matrix of N-by-M with integers. I need to efficiently find the indices for all of the unique elements in the matrix. The solution I have is via a "for" loop:
uM = unique (M(:));
for i = 1 : length(uM)
I(i) = find(M == uM(i));
end
This works fine, but with a large matrix, this is slow. I wonder if there are better solutions. thanks very much!
  댓글 수: 1
Geoff
Geoff 2012년 5월 3일
One way to make this code faster without changing anything fundamental would be to preallocate the cell array before your loop:
I = cell(length(uM),1);

댓글을 달려면 로그인하십시오.

채택된 답변

Richard Brown
Richard Brown 2012년 5월 2일
sort is your friend:
First we sort the entries of A so that like entries are adjacent
[S, iSorted] = sort(A(:));
Then we find the markers - where the value changes, plus the endpoints
markers = [0; find(diff(S)); numel(A)];
All we need to do is find the entries of iSorted corresponding to each block.
for i = 1:numel(markers)-1
uM(i) = S(markers(i)+1);
I{i} = iSorted(markers(i)+1:markers(i+1));
end
  댓글 수: 2
Pinpress
Pinpress 2012년 5월 3일
Great! This turns out to be working fine for me. It's still a "for" loop, but the computational efficiency is OK.
Richard Brown
Richard Brown 2012년 5월 3일
for loops get a bit of an unnecessarily bad rap sometimes

댓글을 달려면 로그인하십시오.

추가 답변 (6개)

Leah
Leah 2012년 5월 2일
Matlab has a nice function built in for this
[B,I,J] = UNIQUE(...) also returns index vectors I and J such
that B = A(I) and A = B(J) (or B = A(I,:) and A = B(J,:)).

Andrei Bobrov
Andrei Bobrov 2012년 5월 3일
[uM,n,n] = unique (M(:));
I = accumarray(n,1:numel(n),[],@(x){sort(x)});
  댓글 수: 2
Richard Brown
Richard Brown 2012년 5월 3일
I think even @(x) {x} would meet the brief for your function
Andrei Bobrov
Andrei Bobrov 2012년 5월 3일
Hi Richard! I agree with you.

댓글을 달려면 로그인하십시오.


Richard Brown
Richard Brown 2012년 5월 2일
Depending on whether you want the first or the last occurence
[uM, I] = unique(M, 'first')
[uM, I] = unique(M)

Walter Roberson
Walter Roberson 2012년 5월 2일
Your suggested code will not work if there are any duplicates, as the find() would return multiple values in that case and multiple values cannot be stored into a single numeric array element.
Have you considered using the second or third return value from unique() ?

Pinpress
Pinpress 2012년 5월 2일
Thanks guys -- forgot to mention that I need to find the indices for all of the elements if there are multiple occurrences. So simply using "unique" doesn't seem to work.
And yes, the original code I had will not work when more than one indices returns (which was precisely the reason I did not use [A, I, J] = unique (B);].
Any other thoughts?
  댓글 수: 2
Oleg Komarov
Oleg Komarov 2012년 5월 2일
A and J will give you what you want.
Richard Brown
Richard Brown 2012년 5월 2일
Not really - J is just A(:), but with the unique elements replaced with 1:nUnique. So it's no better

댓글을 달려면 로그인하십시오.


Geoff
Geoff 2012년 5월 2일
This comes straight out of some of my own code... I guess it's the reverse of what you want though.
uM = unique(M);
I = arrayfun(@(x) find(uM==x,1), M);
For every element in M, it gives an index into uM. I use this to reduce columns of data in a matrix that are common to multiple targets.
So you seem to want: for every element in uM an array of indices into M. The result of course would be a cell array.
This would be:
I = arrayfun(@(x) find(M==x), uM, 'UniformOutput', false);
  댓글 수: 2
Richard Brown
Richard Brown 2012년 5월 2일
I think that's what he was trying before, but found the repeated calls to find to be too slow
Pinpress
Pinpress 2012년 5월 3일
Calling "find" thousands of times will be slow.

댓글을 달려면 로그인하십시오.

카테고리

Help CenterFile Exchange에서 Loops and Conditional Statements에 대해 자세히 알아보기

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by