GeekCat
GeekCat

Reputation: 409

Matlab: find first and final occurrences of elements in a vector?

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:

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

Answers (3)

Luis Mendo
Luis Mendo

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

rayryeng
rayryeng

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

Marcin
Marcin

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

Related Questions