Home > front end >  Finding multiple coincidences between two vectors
Finding multiple coincidences between two vectors

Time:11-03

I have two vectors, and I'm trying to find ALL coincidences of one on the other within a certain tolerance without using a for loop. By tolerance I mean for example if I have the number 3, with tolerance 2, I will want to keep values within 3±2, so (1,2,3,4,5).

A = [5 3 4 2]; B = [2 4 4 4 6 8];

I want to obtain a cell array containing on each cell the numbers of all the coincidences with a tolerance of 1 (or more) units. (A = B - 1) I have a solution with zero units (A = B), which would look something like this:

tol = 0;
[tf, ia] = ismembertol(B,A,tol,'DataScale',1); % For tol = 0, this is equivalent to using ismember
idx = 1:numel(B);
ib = accumarray(nonzeros(ia), idx(tf), [], @(x){x}) % This gives the cell array

The output is:

ib = 
[]
[]
[2 3 4]
[1]

Which is as desired. If I change the tolerance to 1, the code doesn't work as intended. It outputs instead:

tol = 1
[tf, ia] = ismembertol(B,A,tol,'DataScale',1); % For tolerance = 1, this is equivalent to using ismember
idx = 1:numel(B);
ib = accumarray(nonzeros(ia), idx(tf), [], @(x){x}) % This gives the cell array
ib = 
[5]
[2 3 4]
[]
[1]

When I would expect to obtain:

ib = 
[2 3 4 5]
[1 2 3 4]
[2 3 4]
[1]

What am I doing wrong? Is there an alternative solution?

CodePudding user response:

Your problem is that, in the current state of your code, ismembertol only outputs 1 index per element of B found in A, so you lose information in the cases where an element can be found several times within tolerance.

As per the documentation You can use the 'OutputAllIndices',true value pair argument syntax, to output what you want in ia with just a call to ismembertol:

A = [5 3 4 2]; B = [2 4 4 4 6 8];

tol = 0;
[tf, ia] = ismembertol(A,B,tol,'DataScale',1,'OutputAllIndices',true);

celldisp(ia) % tol = 0

ia{1} =

 0

ia{2} =

 0

ia{3} =

 2
 3
 4

ia{4} =

 1

celldisp(ia) % tol = 1

ia{1} =

 2
 3
 4
 5

ia{2} =

 1
 2
 3
 4

ia{3} =

 2
 3
 4

ia{4} =

 1

CodePudding user response:

Here is a manual approach, just to provide another method. It computes an intermediate matrix of all absolute differences (using implicit expansion), and from the row and column indices of the entries that are less than the tolerance it builds the result:

A = [5 3 4 2];
B = [2 4 4 4 6 8];
tol = 1;
[ii, jj] = find(abs(A(:).'-B(:))<=tol);
ib = accumarray(jj, ii, [numel(A) 1], @(x){x});

Note that this approach

  • may be memory-intensive, because of the intermediate matrix;
  • can be made to work in old Matlab versions, because it doesn't use ismembertol; but then implicit expansion has to be replaced by explicitly calling bsxfun:
[ii, jj] = find(abs(bsxfun(@minus, A(:).', B(:)))<=tol);
ib = accumarray(jj, ii, [numel(A) 1], @(x){x});

  • Related