Reputation: 409
I am facing a task which is not very easy to deal with.
Image I have a vector contains 4 ones, 3 twos, 2 threes and 4 fours as:
[1,1,1,1,2,2,2,3,3,4,4,4,4]
That means I have 1,2,3,4 with different number of occurrences.
I want to find the first positions of these elements and final positions of these elements. That means I want to have:
(1,5,8,10) as first occurrences of (1,2,3,4) in the vector
(4,7,9,13) as final occurrences of (1,2,3,4) in the vector
More importantly, what if the reference vector is in random order rather than sorted ?
I know how to use for-loop to do it with help of unique() function.
I am looking for more efficient, vectorized solution to such problem.
Thanks for your time and your kind help will be well appreciated.
Upvotes: 1
Views: 2201
Reputation: 112659
x = [1,1,1,1,2,2,2,3,3,4,4,4,4];
s = [1,2,3,4];
[valid, first] = max(bsxfun(@eq, x(:), s(:).'),[],1);
[~, last] = max(bsxfun(@eq, flipud(x(:)), s(:).'),[],1);
first(~valid) = 0;
last = numel(x)-last+1;
last(~valid) = 0;
This uses the fact that the second output of max
returns the position of the first maximizer within each column. Also, the first output is used to make sure each value occurs at least once; if that's not the case the resut is set to zero. So for example the result for s = [1,2,3,4,5]
would be
first =
1 5 8 10 0
last =
4 7 9 13 0
To obtain the last positions the same approach is used, only with a flipped vector.
The elements can be in any order within each vector.
Upvotes: 0
Reputation: 104464
I'm not sure why you couldn't just use unique
without a for
loop. Using just the second output, you can determine what you're looking for.
Given your example:
A = [1,1,1,1,2,2,2,3,3,4,4,4,4];
Simply call unique
this way if you want the index of the first occurrence for each unique element in A
:
[~,out_first,~] = unique(A, 'first');
Also, if you want to find the index of the last occurrence for each unique element in A
, do it like this:
[~,out_last,~] = unique(A, 'last');
Given your above example, this is what we get for both the first and last occurrences:
>> out_first
out_first =
1
5
8
10
>> out_last
out_last =
4
7
9
13
If your vector A
was in a random order, unique
will still find the first occurrence relative to the starting position of A
. For example, if I shuffle the elements in A
:
B = A(randperm(numel(A)))
>> B
B =
2 1 4 2 3 2 1 1 1 4 3 4 4
This is what I get when I run unique
on this shuffled vector:
>> out_first
out_first =
2
1
5
3
>> out_last
out_last =
9
6
11
13
As you can see, the first time we encounter the value 1, it's at index 2, the first time we encounter the value 2, it's at index 1 and so on. You can follow the same pattern with the last encounters.
As such, I don't see how more vectorized you could get other than a single function call to unique
. There's no need to use a for
loop with it.
Upvotes: 2
Reputation: 238081
Have a look at the following code and examples and also on this awnser:
a = [1,1,1,1,2,2,2,3,3,4,4,4,4];
first_occurences = [1, find(diff([a(1) a a(end)]))]
last_occurences = [first_occurences(2:end) - 1, length(a)]
first_occurences =
1 5 8 10
last_occurences =
4 7 9 13
a = [2,2,2, 1,1,1,1,4,4,4,4, 3,3];
first_occurences = [1, find(diff([a(1) a a(end)]))]
last_occurences = [first_occurences(2:end) - 1, length(a)]
first_occurences =
1 4 8 12
last_occurences =
3 7 11 13
a = [4,4,4,4, 1,1,1,1, 3,3, 2,2,2];
first_occurences = [1, find(diff([a(1) a a(end)]))]
last_occurences = [first_occurences(2:end) - 1, length(a)]
first_occurences =
1 5 9 11
last_occurences =
4 8 10 13
Upvotes: 0