Find the rows of a matrix with conditions concerning the values of certain columns in matlab - matlab

As the title says, I want to find all rows in a Matlab matrix that in certain columns the values in the row are equal with the values in the previous row, or in general, equal in some row in the matrix. For example I have a matrix
1 2 3 4
1 2 8 10
4 5 7 9
2 3 6 4
1 2 4 7
and I want to find the following rows:
1 2 3 4
1 2 3 10
1 2 4 7
How do I do something like that and how do I do it generally for all the possible pairs in columns 1 and 2, and have equal values in previous rows, that exist in the matrix?

Here's a start to see if we're headed in the right direction:
>> M = [1 2 3 4;
1 2 8 10;
4 5 7 9;
2 3 6 4;
1 2 4 7];
>> N = M; %// copy M into a new matrix so we can modify it
>> idx = ismember(N(:,1:2), N(1,1:2), 'rows')
idx =
1
1
0
0
1
>> N(idx, :)
ans =
1 2 3 4
1 2 8 10
1 2 4 7
Then you can remove those rows from the original matrix and repeat.
>> N = N(~idx,:)
N =
4 5 7 9
2 3 6 4

this will give you the results
data1 =[1 2 3 4
1 2 8 10
4 5 7 9
2 3 6 4
1 2 4 7];
data2 = [1 2 3 4
1 2 3 10
1 2 4 7];
[exists,position] = ismember(data1,data2, 'rows')
where the exists vector tells you wheter the row is on the other matrix and position gives you the position...
a less elegant and simpler version would be
array_data1 = reshape (data1',[],1);
array_data2 = reshape (data2',[],1);
matchmatrix = zeros(size(data2,1),size(data1,1));
for irow1 = 1: size(data2,1)
for irow2 = 1: size(data1,1)
matchmatrix(irow1,irow2) = min(data2(irow1,:) == data1(irow2,:))~= 0;
end
end
the matchmatrix is to read as a connectivity matrix where value of 1 indicates which row of data1 matches with which row of data2

Related

Matlab: How to enumerate the possible ways of forming pairs from a list

Suppose I have a list of length 2k, say {1,2,...,2k}. The number of possible ways of grouping the 2k numbers into k (unordered) pairs is n(k) = 1*3* ... *(2k-1). So for k=2, we have the following three different ways of forming 2 pairs
(1 2)(3 4)
(1 3)(2 4)
(1 4)(2 3)
How can I use Matlab to create the above list, i.e., create a matrix of n(k)*(2k) such that each row contains a different way of grouping the list of 2k numbers into k pairs.
clear
k = 3;
set = 1: 2*k;
p = perms(set); % get all possible permutations
% sort each two column
[~, col] = size(p);
for i = 1: 2: col
p(:, i:i+1) = sort(p(:,i:i+1), 2);
end
p = unique(p, 'rows'); % remove the same row
% sort each row
[row, col] = size(p);
for i = 1: row
temp = reshape(p(i,:), 2, col/2)';
temp = sortrows(temp, 1);
p(i,:) = reshape(temp', 1, col);
end
pairs = unique(p, 'rows'); % remove the same row
pairs =
1 2 3 4 5 6
1 2 3 5 4 6
1 2 3 6 4 5
1 3 2 4 5 6
1 3 2 5 4 6
1 3 2 6 4 5
1 4 2 3 5 6
1 4 2 5 3 6
1 4 2 6 3 5
1 5 2 3 4 6
1 5 2 4 3 6
1 5 2 6 3 4
1 6 2 3 4 5
1 6 2 4 3 5
1 6 2 5 3 4
As someone think my former answer is not useful, i post this.
I have the following brute force way of enumerating the pairs. Not particularly efficient. It can also cause memory problem when k>9. In that case, I can just enumerate but not create Z and store the result in it.
function Z = pair2(k)
count = [2*k-1:-2:3];
tcount = prod(count);
Z = zeros(tcount,2*k);
x = [ones(1,k-2) 0];
z = zeros(1,2*k);
for i=1:tcount
for j=k-1:-1:1
if x(j)<count(j)
x(j) = x(j)+1;
break
end
x(j) = 1;
end
y = [1:2*k];
for j=1:k-1
z(2*j-1) = y(1);
z(2*j) = y(x(j)+1);
y([1 x(j)+1]) = [];
end
z(2*k-1:2*k) = y;
Z(i,:) = z;
end
k = 3;
set = 1: 2*k;
combos = combntns(set, k);
[len, ~] = size(combos);
pairs = [combos(1:len/2,:) flip(combos(len/2+1:end,:))];
pairs =
1 2 3 4 5 6
1 2 4 3 5 6
1 2 5 3 4 6
1 2 6 3 4 5
1 3 4 2 5 6
1 3 5 2 4 6
1 3 6 2 4 5
1 4 5 2 3 6
1 4 6 2 3 5
1 5 6 2 3 4
You can also use nchoosek instead of combntns. See more at combntns or nchoosek

How to correct a vector index?

Ok so I am clustering data into clusters which are then indexed using a column. The data is in the form of motion vectors and so my data will look like this after being clustered:
[index x y x' y']
for example:
[1 3 5 4 6;
1 4 6 5 7;
2 3 5 4 6;
2 8 9 9 3;
3 2 3 2 4]
in above array there are 3 clusters, with clusters 1 and 2 each containing 2 vectors.
My problem is that I sometimes have to delete clusters based on certain criteria, and may be left with:
[2 3 5 4 6;
2 8 9 9 3;
3 2 3 2 4]
I want to be able to correct the index after deletion, so that it starts at 1 and ends with the number of clusters. So in this case replace the 2s with 1s and 3s with 2s.
Im sure there must be a simple way using a for loop but Ive been trying for a while and can't get ti right?
Assuming your matrix is called data, try this:
>> data = [2 3 5 4 6;
2 8 9 9 3;
3 2 3 2 4]
data =
2 3 5 4 6
2 8 9 9 3
3 2 3 2 4
>> data(:,1) = cumsum(diff(data([1 1:end], 1)) ~= 0) + 1
data =
1 3 5 4 6
1 8 9 9 3
2 2 3 2 4
A simple call to unique will help you do that. You can use the third output of it to assign each unique and new ID using the first column of the new data matrix (index vector) to replace its first column. Also, make sure you use the 'stable' flag so that it assigns IDs in order of occurrence from top to bottom:
%// Data setup
A = [1 3 5 4 6;
1 4 6 5 7;
2 3 5 4 6;
2 8 9 9 3;
3 2 3 2 4];
%-----
B = A(3:end,:); %// Remove first two rows
%// Go through the other IDs and reassign to unique IDs from 1 up to whatever
%// is left
[~,~,id] = unique(B(:,1), 'stable');
%// Replace the first column of the new matrix with the new IDs
B(:,1) = id; %// Replace first column with new IDs
We get:
>> B
B =
1 3 5 4 6
1 8 9 9 3
2 2 3 2 4

Creating an index matrix depending on Reference matrix and matrix of Data matlab

given matrix A of size 6 by 6 contain blocks of numbers,each block of size 2 by 2, and outher reference matrix R of size 2 by 12 also contain blocks of numbers, each block of size 2 by 2. the perpse of the whole process is to form a new matrix, called the Index matrix, contain index's that refer to the position of the blocks within the matrix A based on the order of the blocks within the reference matrix R. and here is an exemple
matrix A:
A =[1 1 2 2 3 3;
1 1 2 2 3 3;
1 1 3 3 4 4;
1 1 3 3 4 4;
4 4 5 5 6 6;
4 4 5 5 6 6 ]
matrix R:
R=[1 1 2 2 3 3 4 4 5 5 6 6;
1 1 2 2 3 3 4 4 5 5 6 6 ]
the new matrix is:
Index =[1 2 3;
1 3 4;
4 5 6]
any ideas ?
With my favourite three guys - bsxfun, permute, reshape for an efficient and generic solution -
blksz = 2; %// blocksize
num_rowblksA = size(A,1)/blksz; %// number of blocks along rows in A
%// Create blksz x blksz sized blocks for A and B
A1 = reshape(permute(reshape(A,blksz,num_rowblksA,[]),[1 3 2]),blksz^2,[])
R1 = reshape(R,blksz^2,1,[])
%// Find the matches with "bsxfun(#eq" and corresponding indices
[valid,idx] = max(all(bsxfun(#eq,A1,R1),1),[],3)
%// Or with PDIST2:
%// [valid,idx] = max(pdist2(A1.',reshape(R,blksz^2,[]).')==0,[],2)
idx(~valid) = 0
%// Reshape the indices to the shapes of blocked shapes in A
Index = reshape(idx,[],num_rowblksA).'
Sample run with more random inputs -
>> A
A =
2 1 1 2
1 2 2 1
1 1 1 1
2 2 2 2
1 2 2 1
1 2 1 1
>> R
R =
2 1 1 1 1 2 2 2 1 1 1 1
2 1 2 1 1 2 2 1 2 2 2 1
>> Index
Index =
0 0
5 5
3 0

How to flip specific parts of a matrix

I am trying to flip certain parts of a matrix. I can explain better by example. Let's say that I have a matrix
M = [ 1 3 6;
1 2 4;
1 7 1;
2 9 0;
2 8 3;
2 4 2;
2 3 1;
3 6 5;
3 4 5;
3 1 9;
4 2 4;
4 8 6 ]
What I'd like to do here is take any rows with an even number in the first column, and flip the third column elements. The end result would look like this:
1 3 6
1 2 4
1 7 1
2 9 1 *
2 8 2 *
2 4 3 *
2 3 0 *
3 6 5
3 4 5
3 1 9
4 2 6 *
4 8 4 *
Note the rows marked with a star have had the elements of the third column flipped upside-down. The problem I'm having is going through each row like in a for-loop you cannot flip an entire set of rows.
Thanks in advance for any help.
Another time accumarray is the way to go:
A =[ 1 3 6 ;
1 2 4 ;
1 7 1 ;
2 9 0 ;
2 8 3 ;
2 4 2 ;
2 3 1 ;
3 6 5 ;
3 4 5 ;
3 1 9 ;
4 2 4 ;
4 8 6 ]
C = accumarray(A(:,1),A(:,3),[],#(x) {flipud(x)} ); %// get groups according to
%// first column and flip it
C = vertcat(C{:}); %// cell array returned,
%// transform to matrix
mask = ~mod(A(:,1),2); %// mask for even numbers
A(mask,3) = C(mask); %// replace masked values of 3rd column with flipped ones
returns:
A =
1 3 6
1 2 4
1 7 1
2 9 1
2 8 2
2 4 3
2 3 0
3 6 5
3 4 5
3 1 9
4 2 6
4 8 4
Certainly slower, but just for fun in two lines:
C = accumarray(A(:,1),A(:,3),[],#(x) {flipud(x)} );
A(~mod(A(:,1),2),3) = getfield( vertcat(C{:}), {~mod(A(:,1),2)});
%// well no, I won't explain it...
Edit: I assumed your first column just contains integers!
I would suggest you break the problem down into stages, something like so:
Identify blocks you wish to flip
Extract them
Flip them
Replace them
You can identify a set of even numbers using the unique and mod functions, then use a for loop over them and use logical indexing to pull/replace the blocks.
Here, try this
a = magic(5); % Some data in a 5x5 matrix
b = 1:numel(a); % Indices of <a>
Rearrange b however you want, then do a=a(b) to reassign a based on the reassigned indices of b. For example, the following code
disp(a(b));
would just return the elements of a in their original order. For your application this code should work:
a = <your matrix data>
b = 1:numel(a);
b = [b(1:27) fliplr(b(28:31)) b(32:34) fliplr(b(35:36))] % Change this part
a = reshape(a(b),size(a))
You should change b based on whatever you need it to do.

Rearranging matrix using col2im in Matlab

My matrix is this:
0 3 0
0 1 2
4 4 1
I use im2col on it like this:
im2col(A, [2 2], 'sliding')
which correctly yields this:
0 0 3 1
0 4 1 4
3 1 0 2
1 4 2 1
I call this matrix K. Now I use col2im to go back to my original matrix. From the Matlab documentation I use this:
col2im(K, [2 2], [5 5],'sliding')
But this doesn't gives me my original matrix A. Reason being [5 5] should be [4 4] to get a 3*3 matrix for starters. But when I do that I get
??? Error using ==> reshape
To RESHAPE the number of elements must not change.
Why is that? And how can I get my original matrix back?
Fromthe docs:
A = col2im(B,[m n],[mm nn],'sliding') rearranges the row vector B into
a matrix of size (mm-m+1)-by-(nn-n+1). B must be a vector of size
1-by-(mm-m+1)*(nn-n+1). B is usually the result of processing the
output of im2col(...,'sliding') using a column compression function
(such as sum).
So that says to me you should be trying something like:
col2im(sum(K), [2 2], [4 4],'sliding')
however that would require K to have 9 columns. I don't have the image processing toolbox handy to test this right now
Your col2im doesn't work because it uses reshape and for that the number of elements of the matrix you wish to reshape (K) and the new one, need to be the same. This is not the case anymore, as through your transformation of A with im2col you obviously changed that. A has 9 and K 16 elements.
So you basically need to get back to a 3*3 matrix again by getting rid of the redundand doubled elements (due to the overlapping 2*2 blocks used in im2col) in K.
For that you could just make a new matrix (C) with the elements that you need:
C = [K([1,3,11;2,4,12;6,8,16])]
As long as you first went from a 3*3 to a 4*4 matrix using the same order of blocks this should work.
Maybe you could tell us more about what you really want to achieve, because I don't see any reason for this in the first place. It may also be possible that you might be better off using other functions instead, but I can only see that if I know what the reasoning behind your question is.
clear
clc
img = double(imread('tire.tif'));
[r c] = size(img);
w = 8;
imgBlock = im2col(img,[w w],'sliding'); imgBlock = imgBlock(:);
[x y] = meshgrid(1:c,1:r);
xx = im2col(x,[w w], 'sliding'); xx = xx(:);
yy = im2col(y,[w w], 'sliding'); yy = yy(:);
img2 = accumarray([yy xx], imgBlock, [], #mean);
figure,imshow(img, []);
figure,imshow(img2,[]);
% random matrix as image
img = randi(10,4)
img =
6 2 2 7
5 8 7 8
1 4 3 5
4 6 7 1
% matrix size
[r c] = size(img)
% patch size
w = 2;
% image to patch
imgBlock = im2col(img,[w w],'sliding')
% image patchs matrix to a vector
imgBlock = imgBlock(:);
r =
4
c =
4
imgBlock =
6 5 1 2 8 4 2 7 3
5 1 4 8 4 6 7 3 7
2 8 4 2 7 3 7 8 5
8 4 6 7 3 7 8 5 1
% index matrix size equal image size
[x y] = meshgrid(1:c,1:r)
% index matric to patchs;to vector
xx = im2col(x,[w w], 'sliding'); xx = xx(:);
yy = im2col(y,[w w], 'sliding'); yy = yy(:);
x =
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
y =
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
% yy :row index xx: column index
% applies the function mean to each subset of elements in imgBlock that have identical subscripts in [yy xx].
img2 = accumarray([yy xx], imgBlock, [], #mean);
img
img2
img =
6 2 2 7
5 8 7 8
1 4 3 5
4 6 7 1
img2 =
6 2 2 7
5 8 7 8
1 4 3 5
4 6 7 1
% [col,row,value]
a = [xx,yy,imgBlock]
a =
1 1 6
1 2 5
2 1 2
2 2 8
1 2 5
1 3 1
2 2 8
2 3 4
1 3 1
1 4 4
2 3 4
2 4 6
2 1 2
2 2 8
3 1 2
3 2 7
2 2 8
2 3 4
3 2 7
3 3 3
2 3 4
2 4 6
3 3 3
3 4 7
3 1 2
3 2 7
4 1 7
4 2 8
3 2 7
3 3 3
4 2 8
4 3 5
3 3 3
3 4 7
4 3 5
4 4 1
% The number of times that img(2,2) occurs in the matrix img
a(xx == 2 & yy == 2,:)
ans =
2 2 8
2 2 8
2 2 8
2 2 8