2
votes

I am writing a piece of code that is supposed to find the rows in Nx3 matrix E that contains the values of Mx3 matrix T, but they are not necessarily in the same order. I wrote something which is working, but I am not happy with the style and wish to apply logical operations on the matrices using ismember and find, rather than the loop I am using:

E = [7 36 37; 9 1 5; 4 34 100; 4 12 33; 4 34 33];
T = [37 7 36; 4 34 33];      

for i=1:size(T,1)
    T(i,:) = sort(T(i,:));
end

for i=1:size(E,1)
    E(i,:) = sort(E(i,:));
end

res = zeros(size(T,1),1);
for i=1:size(T,1)
    a = ismember(E,t(i,:));    
    res(i,1) = find(sum(a,2)==3);
end

My idea was to sort each row of E and T, so they will be in the same order and then compare each row with a loop. However, I am trying to learn to write my code in a more MATLAB-style and wish to apply ismember and maybe find to do the same operation. Something like this:

a = sum(ismember(E,T(1,1))~=0,2);
b = sum(ismember(E,T(1,2))~=0,2);
c = sum(ismember(E,T(1,3))~=0,2);
r = find(a&b&c);

Any more graceful solutions?

Thank you!

2
Have you considered ismember(A,B,'rows') or perhaps setdiff? Any solution based on sorting is probably going to be a little obfuscated, even if it's performant.Joshua Barr

2 Answers

3
votes

You can use ismember with the 'rows' option:

ismember(sort(E, 2), sort(T, 2), 'rows')

This produces a logical (boolean) vector that indicates which rows of matrix T (or any of their permutations) appear in matrix E. Note that each row is sorted independently to handle reordering.

If you need the indices of the row of T, use both outputs of ismember:

[tf, loc] = ismember(sort(E, 2), sort(T, 2), 'rows')

Note: if your data contains floating-point numbers, the ismember approach might fail, so you'll need to set a tolerance for comparison. See this answer for a more robust alternative approach (and remember to sort your input matrices along the 2nd dimension first!).

Example

E = [7 36 37; 9 1 5; 4 34 100; 4 12 33; 4 34 33];
T = [37 7 36; 4 34 33];
[tf, loc] = ismember(sort(E, 2), sort(T, 2), 'rows');

The result is:

tf =          loc =
     1              1
     0              0
     0              0
     0              0
     1              2
1
votes

You can do what you ask using bsxfun and the eq function:

Esorted = sort(E, 2);
Tsorted = sort(T, 2);

rowsEq = bsxfun(@eq, Esorted, permute(Tsorted, [3 2 1]));
rowsEqInd = sum(rowsEq, 2) == size(T, 2);
EEqInd = sum(rowsEqInd, 3) > 0;

There is no need to sort each row of E and T in a loop - sort will do this for you. rowsEq is a 3-D array indicating the equality of the elements in each of the rows of T to each of the rows of E; where there are three 1s in a row in this matrix, the rows of E and T are equal, so we sum up and test that the number of 1s is 3 (or size(T, 2)). We're left with an array indicating the equality of each row of T separately, so the final sum combines these results to give what you desire.