Artur Castiel
Artur Castiel

Reputation: 185

Applying intersect for each row in different matrices in MATLAB

I have two big matrices and I would like to perform an intersect operation between each row of these matrices. I would get something like that:

a =

     8     1     6
     3     5     7
     4     9     2

b =

     8     3     4
     1     5     9
     6     7     2

intersect(a,b) =

             8
             5
             2

An intersect done for all n rows of A and B not using a for loop. Is it possible?

Upvotes: 2

Views: 146

Answers (2)

Edit: this is a kludgy solution, with only apparent vectorization (but in reality, arrayfun is a wrapper around a loop). See the other answer by Divakar for a proper vectorized answer.


If your intersections are guaranteed to have equal size in each row, you can use arrayfun like this:

intersect_array_fun = @(A,B) arrayfun(@(k) intersect(A(k,:),B(k,:)), ...
                                     (1:size(A,1)).');
row_intersects = intersect_array_fun(a,b);

This will give you the answer:

row_intersects =

     8
     5
     2

Note that this will fail if your output from arrayfun is non-uniform, i.e. if there are multiple sizes returned by intersect for every row. The error looks like this:

Error using arrayfun
Non-scalar in Uniform output, at index 1, output 1.
Set 'UniformOutput' to false.

Error in @(A,B)arrayfun(@(k)intersect(A(k,:),B(k,:)),1:size(A,1))

We can expect this and do instead

intersect_array_fun = @(A,B) arrayfun(@(k) intersect(A(k,:),B(k,:)), ...
                                      (1:size(A,1)).');
intersect_cell_fun = @(A,B) arrayfun(@(k) intersect(A(k,:),B(k,:)), ...
                                     (1:size(A,1)).', ...
                                     'uniformoutput',false);

try
    row_intersects = intersect_array_fun(a,b);
catch
    row_intersects = intersect_cell_fun(a,b);
end

This will catch the error, and tell arrayfun to return cell output in case there's a mismatch in the size of the intersects. For your above example the result is

row_intersects =

     8
     5
     2

but if we change a(1,3) to 4, we get

row_intersects = 

    [1x2 double]
    [         5]
    [         2]

instead: a column cell array.

This brings us to my last point: your question is of vectorization. You want to achieve all this without a for loop, working simultaneously on every row. Well, my first example might have fooled you, but arrayfun is just a wrapper for a loop, and is not more efficient than the equivalent loop. However, my latter example with mismatching row sizes suggests that what you're doing is inherently not vectorizable. Since the output possibly consists of varying-length vectors for each row, you probably can't avoid using a loop.

Upvotes: 3

Divakar
Divakar

Reputation: 221594

Here's a vectorized approach using broadcasting/bsxfun -

b_mask = squeeze(any(bsxfun(@eq,a,permute(b,[1,3,2])),2));
bt = b.';
out = mat2cell(bt(b_mask'),sum(b_mask,2));

Sample run -

a =
     8     1     6
     3     5     7
     4     9     2
b =
     3     8     2     6     1
     5     6     9     0     3
     6     7     1     5     3
out{1} =
     8
     6
     1
out{2} =
     5
     3
out{3} =
     []

Please note that this would not work for cases with duplicate numbers in a row in b, as in those cases it would put out duplicated numbers from b in the output. This is probably not the expected one, given that we are performing intersect operation that gives unique numbers.

To make it work for duplicate elements, we need to modify the creation of b_mask, keeping rest as it is. So, the modified version would look something like this -

matches0 = bsxfun(@eq,a,permute(b,[1,3,2]));
[~,maxidx] = max(matches0,[],3);
matches1 = bsxfun(@eq,permute(1:size(b,2),[1,3,2]),maxidx) & matches0;
b_mask = squeeze(any(matches1,2));

Sample run -

a =
     8     8     1
     3     5     7
     4     9     2
b =
     3     8     1     8     5
     5     6     9     0     3
     6     7     1     5     3
out{1} =
     8
     1
out{2} =
     5
     3
out{3} =
     []

Upvotes: 3

Related Questions