MATLAB: Splitting a matrix based on multiple values - matlab

I'm dealing with matrices of this format:
M =
1 1 3
1 1 1
1 2 2
1 2 1
1 2 2
2 1 5
2 1 1
2 2 3
2 2 4
2 2 2
...
What I want to do is extract sub matrices where the values in the first and second column can be grouped such that:
M1 =
1 1 3
1 1 1
M2 =
1 2 2
1 2 1
1 2 2
M3 =
2 1 5
2 1 1
...
I have been trying to think hard about how to index the matrix for this and I have a matrix available:
I =
1 1
1 2
2 1
2 2
...
that I could use for indexing. I was wondering if I could use it but I'm not 100% sure how. I don't want to use a for loop since the matrixes can be rather large and the order of complexity can become very large.
Thank you for reading!

This is easily done with unique and accumarray:
M = [ 1 1 3
1 1 1
1 2 2
1 2 1
1 2 2
2 1 5
2 1 1
2 2 3
2 2 4
2 2 2 ]; %// data
[~, ~, u] = unique(M(:,1:2), 'rows'); %// unique labels of rows based on columns 1 and 2
M_split = accumarray(u(:), (1:size(M,1)).', [], #(x){M(sort(x),:)}); %'// group rows
% // based on labels
This gives a cell array containing the partial matrices. In your example,
M_split{1} =
1 1 3
1 1 1
M_split{2} =
1 2 2
1 2 1
1 2 2
M_split{3} =
2 1 5
2 1 1
M_split{4} =
2 2 3
2 2 4
2 2 2

Related

A table with 2 columns where one column increases sequentially while the other column displaying the same number till a limit is reached

I have been trying to write a code where the users enters two numbers in order to get 2 columns. It is very hard to explain by words what I am trying to achieve so here is an example:
If the user inputs a = 1 and b = 1, the following table should be created:
ans =
1 1
If the user inputs a = 2 and b = 2:
ans =
1 1
1 2
2 1
2 2
If the user inputs a = 2 and b = 5:
ans =
1 1
1 2
1 3
1 4
1 5
2 1
2 2
2 3
2 4
2 5
For other values of a and b, the matrix should be constructed according to the above shown sequence.
This can be achieved straight-forward by the use of repelem and repmat:
[repelem((1:a).',b),repmat((1:b).',a,1)]
A more elegant way is using meshgrid and reshape it after:
[A,B] = meshgrid(1:a,1:b);
[A(:),B(:)]
Let's create an anonymous function and test the first approach:
>> fun = #(a,b) [repelem((1:a).',b),repmat((1:b).',a,1)];
>> fun(1,1)
ans =
1 1
>> fun(2,2)
ans =
1 1
1 2
2 1
2 2
>> fun(2,5)
ans =
1 1
1 2
1 3
1 4
1 5
2 1
2 2
2 3
2 4
2 5
Here is an alternative way
for example a = 2 and b = 5
A(1:b*a,1) = reshape(mtimes((1:a).',ones(1,b)).',1,b*a)
A(1:b*a,2) = reshape(mtimes((1:b).',ones(1,a)),1,b*a)
A =
1 1
1 2
1 3
1 4
1 5
2 1
2 2
2 3
2 4
2 5
There is just one logic, in the code below you define a matrix of row size and a and column size b
>> mtimes((1:a).',ones(1,b))
ans =
1 1 1 1 1
2 2 2 2 2
and the next step simply reshapes the matrix column wise for a and row wise for b by taking a transpose
A(1:b*a,1) = reshape(mtimes((1:a).',ones(1,b)).',1,b*a)
A(1:b*a,2) = reshape(mtimes((1:b).',ones(1,a)),1,b*a)

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

Find column vectors contained in B but not in A - MATLAB

I have matrix A of size 10x100 and matrix B with size 10x200.
How to find the column vectors contained in B but not in A? ( A and B do not have the same number of columns)
to elaborate #Cheery's comment with an example.
A=[1;4];
B=[1 2 4;4 5 6];
C=setdiff(B',A','rows')';
more details see http://www.mathworks.com/help/matlab/ref/setdiff.html
You can also use bsxfun here -
Bout = B(:,all(any(bsxfun(#ne,B,permute(A,[1 3 2])),1),3))
Sample run -
A =
2 2 2 2 2
2 2 1 1 1
1 1 2 1 3
B =
3 2 3 2 1 2 2
3 1 3 1 1 1 3
2 3 1 2 1 2 3
Bout =
3 3 1 2
3 3 1 3
2 1 1 3

Iterate one vector through another in Matlab Part 2

This is a similar request to my post at Iterate one vector through another in Matlab
I am using Luis' suggestion with the following code:
E=[1 2 3 4 5 6 7 8 9 10];
A = [1 2];
s = size(E,2);
t = numel(A);
C = cell(1,s);
[C{:}] = ndgrid(A);
C = cat(s+1, C{:});
C = fliplr(reshape(C, t^s, s));
This produces a good result for C as a 1024x10 matrix with all possible permutations of 1 and 2 to a length of 10 columns. What I want to do is remove any rows that are not in increasing order. For example now I get:
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 2
1 1 1 1 1 1 1 1 2 1
1 1 1 1 1 1 1 1 2 2
All are valid except for the third row since it goes from 2 to back to 1.
I have code to get the desired result, but it is very slow and inefficient.
E=[1 2 3 4 5 6 7 8 9 10];
A = [1 2];
s = size(E,2);
t = numel(A);
C = cell(1,s);
[C{:}] = ndgrid(A);
C = cat(s+1, C{:});
C = fliplr(reshape(C, t^s, s));
min=0;
for row=1:size(C,1)
for col=1:size(C,2)
if(C(row,col)>min)
min=C(row,col);
elseif(C(row,col)<min)
C(row,:)=0;
continue;
end
end
min=0;
end
C = C(any(C,2),:); %remove all zero rows
The desired output is now:
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 2
1 1 1 1 1 1 1 1 2 2
1 1 1 1 1 1 1 2 2 2
1 1 1 1 1 1 2 2 2 2
1 1 1 1 1 2 2 2 2 2
1 1 1 1 2 2 2 2 2 2
1 1 1 2 2 2 2 2 2 2
1 1 2 2 2 2 2 2 2 2
1 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2
Any ideas on how to optimize my code so I do not need to use nested loops?
The super-simple but not-quite-so-obvious solution via a couple of row-wise operations:
d = diff(C, [], 2);
m = min(d, [], 2);
C = C(m>=0, :);
Of course, in this particular example it would be far easier to just generate the resulting matrix directly:
C = flipud(triu(ones(s+1,s).*(max(A)-min(A))) + min(A));
but I assume you're also interested in less trivial values of A ;)

MATLAB: How do you add all the first n rows of a matrix within the same column, and save the result to the last row?

It was hard to phrase the question, but here's an example of what I'm looking for:
1 2 3 4
2 1 1 1
2 2 3 1
0 0 0 0
and in column one, I add all the value of all of the first three rows and save it to the third and so on, so that it becomes:
1 2 3 4
2 1 1 1
2 2 3 1
5 5 7 6
I think you can use sum:
octave:23> m = [1 2 3 4; 2 1 1 1; 2 2 3 1; 0 0 0 0]
m =
1 2 3 4
2 1 1 1
2 2 3 1
0 0 0 0
octave:24> m(length(m), :) = sum(m)
m =
1 2 3 4
2 1 1 1
2 2 3 1
5 5 7 6