Matlab returns every second occurrence of a value in a vector

I have a vector with identifier numbers that repeat an even number of times. I am interested in every second time every number appears. I want to create a logical mask that gives true / 1 for every second number. I already did 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 a Vector example:

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) 
+5
source share
3 answers

Let bsxfun for a vectorized solution be

 %// Assuming A as the input vector M = bsxfun(@eq,A(:),unique(A(:).')) %//' out = any(M - mod(cumsum(M,1).*M,2),2) 
+3
source

You can do this very easily with a combination of unique and accumarray . First assign each value a unique identifier, then merge all the cells in the array together that are part of the same ID. You will need to sort them, as accumarray does not guarantee ordering when you combine things together. The result will be an array of cells in which each cell gives you the location of the array that took place for a particular index.

Once you do this, extract every second element from every cell generated from accumarray , then use them to set all the corresponding locations in the mask to 1. You can use the cellfun combination, which can be used to process each cell separately and to extract each the second element to create a new array of cells and vertcat , which can be used to sum all arrays of cells into one final array of indices. This index array can be used to set the mask location 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 
+4
source

Here is one approach:

 A = [101,102,103,101,104,102,101,103,101,104]; IDs = unique(A); % Find all the IDs present test = arrayfun(@(x) find(A==x), IDs, 'UniformOutput', false); % Per ID, find where A == ID repeatidx = cellfun(@(x) x(2:2:length(x)), test, 'UniformOutput', false); % Dump out the second match repeatidx = cell2mat(repeatidx); % Flatten the cell array B = false(size(A)); % Intialize output boolean array B(repeatidx) = true; % Set true values based on repeatidx 

What returns:

 B = 0 0 0 1 0 1 0 1 1 1 
+3
source

All Articles