Find set intersection of multiple arrays in MATLAB - matlab

I tried to solve this problem, but I could not implement.
Could you help me anything for this?
Problem
Mat1 | Mat2 | Mat3
1 2 | 1 3 | 2 6
1 3 | 2 6 | 2 5
2 4 | 3 1 | 3 1
3 1 | 3 5 | 5 2
4 5 |
When there are 3 matrices(for example above), I want to get this result for the intersection rows in [column1 column2 matrixnumber] form.
The result for above example would be
1 3 1
1 3 2
2 6 2
2 6 3
3 1 1
3 1 2
3 1 3
It would be OK if the result is in the form [column1 column2 firstmatrix secondmatrix, ...]
1 3 1 2
2 6 2 3
3 1 1 2 3
For this problem, I want to use at most one for-loop.
Do you have any idea for this?

Here an alternative solution (which seems to run faster than Gunther's) using MATLAB's intersect:
Mat = {[1 2; 1 3; 2 4; 3 1; 4 5],
[1 3; 2 6; 3 1; 3 5],
[2 6; 2 5; 3 1; 5 2]};
result = zeros(sum(cellfun(#(x)size(x, 1), Mat)), 3); % # Preallocate memory
k = 1;
for cc = transpose(nchoosek(1:numel(Mat), 2))
x = intersect(Mat{cc}, 'rows'); % # Find intersection
y = ones(size(x, 1), 2) * diag(cc); % # Generate matrix indices
result(k:k + numel(y) - 1, :) = [[x; x], y(:)];
k = k + numel(y);
end
result(all(~result, 2), :) = []; % # Discard zero rows
result = unique(result, 'rows'); % # Discard repeated rows
The matrix result should now contain the unique intersection rows and their corresponding matrix indices, just like you want:
result =
1 3 1
1 3 2
2 6 2
2 6 3
3 1 1
3 1 2
3 1 3

If I understand correctly, you have a number of sets of pairs: Mat1,Mat2, Mat3, ... MatN. Now you want to find the unique pairs and then find out in which set every unique pair appears.
If you have a large number of sets, I suggest you start using a cell array to hold them all, makes things a lot easier:
N = 3; % total number of data sets
Mat = cell(N,1);
Mat{1} = [1 2;
1 3;
2 4;
3 1;
4 5];
Mat{2} = [1 3;
2 6;
3 1;
3 5];
Mat{3} = [2 6;
2 5;
3 1;
5 2];
% etc.
First let's find the unique pairs:
uniq_pairs = unique(cat(1,Mat{:}),'rows');
M = size(uniq_pairs ,1);
Then use ismember to check which sets contain which pairs:
matcontpair = false(M,N); %preallocate
for ii=1:N % unavoidable loop
matcontpair(:,ii) = ismember(uniq_pairs,Mat{ii},'rows');
end
To translate this intersection matrix to a set of matrix numbers for each pair, loop through it again and store the final result in a cell array (you can't use an array, because they might not be of same size (some pairs only found once, other twice, other three times ...)
pair_occurence= cell(M,1);
d=1:N;
for jj=1:M
pair_occurence{jj} = d(matcontpair(jj,:));
end
Now you have a matrix uniq_pairs of size Mx2 containing the unique pairs, and a occurence cell array pair_occurence of size Mx1: each cell corresponds to a pair and contains a list of matrices where the pair is present.
If you want to remove pairs from the list which are only present in one matrix, use the following:
% find them
lonely_pairs = cellfun(#numel,pair_occurence)<2;
% and destroy them
uniq_pairs(lonely_pairs,:) = [];
pair_occurence(lonely_pairs) = [];

Related

How find rows and columns in matlab

I have variable matrix :
A = [1 2 8 8 1
4 6 8 1 1
5 3 1 1 8];
and I have variable B :
B=[2 3 1 8 8];
Question is how to find rows and columns (sort by rows) in variable A from variable B.
Example, first index in variable B is 2, and then I want to find value 2 in variable A and get to first rows and columns, and next process until index 5, but if rows and columns has been used so get second position (ex. index 4 & 5 having same value).
rows;
columns;
Result is:
rows = 1 3 1 1 1
columns = 2 2 1 3 4
Use can use find and sub2ind to achieve what you want
but for that you have to take transpose of your A first
A = [1 2 8 8 1
4 6 8 1 1
5 3 1 1 8];
B= [2 3 1 8 8];
TMP = A.';
for i = 1:length(B)
indx = find(TMP== B(i),1,'first') %Finding the element of B present in A
if(~isempty(indx )) % If B(i) is a member of A
[column(i),row(i)] = ind2sub(size(TMP),indx) % store it in row and column matrix
TMP(indx) = nan; % remove that element
end
end
column =
2 2 1 3 4
row =
1 3 1 1 1
As in one of the comments Usama suggested preallocation of memory
you can do that by using
row = zeros(1,sum(ismember(B,A)))
column= zeros(1,sum(ismember(B,A)))
The above code works even if there are some members of B not present in A
Use find. The function could return both a linear index or a row/col index.
Using linear index a solution could be
idx = zeros(size(B));
for i = 1:numel(B)
% Find all indexes
tmpIdx = find(A == B(i));
% Remove those already used
tmpIdx = setdiff(tmpIdx, idx);
% Get the first new unique
idx(i) = tmpIdx(1);
end
% Convert index to row and col
[rows, cols] = ind2sub(size(A),idx)
Giving:
rows = 1 3 1 1 2
cols = 2 2 1 3 3
Note that as the linear indexing goes down column by column, the result here differs from the one in your example (although still a correct index)
rows = 1 3 1 1 1
columns= 2 2 1 3 4
But to get this you could just transpose the A matrix (A.') and flip the rows and cols (the result from ind2sub)
Here is on solution where I use for loop, I tried to optimize the number of iteration and the computational cost. If there is no corresponding value between B and A the row/col index return NaN.
[Bu,~,ord] = unique(B,'stable');
% Index of each different values
[col,row] = arrayfun(#(x) find(A'==x),Bu,'UniformOutput',0)
% For each value in vector B we search the first "non already used" corresponding value in A.
for i = 1:length(B)
if ~isempty(row{ord(i)})
r(i) = row{ord(i)}(1);
row{ord(i)}(1) = [];
c(i) = col{ord(i)}(1);
col{ord(i)}(1) = [];
else
r(i) = NaN;
c(i) = NaN;
end
end
RESULT:
c = [2 2 1 3 4]
r = [1 3 1 1 1]

Matrix operations matlab to move values around

I have a matrix that I'd like to create a new ordering of, for example,
vals = [1 2; 3 4]
I also have two matrices, new_x and new_y such that new_x(a,b) = j and new_x(a,b) = k means that I want the value at vals (a,b) to be mapped to new_vals(j,k).
For example, given
new_x = [1 2; 2 1]
new_y = [2 2; 1 1]
I'd want
new_vals = [4 3; 1 2]
I understand that I could just write two for loops to build the new array, but matlab is notoriously good at providing operations on entire matricies. My question is, how would I build new_vals without the for loops?
Basically you are trying to get a matrix that when indexed with new_x and new_y would give us vals, i.e. -
output(new_x(1,1),new_y(1,1)) must be equal to vals(1,1),
output(new_x(1,2),new_y(1,2)) must be equal to vals(1,2) and so on.
We will try to verify this later on. For now, here's one solution using linear indexing -
nrows = size(vals,1); %// Store number of rows
%// Calculate linear indices
idx = (new_x + (new_y-1)*nrows);
%// Trace/map back to sorted version of "1:numel(vals)"
[~,traced_back_idx] = sort(idx(:));
%// Index into vals with traced back linear indices & then reshape & transpose
out = reshape(vals(traced_back_idx),[],nrows).'
Here's another and possibly faster way -
out = nan(size(vals));
out((new_x + (new_y-1)*nrows)) = vals;
out = out.'
As discussed earlier for verification, let's index into out with new_x and new_y and that should match up with vals. Here's a code to do so -
for ii = 1:size(out,1)
for jj = 1:size(out,2)
check_back(ii,jj) = out(new_y(ii,jj),new_x(ii,jj));
end
end
Sample runs -
Case #1 (sample from question):
vals =
1 2
3 4
new_x =
1 2
2 1
new_y =
2 2
1 1
new_vals =
4 3
1 2
out =
4 3
1 2
check_back = (must be same as vals)
1 2
3 4
Case #2:
vals =
1 2 5
3 4 5
6 8 3
new_x =
1 2 3
3 1 2
3 2 1
new_y =
2 2 3
2 1 1
1 3 3
out =
4 5 6
1 2 3
3 8 5
check_back = (must be same as vals)
1 2 5
3 4 5
6 8 3
I think i see what you are trying to do here. new_x and new_y are just coordinates for the new_val matrix rigth? The problem is tha what you are trying to do only works for vectors, not for matrix, so the only way is to transform the matrix into a vector, reorder the values and then go back to matrix like:
vals = [1 ,2; 3, 4];
A=reshape(vals,1,4); % A is a vector [ 1 3 2 4]
new_coord=[2,3,4,1];
B(new_c)=A; %B is [4 1 3 2]
new_val=reshape(B,2,2) %back to matrix
Obtainig new_val=[4 3; 1 2]. Also B=A(new_c) is also allowed but with different coordinates, eventhoug is much easy to think the rigth coordinates in that way.
I am sure there must be a way to include the new_x matrix and transform everything into new_coord

shifting versions of a matrix

I have a m-by-n matrix and I want to shift each row elements k no. of times (" one resultant matrix for each one shift so a total of k matrices corresponding to each row shifts ")(k can be different for different rows and 0<=k<=n) and want to index all the resultant matrices corresponding to each individual shift.
Eg: I have the matrix: [1 2 3 4; 5 6 7 8; 2 3 4 5]. Now, say, I want to shift row1 by 2 times (i.e. k=2 for row1) and row2 by 3times (i.e. k=3 for row2) and want to index all the shifted versions of matrices (It is similar to combinatorics of rows but with limited and diffeent no. of shifts to each row).
Can someone help to write up the code? (please help to write the general code but not for the example I mentioned here)
I found the following question useful to some extent, but it won't solve my problem as my problem looks like a special case of this problem:
Matlab: How to get all the possible different matrices by shifting it's rows (Update: each row has a different step)
See if this works for you -
%// Input m-by-n matrix
A = rand(2,5) %// Edit this to your data
%// Initialize shifts, k for each row. The number of elements would be m.
sr = [2 3]; %// Edit this to your data
[m,n] = size(A); %// Get size
%// Get all the shits in one go
sr_ind = arrayfun(#(x) 0:x,sr,'un',0); %//'
shifts = allcomb(sr_ind{:},'matlab')'; %//'
for k1 = 1:size(shifts,2)
%// Get shift to be used for each row for each iteration
shift1 = shifts(:,k1);
%// Get circularly shifted column indices
t2 = mod(bsxfun(#minus,1:n,shift1),n);
t2(t2==0) = n;
%// Get the linear indices and use them to index into input to get the output
ind = bsxfun(#plus,[1:m]',(t2-1)*m); %//'
all_matrices = A(ind) %// outputs
end
Please note that this code uses MATLAB file-exchange code allcomb.
If your problem in reality is not more complex than what you showed us, it can be done by a double loop. However, i don't like my solution, because you would need another nested loop for each row you want to shift. Also it generates all shift-combinations from your given k-numbers, so it has alot of overhead. But this can be a start:
% input
m = [1 2 3 4; 5 6 7 8; 2 3 4 5];
shift_times = {0:2, 0:3}; % 2 times for row 1, 3 times for row 2
% desird results
desired_matrices{1} = [4 1 2 3; 5 6 7 8; 2 3 4 5];
desired_matrices{2} = [3 4 1 2; 5 6 7 8; 2 3 4 5];
desired_matrices{3} = [1 2 3 4; 8 5 6 7; 2 3 4 5];
desired_matrices{4} = [4 1 2 3; 8 5 6 7; 2 3 4 5];
desired_matrices{5} = [3 4 1 2; 8 5 6 7; 2 3 4 5];
% info needed:
[rows, cols] = size(m);
count = 0;
% make all shift combinations
for shift1 = shift_times{1}
% shift row 1
m_shifted = m;
idx_shifted = [circshift([1:cols]',shift1)]';
m_shifted(1, :) = m_shifted(1, idx_shifted);
for shift2 = shift_times{2}
% shift row 2
idx_shifted = [circshift([1:cols]',shift2)]';
m_shifted(2, :) = m_shifted(r_s, idx_shifted);
% store them
store{shift1+1, shift2+1} = m_shifted;
end
end
% store{i+1, j+1} stores row 1 shifted by i and row 2 shifted by j
% example
all(all(store{2,1} == desired_matrices{1})) % row1: 1, row2: 0
all(all(store{2,2} == desired_matrices{4})) % row1: 1, row2: 1
all(all(store{3,2} == desired_matrices{5})) % row1: 2, row2: 1

average 3rd column when 1st and 2nd column have same numbers

just lets make it simple, assume that I have a 10x3 matrix in matlab. The numbers in the first two columns in each row represent the x and y (position) and the number in 3rd columns show the corresponding value. For instance, [1 4 12] shows that the value of function in x=1 and y=4 is equal to 12. I also have same x, and y in different rows, and I want to average the values with same x,y. and replace all of them with averaged one.
For example :
A = [1 4 12
1 4 14
1 4 10
1 5 5
1 5 7];
I want to have
B = [1 4 12
1 5 6]
I really appreciate your help
Thanks
Ali
Like this?
A = [1 4 12;1 4 14;1 4 10; 1 5 5;1 5 7];
[x,y] = consolidator(A(:,1:2),A(:,3),#mean);
B = [x,y]
B =
1 4 12
1 5 6
Consolidator is on the File Exchange.
Using built-in functions:
sparsemean = accumarray(A(:,1:2), A(:,3).', [], #mean, 0, true);
[i,j,v] = find(sparsemean);
B = [i.' j.' v.'];
A = [1 4 12;1 4 14;1 4 10; 1 5 5;1 5 7]; %your example data
B = unique(A(:, 1:2), 'rows'); %find the unique xy pairs
C = nan(length(B), 1);
% calculate means
for ii = 1:length(B)
C(ii) = mean(A(A(:, 1) == B(ii, 1) & A(:, 2) == B(ii, 2), 3));
end
C =
12
6
The step inside the for loop uses logical indexing to find the mean of rows that match the current xy pair in the loop.
Use unique to get the unique rows and use the returned indexing array to find the ones that should be averaged and ask accumarray to do the averaging part:
[C,~,J]=unique(A(:,1:2), 'rows');
B=[C, accumarray(J,A(:,3),[],#mean)];
For your example
>> [C,~,J]=unique(A(:,1:2), 'rows')
C =
1 4
1 5
J =
1
1
1
2
2
C contains the unique rows and J shows which rows in the original matrix correspond to the rows in C then
>> accumarray(J,A(:,3),[],#mean)
ans =
12
6
returns the desired averages and
>> B=[C, accumarray(J,A(:,3),[],#mean)]
B =
1 4 12
1 5 6
is the answer.

Expand matrix based on first row value (MATLAB)

My input is the following:
X = [1 1; 1 2; 1 3; 1 4; 2 5; 1 6; 2 7; 1 8];
X =
1 1
1 2
1 3
1 4
2 5
1 6
2 7
1 8
I am looking to output a new matrix based on the value of the first column. If the value is equal to 1 -- the output will remain the same, when the value is equal to 2 then I would like to output two of the values contained in the second row. Like this:
Y =
1
2
3
4
5
5
6
7
7
8
Where 5 is output two times because the value in the first column is 2 and the same for 7
Here it is (vectorized):
C = cumsum(X(:,1))
A(C) = X(:,2)
D = hankel(A)
D(D==0) = inf
Y = min(D)
Edit:
Had a small bug, now it works.
% untested code:
Y = []; % would be better to pre-allocate
for ii = 1:size(X,1)
Y = [Y; X(ii,2)*ones(X(ii,1),1)];
end