Find the elements in an array that is not in another array - matlab

I have two arrays lets say A = [1;2;4;7;10;20];
B = [1;4;8];
Now I want to find the elements of A, that are not in B i.e; [2;7;10;20]. I just need their index that is the index of the elements [2;7;10;20] in A. How can I implement this in matlab. I can use loops and all. But that is not what I want. I want an elegant solution. Suggestions?

You can do that using the ismember function.
A = [1;2;4;7;10;20];
B = [1;4;8];
ismem = ismember(A,B);
will give you
[1 0 1 0 0 0]'
If you really need the indices, you can use find.
find(ismem==0)
Just as a reminder, you can always use logical indexing like so:
A(~ismem)
will give you
[2 7 10 20]'

If you want the elements of A which are not in B you can use setdiff.
If you want the indices of the elements rather than their values, you can use ismember and negate the result.

Check out setxor:
[C, ia, ib] = setxor(A, B)
Here is the Mathworks page. You will want ia for the indexes of those that are in A but not B.

Related

Comparing only nonzero elements

The specific task I'm trying to achieve is hard to describe, so here's an example: given A and x
A = [1 2;
3 0;
3 5;
4 0];
x = [1 2 3];
I want the algorithm to output
output: [1 2]
meaning that all of the nonzero elements in rows 1 and 2 in A are in x.
I have done this using cell arrays and loops; however, A and x are very large and my approach is not at all efficient. Also, I can't seem to figure out how to rework ismember to give me what I want. What is the fastest/least memory intensive method?
EDIT: Apologies, my original example was too simplistic. It is corrected now.
The first answer is good, but I would recommend to not using arrayfun. There are more eloquent ways to do what you ask. Use ismember combined with all, then index into the matrix A when you're done. Basically, your problem is to determine if a row has all of the values found in x and ignoring the zero values. In this case, we can find all of the values in the matrix A that are actually zero, then use this to augment our result.
Using A as the first input and x as the second input will return a matrix of the same size as A that tells you whether an element in A is found in x. If you want to check if all elements in the matrix A for a row can be found in x, check if all elements in a row is 1. On top of this, find all of the elements that are zero, then with the output of ismember set these to 1. This can be done with using a logical OR. After, you can use all and check each row independently by using the output of ismember as the first input into all and setting the second argument to 2. This would then return all of the rows in the matrix A where any column is found in x ignoring any values that are zero for a row in A which is what you're looking for:
A = [1 2; 3 0; 4 0];
x = [1 2 3];
mask = ismember(A, x);
ind = all(mask | A == 0, 2);
I'm also in favour of one-liners. We can consolidate this into one line of code:
ind = all(ismember(A, x) | A == 0, 2);
Even shorter is to simply invert A. All zero elements become true and false otherwise:
ind = all(ismember(A, x) | ~A, 2);
ind would thus be:
>> ind
ind =
3×1 logical array
1
1
0
Since you want the actual row indices, you can just use find on top of this:
>> find(ind)
ans =
1
2
To verify, let's use your second example in your comments:
>> A = [1 2;3 5;4 0];
>> x = [1 2 3];
>> ind = all(ismember(A, x) | ~A, 2)
ind =
3×1 logical array
1
0
0
>> find(ind)
ans =
1
I think the best way to rework ismember is to make sure there are no "no members" by just checking for the nonzero elements in A.
arrayfun can do the work in a fast way. It uses the most efficient parallel computing for your specific machine. The following line should return the correct output:
find(arrayfun(#(a) sum(~ismember(A(a,A(a,:)>0),x)),1:size(A,1))==0)
Is this what you were looking for?
However, if your problem is related to memory, then you may have to break the arrayfun operation into pieces (1:floor(size(A,1)/2), floor(size(A,1)/2):size(A,1) or smaller chunks), since MATLAB puts a bunch of workers to do the task, and may use all your available RAM memory...

logical operation within vector range expression in MATLAB

can I have something like
A=1:10;
A(1:2 && 5:6)=0;
meaning I want to zero out specific ranges within my vector index expression in one line
Is that possible?
And what if I wanted to zero out all the rest like
A(~[1:2]) = 0
What's the way of logical NOT within vector indexing?
Thanks
The following should work:
idx = [1:2,5:6];
A(idx) = 0
If you want to zero the complement of the vector of indices:
idx = [1:2,5:6];
A(~ismembc(1:length(A),idx)) = 0
Where ismembc is a faster, lightweight version of ismember that assumes the array is sorted and non-sparse with no NaN elements. (Credit goes to this question.)
Just do A([1:2 5:6]). I.e., just create a vector of the indices you want to zero out.

Mapping ids of two vectors

I have two vectors with the same elements but their order is not same. For eg
A
10
9
8
B
8
9
10
I want to find the mapping between the two
B2A
3
2
1
How can I do this in matlab efficiently?
I think the Matlab sort is efficient. So:
[~,I]=sort(A); %sort A; we want the indices, not the values
[~,J]=sort(B); %same with B
%I(1) and J(1) both point to the smallest value, and a similar statement is true
%for other pairs, even with repeated values.
%Now, find the index vector that sorts I
[~,K]=sort(I);
%if K(1) is k, then A(k) is the kth smallest entry in A, and the kth smallest
%entry in B is J(k)
%so B2A(1)=J(k)=J(K(1)), where BSA is the desired permutation vector
% A similar statement holds for the other entries
%so finally
B2A=J(K);
if the above were in script "findB2A" the following should be a check for it
N=1e4;
M=100;
A=floor(M*rand(1,N));
[~,I]=sort(rand(1,N));
B=A(I);
findB2A;
all(A==B(B2A))
There are a couple of ways of doing this. The most efficient in terms of lines of code is probably using ismember(). The return values are [Lia,Locb] = ismember(A,B), where Locb are the indices in B which correspond to the elements of A. You can do [~, B2A] = ismember(A, B) to get the result you want. If your version of MATLAB does not allow ~, supply a throwaway argument for the first output.
You must ensure that there is a 1-to-1 mapping to get meaningful results, otherwise the index will always point to the first matching element.
Here a solution :
arrayfun(#(x)find(x == B), A)
I tried with bigger arrays :
A = [ 7 5 2 9 1];
B = [ 1 9 7 5 2];
It gives the following result :
ans =
3 4 5 2 1
Edit
Because arrayfun is usually slower than the equivalent loop, here a solution with a loop:
T = length(A);
B2A = zeros(1, length(A));
for tt = 1:T
B2A(1, tt) = find(A(tt) == B);
end
I would go for Joe Serrano's answer using three chained sort's.
Another approach is to test all combinations for equality with bsxfun:
[~, B2A] = max(bsxfun(#eq, B(:), A(:).'));
This gives B2A such that B(B2A) equals A. If you want it the other way around (not clear from your example), simply reverse A and B within bsxfun.

remove elements with corresponding zeros in another matrix matlab

I have two matrices A & B in Matlab, for example
A=[0,0,1,2,3,0,4,2,0]
B=[2,3,1,2,2,3,4,4,1]
What I want to do is to set elements in B to zero where they have the same position as zero elements in A. So in my example:
A=[0,0,1,2,3,0,4,2,0]
B=[2,3,1,2,2,3,4,4,1]
I want B to be like this:
B=[0,0,1,2,2,0,4,4,0]
Any idea?
You can do it using logical indexing like so: B(A==0) = 0
EDIT:
You can also do it like this: B.*(A~=0) which will be easier to generalise to higher dimensions using bsxfun as per your comment below.
The only problem with doing something that Dan suggests is if A and B are not the same size. You can still however do this with a little bit of extra work.
indices = find(A==0);
indices = indices(indices <= length(B));
B(indices) = 0;

Any built-in Matlab function to find if a certain number lies between two members of a vector?

It should return lowest index i if a number a lies between x(i) and x(i+1). I know it's not hard to write a function which would do this but is there any built-in Matlab function for this?
Assuming the vector elements are sorted it would be a trivial search O(logn) I guess but is there any better way to do it if the elements are not sorted without going through sorting?
Thanks in advance!
Logical indices are well suited for these kind of comparisons:
x = [6 2 6 7 3 5];
a = 4;
find(a > x(1:end-1) & a < x(2:end), 1)
ans = 2
Try
a=rand(1);
b=rand(1,10);
c=a-b;
find(c(2:end).*c(1:end-1)<0,1)