Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

matlab return every second occurrence of value in vector

I have a vector with ID numbers that repeat an even number of times. I am only interested in every second time each number appears. I want to create a boolean mask that gives a true/1 for each second occurance of a number. I have already done this with a loop, but the actual vector will contain millions of elements, so the loop is too slow. I need a "vectorized" solution.

Here is an example Vector:

101
102
103
101
104
102
101
103
101
104

This should output the following mask:

0 (first occurrence of 101)
0 (first occurrence of 102)
0 (first occurrence of 103)
1 (second occurrence of 101)
0 (first occurrence of 104)
1 (second occurrence of 102)
0 (third occurrence of 101) 
1 (second occurrence of 103)
1 (fourth occurrence of 101)
1 (second occurrence of 104)
like image 618
Trashman Avatar asked Sep 15 '15 17:09

Trashman


2 Answers

You can do this very easily with a combination of unique and accumarray. First assign each value a unique ID, then bin all array locations together that are part of the same ID. You'll need to sort them as accumarray doesn't guarantee an order when you are binning things together. The output of this will be a cell array where each cell gives you the array locations that occurred for a particular index.

Once you do this, extract out every second element from each cell generated from accumarray, then use these to set all of the corresponding locations in a mask to 1. You can use a combination of cellfun, which can be used to process each cell individually and extracting every second element to create a new cell array, and vertcat which can be used to stack all of the cell arrays together into one final index array. This index array can be used to accomplish setting the locations in your mask to true:

%// Your data
V = [101,102,103,101,104,102,101,103,101,104];

%// Get list of unique IDs
[~,~,id] = unique(V,'stable');

%// Bin all of the locations in V together that belong to the
%// same bin
out = accumarray(id, (1:numel(V)).',[], @(x) {sort(x)}); %'

%// Extract out every second value that is for each bin
out2 = cellfun(@(x) x(2:2:end), out, 'uni', 0);

%// Create a mask and set the corresponding locations to true
mask = false(numel(V), 1);
mask(vertcat(out2{:})) = 1;

We get:

>> mask

mask =

     0
     0
     0
     1
     0
     1
     0
     1
     1
     1
like image 91
rayryeng Avatar answered Oct 29 '22 19:10

rayryeng


Let's bsxfun it for a vectorized solution -

%// Assuming A as the input vector
M = bsxfun(@eq,A(:),unique(A(:).'))  %//'
out = any(M - mod(cumsum(M,1).*M,2),2)
like image 34
Divakar Avatar answered Oct 29 '22 18:10

Divakar