Column combination matrix - matlab

I want to combine columns of matrices, for example,
A=[1,2,3;4,5,6]';B=[1,3,5;2,9,0]';
and I want
C1=[1,2,3;1,3,5]'
C2=[1,2,3;2,9,0]'
C3=[4,5,6;1,3,5]'
C4=[4,5,6;2,9,0]'
How do I do that in matlab? Is there a function that does this?
Thanks!

This should do the trick:
A=[1,2,3;4,5,6]';
B=[1,3,5;2,9,0]';
Cs = [];
index = 0;
for i = 1:length(A(1,:))
for j = 1:length(B(1,:))
index += 1;
Cs(:,:,index) = [A(:,i), B(:,j)];
end
end
Cs

Is this what you want?
[ii, jj] = ndgrid(1:size(A,2));
C = permute(cat(3, A(:,jj), B(:,ii)), [1 3 2]);
The result is a 3D array such that (C(:,:,1) is your C1, etc:
C(:,:,1) =
1 1
2 3
3 5
C(:,:,2) =
1 2
2 9
3 0
C(:,:,3) =
4 1
5 3
6 5
C(:,:,4) =
4 2
5 9
6 0

Related

How to concatenate matrices of unequal dimensions in 3rd dimension in MATLAB?

Like the title says, I have several matrices that I would like to stack in the 3rd dimension. I currently have a solution for stacking unequal vectors horizontally, which reads like so:
A = [1;2;3;4];
B = [1;2];
[i1,j1] = ndgrid(1:size(A,1),1:size(A,2));
[i2,j2] = ndgrid(1:size(B,1),(1:size(B,2))+size(A,2));
plane_stats = accumarray([i1(:),j1(:);i2(:),j2(:)],[A(:);B(:)]);
for any two vectors A and B of unequal size (caveat: I have only tested this for column vectors, stacked horizontally). Wherever there would be a gap, this adds zeros as padding, so the result looks as follows:
1 1
2 2
3 0
4 0
However, I would like to generalize this to 3D so I can stack a third (also unequal) matrix C behind the combination of A and B, like so:
C = [1 2;3 4;5 6]
%some modification of existing ndgrid code
combo(:,:,1) =
1 1
2 2
3 0
4 0
combo(:,:,2) =
1 2
3 4
5 6
0 0
However I am not sure how to extend the solution I currently have. Any and all help/insight is appreciated
I would suggest something like this:
Given unequal sized 2D matrices A, B, C,
A = [1;2;3];
B = [1 2 3];
C = [1 2;3 4;5 6;7 8];
Solution 1
To stack at the 3rd dimension, we can simply do this:
n_rows = max([size(A,1),size(B,1),size(C,1)]);
n_cols = max([size(A,2),size(B,2),size(C,2)]);
combo = zeros(n_rows, n_cols, 3);
combo(1:size(A,1),1:size(A,2),1) = A
combo(1:size(B,1),1:size(B,2),2) = B
combo(1:size(C,1),1:size(C,2),3) = C
Solution 2
In additionally, to stack matrices incrementally in a loop:
matrices = {A,B,C};
for i = 1:numel(matrices)
mat = matrices{i};
combo(1:size(mat,1),1:size(mat,2),i) = mat;
end
Matlab will automatically padding zeros for you.
Solution 3
Another version without warning:
matrices = {A,B,C};
n_rows = 0; n_cols = 0;
for i = 1:numel(matrices)
mat = matrices{i};
n_rows = max(n_rows, size(mat,1));
n_cols = max(n_cols, size(mat,2));
end
combo = zeros(n_rows, n_cols, numel(matrices));
for i = 1:numel(matrices)
combo(1:size(matrices{i},1),1:size(matrices{i},2),i) = matrices{i};
end
Result
All above 3 solutions resulting the same:
>> combo
combo(:,:,1) =
1 0 0
2 0 0
3 0 0
0 0 0
combo(:,:,2) =
1 2 3
0 0 0
0 0 0
0 0 0
combo(:,:,3) =
1 2 0
3 4 0
5 6 0
7 8 0
Your approach for merging A and B is totally fine, it doesn't require any improvement and you can keep on using it:
A = [1;2;3;4];
B = [1;2];
[i1,j1] = ndgrid(1:size(A,1),1:size(A,2));
[i2,j2] = ndgrid(1:size(B,1),(1:size(B,2))+size(A,2));
plane_stats = accumarray([i1(:),j1(:);i2(:),j2(:)],[A(:);B(:)]);
Now... what's great about Matlab is that matrices are automatically expanded (and zero-padded) whenever the code requires it. Hence, for a given matrix C, all you have to do is:
combo = plane_stats;
[C_m,C_n] = size(C);
combo(1:C_m,1:C_n,2) = C;
A few examples:
combo = plane_stats;
C = [1 2; 3 4; 5 6];
[C_m,C_n] = size(C);
combo(1:C_m,1:C_n,2) = C
combo(:,:,1) =
1 1
2 2
3 0
4 0
combo(:,:,2) =
1 2
3 4
5 6
0 0
combo = plane_stats;
C = [1 2; 3 4; 5 6; 7 8; 9 10];
[C_m,C_n] = size(C);
combo(1:C_m,1:C_n,2) = C
combo(:,:,1) =
1 1
2 2
3 0
4 0
0 0
combo(:,:,2) =
1 2
3 4
5 6
7 8
9 10
combo = plane_stats;
C = [1 2 3; 3 4 5; 5 6 7; 7 8 9; 10 11 12; 13 14 15];
[C_m,C_n] = size(C);
combo(1:C_m,1:C_n,2) = C
combo(:,:,1) =
1 1 0
2 2 0
3 0 0
4 0 0
0 0 0
0 0 0
combo(:,:,2) =
1 2 3
3 4 5
5 6 7
7 8 9
10 11 12
13 14 15
combo = plane_stats;
C = [1; 2];
[C_m,C_n] = size(C);
combo(1:C_m,1:C_n,2) = C
combo(:,:,1) =
1 1
2 2
3 0
4 0
combo(:,:,2) =
1 0
2 0
0 0
0 0

Find and replace the rows of an array having repeated number by a fixed given row

I have a matrix having rows with repeated numbers. I want to find those rows and replace them with a dummy row so as to keep the number of rows of the matrix constant.
Dummy_row = [1 2 3]
(5x3) Matrix A
A = [2 3 6;
4 7 4;
8 7 2;
1 3 1;
7 8 2]
(5x3) Matrix new_A
new_A = [2 3 6;
1 2 3;
8 7 2;
1 2 3;
7 8 2]
I tried the following which deleted the rows having repeated numbers.
y = [1 2 3]
w = sort(A,2)
v = all(diff(t,1,2)~=0|w(:,1:2)==0,2) % When v is zero, the row has repeated numbers
z = A(w,:)
Can you please help?
bsxfun based solution -
%// Create a row mask of the elements that are to be edited
mask = any(sum(bsxfun(#eq,A,permute(A,[1 3 2])),2)>1,3);
%// Setup output variable and set to-be-edited rows as copies of [1 2 3]
new_A = A;
new_A(mask,:) = repmat(Dummy_row,sum(mask),1)
Code run -
A =
2 3 6
4 7 4
8 7 2
1 3 1
7 8 2
new_A =
2 3 6
1 2 3
8 7 2
1 2 3
7 8 2
You could use the following:
hasRepeatingNums = any(diff(sort(A, 2), 1, 2)==0, 2);
A(hasRepeatingNums,:) = repmat(Dummy_row, nnz(hasRepeatingNums), 1);
See if this works for you,
A= [ 2 3 6;
4 7 4;
8 7 2;
5 5 5;
1 8 8;
1 3 1;
7 8 2 ];
Dummy_row = [1 2 3];
b = diff(sort(A,2),1,2);
b = sum(b == 0,2);
b = b > 0;
c = repmat(Dummy_row,sum(b),1);
b = b' .* (1:length(b));
b = b(b > 0);
newA = A;
newA(b,:) = c;
gives,
newA =
2 3 6
1 2 3
8 7 2
1 2 3
1 2 3
1 2 3
7 8 2
Edit
Not much change is needed, try this,
Dummy_row = [1 2 3];
b = sum(A == 0,2);
b = b > 0;
c = repmat(Dummy_row,sum(b),1);
b = b' .* (1:length(b));
b = b(b > 0);
newA = A;
newA(b,:) = c;

Making a match-and-append code more efficient without 'for' loop

I am trying to match 1st column of A with 1st to 3rd columns of B, and append corresponding 4th column of B to A.
For example,
A=
1 2
3 4
B=
1 2 4 5 4
1 2 3 5 3
1 1 1 1 2
3 4 5 6 5
I compare A(:,1) and B(:, 1:3)
1 and 3 are in A(:,1)
1 is in the 1st, 2nd, 3rd rows of B(:, 1:3), so append B([1 2 3], 4:end)' to A's 1st row.
3 is in the 2nd and 4th rows of B(:,1:3), so append B([2 4], 4:end)' to A's 2nd row.
So that it becomes:
1 2 5 4 5 3 1 2
3 4 5 3 6 5 0 0
I could code this using only for and if.
clearvars AA A B mem mem2 mem3
A = [1 2 ; 3 4]
B = [1 2 4 5 4; 1 2 3 5 3; 1 1 1 1 2; 3 4 5 6 5]
for n=1:1:size(A,1)
mem = ismember(B(:,[1:3]), A(n,1));
mem2 = mem(:,1) + mem(:,2) + mem(:,3);
mem3 = find(mem2>0);
AA{n,:} = horzcat( A(n,:), reshape(B(mem3,[4,5])',1,[]) ); %'
end
maxLength = max(cellfun(#(x)numel(x),AA));
out = cell2mat(cellfun(#(x)cat(2,x,zeros(1,maxLength-length(x))),AA,'UniformOutput',false))
I am trying to make this code efficient, by not using for and if, but couldn't find an answer.
Try this
a = A(:,1);
b = B(:,1:3);
z = size(b);
b = repmat(b,[1,1,numel(a)]);
ab = repmat(permute(a,[2,3,1]),z);
row2 = mat2cell(permute(sum(ab==b,2),[3,1,2]),ones(1,numel(a)));
AA = cellfun(#(x)(reshape(B(x>0,4:end)',1,numel(B(x>0,4:end)))),row2,'UniformOutput',0);
maxLength = max(cellfun(#(x)numel(x),AA));
out = cat(2,A,cell2mat(cellfun(#(x)cat(2,x,zeros(1,maxLength-length(x))),AA,'UniformOutput',false)))
UPDATE Below code runs in almost same time as the iterative code
a = A(:,1);
b = B(:,1:3);
z = size(b);
b = repmat(b,[1,1,numel(a)]);
ab = repmat(permute(a,[2,3,1]),z);
df = permute(sum(ab==b,2),[3,1,2])';
AA = arrayfun(#(x)(B(df(:,x)>0,4:end)),1:size(df,2),'UniformOutput',0);
AA = arrayfun(#(x)(reshape(AA{1,x}',1,numel(AA{1,x}))),1:size(AA,2),'UniformOutput',0);
maxLength = max(arrayfun(#(x)(numel(AA{1,x})),1:size(AA,2)));
out2 = cell2mat(arrayfun(#(x,i)((cat(2,A(i,:),AA{1,x},zeros(1,maxLength-length(AA{1,x}))))),1:numel(AA),1:size(A,1),'UniformOutput',0));
How about this:
%# example data
A = [1 2
3 4];
B = [1 2 4 5 4
1 2 3 5 3
1 1 1 1 2
3 4 5 6 5];
%# rename for clarity & reshape for algorithm's convenience
needle = permute(A(:,1), [2 3 1]);
haystack = B(:,1:3);
data = B(:,4:end).';
%# Get the relevant rows of 'haystack' for each entry in 'needle'
inds = any(bsxfun(#eq, haystack, needle), 2);
%# Create data that should be appended to A
%# All data and functionality in this loop is local and static, so speed
%# should be optimal.
append = zeros( size(A,1), numel(data) );
for ii = 1:size(inds,3)
newrow = data(:,inds(:,:,ii));
append(ii,1:numel(newrow)) = newrow(:);
end
%# Now append to A, stripping unneeded zeros
A = [A append(:, ~all(append==0,1))]

Divide list of numbers into 3 groups in matlab

I have a list of numbers, [1:9], that I need to divide three groups. Each group must contain at least one number. I need to enumerate all of the combinations (i.e. order does not matter). Ideally, the output is a x by 3 array. Any ideas of how to do this in matlab?
Is this what you want:
x = 1:9;
n = length(x);
T=3;
out = {};
%// Loop over all possible solutions
for k=1:T^n
s = dec2base(k, T, n);
out{k}{T} = [];
for p=1:n
grpIndex = str2num(s(p))+1;
out{k}{grpIndex} = [out{k}{grpIndex} x(p)];
end
end
%// Print result. size of out is the number of ways to divide the input. out{k} contains 3 arrays with the values of x
out
Maybe this is what you want. I'm assuming that the division in groups is "monotonous", that is, first come the elements of the first group, then those of the second etc.
n = 9; %// how many numbers
k = 3; %// how many groups
b = nchoosek(1:n-1,k-1).'; %'// "breaking" points
c = diff([ zeros(1,size(b,2)); b; n*ones(1,size(b,2)) ]); %// result
Each column of c gives the sizes of the k groups:
c =
Columns 1 through 23
1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 5
1 2 3 4 5 6 7 1 2 3 4 5 6 1 2 3 4 5 1 2 3 4 1
7 6 5 4 3 2 1 6 5 4 3 2 1 5 4 3 2 1 4 3 2 1 3
Columns 24 through 28
5 5 6 6 7
2 3 1 2 1
2 1 2 1 1
This produces what I was looking for. The function nchoosekr_rec() is shown below as well.
for x=1:7
numgroups(x)=x;
end
c=nchoosekr_rec(numgroups,modules);
i=1;
d=zeros(1,modules);
for x=1:length(c(:,1))
c(x,modules+1)=sum(c(x,1:modules));
if c(x,modules+1)==length(opt_mods)
d(i,:)=c(x,1:modules);
i=i+1;
end
end
numgroups=[];
for x=1:length(opt_mods)
numgroups(x)=x;
end
count=0;
for x=1:length(d(:,1))
combos=combnk(numgroups,d(x,1));
for y=1:length(combos(:,1))
for z=1:nchoosek(9-d(x,1),d(x,2))
new_mods{count+z,1}=combos(y,:);
numgroups_temp{count+z,1}=setdiff(numgroups,new_mods{count+z,1});
end
count=count+nchoosek(9-d(x,1),d(x,2));
end
end
count=0;
for x=1:length(d(:,1))
for y=1:nchoosek(9,d(x,1))
combos=combnk(numgroups_temp{count+1},d(x,2));
for z=1:length(combos(:,1))
new_mods{count+z,2}=combos(z,:);
new_mods{count+z,3}=setdiff(numgroups_temp{count+z,1},new_mods{count+z,2});
end
count=count+length(combos(:,1));
end
end
function y = nchoosekr_rec(v, n)
if n == 1
y = v;
else
v = v(:);
y = [];
m = length(v);
if m == 1
y = zeros(1, n);
y(:) = v;
else
for i = 1 : m
y_recr = nchoosekr_rec(v(i:end), n-1);
s_repl = zeros(size(y_recr, 1), 1);
s_repl(:) = v(i);
y = [ y ; s_repl, y_recr ];
end
end
end

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