Reputation: 522
Given a vector A
of group numbers (such as the one returned by findgroups
), how to return a vector B
of the same length containing indices of elements within groups of A
?
For example, if A = [1 1 1 2 2 2 1 1 2 2]
then B = [1 2 3 1 2 3 4 5 4 5]
.
Add 1
My own solution to this is
s = splitapply(@(x) {x, [1:numel(x)]'}, [1:numel(A)]', A(:))
B(vertcat(s{:,1})) = vertcat(s{:,2})
but it seems somewhat convoluted.
Upvotes: 2
Views: 198
Reputation: 15837
A solution using sort
and accumarray
:
[s is]=sort(A);
idx = accumarray(s(:),1,[],@(x){1:numel(x)});
B(is)=[idx{:}];
Another solution using the image processing toolbox:
p=regionprops(A,'PixelIdxList');
B = zeros(size(A));
for k = 1: numel(p)
B(p(k).PixelIdxList) = 1:numel(p(k).PixelIdxList);
end
Upvotes: 3
Reputation: 125854
Here's a solution that may not look as compact, but it's quite fast since it uses cumsum
and indexing:
mA = max(A);
nA = numel(A);
ind = false(mA, nA);
ind(mA.*(0:(nA-1))+A) = true;
B = cumsum(ind, 2);
B = B(ind).';
And here are some timing results for the solutions thus far:
A = [1 1 1 2 2 2 1 1 2 2];
rahnema1: 6.51343e-05
Luis: 3.00891e-05
OmG: 2.36826e-05
gnovice: 4.93539e-06 % <---
A = randi(20, 1, 1000);
rahnema1: 0.000274138
Luis: 0.000257126
OmG: 0.000233348
gnovice: 9.95673e-05 % <---
A = randi(20, 1, 10000);
rahnema1: 0.00162955
Luis: 0.00163943
OmG: 0.00126571
gnovice: 0.00107134 % <---
My solution is the fastest for the above test cases (moderate size, moderate number of unique values). For larger cases, other solutions gain the edge. The solution from rahnema1 seems to do better as the number of unique values in A
increases, whereas the basic for loop from OmG does better when the number of elements in A
increases (with relatively fewer unique values):
>> A = randi(200, 1, 100000);
rahnema1: 0.0108024 % <---
Luis: 0.0931876
OmG: 0.0427542
gnovice: 0.0815516
>> A = randi(20, 1, 1000000);
rahnema1: 0.131256
Luis: 0.171415
OmG: 0.106548 % <---
gnovice: 0.124446
Upvotes: 2
Reputation: 112659
Here's a way that avoids loops:
s = bsxfun(@eq, A(:).', unique(A(:))); % Or, in recent versions, s = A==unique(A).';
t = cumsum(s,2);
B = reshape(t(s), size(A));
Upvotes: 1
Reputation: 18838
A solution can be using a loop to replace the values in A
:
c = unique(A);
B= A;
for (idx = c)
f = find(A == idx);
B(f) = 1:length(f);
end
Upvotes: 1