MATLAB - re-arrange matrix by vertically concatenating submatrices - matlab

I am having trouble with the following task:
Suppose a 3x6 matrix:
A =
0.2787 0.2948 0.4635 0.8388 0.0627 0.0435
0.6917 0.1185 0.3660 0.1867 0.2383 0.7577
0.6179 0.7425 0.0448 0.4009 0.9377 0.4821
What I would like to do is to divide the matrix into blocks, like this:
A =
0.2787 0.2948 | 0.4635 0.8388 | 0.0627 0.0435
0.6917 0.1185 | 0.3660 0.1867 | 0.2383 0.7577
0.6179 0.7425 | 0.0448 0.4009 | 0.9377 0.4821
and vertically concatenate these blocks to get the final result:
0.2787 0.2948
0.6917 0.1185
0.6179 0.7425
0.4635 0.8388
0.3660 0.1867
0.0448 0.4009
0.0627 0.0435
0.2383 0.7577
0.9377 0.4821
I think if I can get help with this, then I can perhaps
do it for arbitrary matrices A. I can solve the above
problem using for-loops, but I am looking for a vectorised solution.
Thanks in advance!
N.

Consider the following:
A = rand(3,6);
blkSz = 2;
C = mat2cell(A, size(A,1), blkSz*ones(1,size(A,2)/blkSz));
C = cat(1,C{:})
This assumes that size(A,2) is evenly divisible by blkSz

This works where your matrix is A and what you want is D
C = mat2cell(A,[3],[2 2 2])
D = cat(1,C{:})

It's possible to do it without cell2mat, (only with reshapes and permute) and thus a lot faster!
You need to use the "3rd dimension". It's similar to what is described in
split long 2D matrix into the third dimension.
Here is the solution for the above matrix:
A1 = reshape(A, 3, 2, []); % 3rd dimension is numel(A)/2/3
A2 = permute(A1, [2 1 3]); % transpose 1st and 2nd dimension
Ans= reshape(A2, 2, [])' ; % note the transpose
For a matrix of this size, the difference in running time is negligible. However, for a large matrix, the difference is more than an order of magnitude:
A=rand(3, 2*10000);
%% good method
tic
A1 = reshape(A, 3, 2, []); %3rd dimension is numel(A)/2/3
A2 = permute(A1, [2 1 3]);
A3 = reshape(A2, 2, [])' ; %note the transpose'
toc
%% mat2cell method
tic
blkSz = 2;
C = mat2cell(A, size(A,1), blkSz*ones(1,size(A,2)/blkSz));
B3 = cat(1,C{:});
toc
%% make sure the answer is the same:
assert(max(A3(:)-B3(:))==0)
output:
>> Elapsed time is 0.001202 seconds.
>> Elapsed time is 0.043115 seconds.

How about this:
width = 2;
m = length(A(:))/width;
fn = #(i) reshape(A(:, i:width:end), m, 1);
B = cell2mat(arrayfun(fn, 1:width, 'UniformOutput', false));
Just specify how many columns you want at a time in the width variable.

By concatenating vertically, matrix width divisible by 3 assumed:
B = [ A(:,1:(size(A,2)/3)); A(:,size(A,2)/3+1:size(A,2)/3*2); A(:,size(A,2)/3*2+1:end) ];

Related

3D matrix multiplication by 2D matrix

I have a NxNx4 matrix(A) and a 4x4 matrix (B). I need to multiply the vector a composed by the four elements of the first matrix A, let's say
a = A(1,1,1)
A(1,1,2)
A(1,1,3)
A(1,1,4)
by the 4x4 matrix B but I'm not sure if there is a faster and clever solution than using a for loop to build the vector a. Does exist a way to do this computation with few lines of code?
I built A like
A(:,:,1) = rand(20);
A(:,:,2) = rand(20);
A(:,:,3) = rand(20);
A(:,:,4) = rand(20);
and the matrix B
B = rand(4);
now I want to multiply B with
B*[A(1,1,1);A(1,1,2);A(1,1,3);A(1,1,4)]
This, for each element of A
B*[A(1,2,1);A(1,2,2);A(1,2,3);A(1,2,4)]
B*[A(1,3,1);A(1,3,2);A(1,3,3);A(1,3,4)]
...
You can do this with a simple loop, note loops aren't necessarily slow in newer MATLAB versions. Mileage may vary.
Loops have the advantage of improving code readability, it's extremely clear what's happening here:
% For matrix A of size N*N*4
C = zeros( size( A ) );
for ii = 1:N
for jj = 1:N
C( ii, jj, : ) = B * reshape( A( ii, jj, : ), [], 1 );
end
end
A loop solution that has good performance specially when N is large:
s = size(A, 3);
C = A(:,:,1) .* reshape(B(:,1),1,1,[]);
for k = 2:s
C = C + A(:,:,k) .* reshape(B(:,k),1,1,[]);
end
I think this does what you want:
C = permute(sum(bsxfun(#times, permute(B, [3 4 2 1]), A), 3), [1 2 4 3]);
Check:
>> C(1,2,:)
ans(:,:,1) =
1.501739582138850
ans(:,:,2) =
1.399465238902816
ans(:,:,3) =
0.715531734553844
ans(:,:,4) =
1.617019921519029
>> B*[A(1,2,1);A(1,2,2);A(1,2,3);A(1,2,4)]
ans =
1.501739582138850
1.399465238902816
0.715531734553844
1.617019921519029

How to replace this for loop with matrix operations in matlab

I know that writing a for loop in Matlab is typically not efficient.
Now I am trying to replace a nested for loop with a better option.
Here is the nested loop,
for i = 1: size(A,1)
for j = 1: size(B,1)
S(i,j, :) = c*(A(i,:)*a - B(j,:)*b);
end
end
What operation should I use? (I was thinking about Cartesian product implementation)
Try the following
AA = permute(A * a, [1, 3, 2]);
BB = permute(B * b, [3, 1, 2]);
CC = c * bsxfun(#minus, AA, BB);
nA = size(A,1);
nB = size(B,1);
Ar = repmat(A, nB, 1); %// repeat A along rows
Br = B(ceil(1/nA:1/nA:nB), :); %// stretch B along rows
S = c*(Ar*a-Br*b); %// do the computations
S = reshape(permute(S, [1 3 2]), nA, nB, []); %// put into shape
A=(1:20)'*ones(1,10);
size(A) % 20,10
a=ones(10,1)*(1:5);
size(a) %10,5
B=ones(3,1)*(1:20);
size(B) %3,20
b=ones(20,1)*(1:5);
size(b) %20,5
c=1;
Aa=A*a;
size(Aa) %20,5
Bb=B*b;
size(Bb) %3,5
na=size(Aa,1);
nb=size(Bb,1);
Ia=(1:na)'* ones(1,nb);
%Ia=1;2;3..nb;1;2;3..nb na times
Ia=reshape(Ia,na*nb,1);
%Ib=1;1;natimes;2;2;2 natimes...nb;nb;nb...natimes
Ib=ones(na,1)*(1:nb);
Ib=reshape(Ib,na*nb,1);
S=(Aa(Ia,:)-Bb(Ib,:))*c;
S=reshape(S,[na nb size(Aa,2)]);

Can I do the following fast in Matlab?

I have three matrices in Matlab, A which is n x m, B which is p x m and C which is r x n.
Say we initialize some matrices using:
A = rand(3,4);
B = rand(2,3);
C = rand(5,4);
The following two are equivalent:
>> s=0;
>> for i=1:3
for j=1:4
s = s + A(i,j)*B(:,i)*C(:,j)';
end;
end
>> s
s =
2.6823 2.2440 3.5056 2.0856 2.1551
2.0656 1.7310 2.6550 1.5767 1.6457
>> B*A*C'
ans =
2.6823 2.2440 3.5056 2.0856 2.1551
2.0656 1.7310 2.6550 1.5767 1.6457
The latter being much more efficient.
I can't find any efficient version for the following variant of the loop:
s=0;
for i=1:3
for j=1:4
x = A(i,j)*B(:,i)*C(:,j)';
s = s + x/sum(sum(x));
end;
end
Here, the matrices being added are normalized by the sum of their values after each step.
Any ideas how to make this efficient like the matrix multiplication above? I thought maybe accumarray could help, but not sure how.
You can do it efficiently with bsxfun:
aux1 = bsxfun(#times, permute(B,[1 3 2]), permute(C,[3 1 4 2]));
aux2 = sum(sum(aux1,1),2);
s = sum(sum(bsxfun(#rdivide, aux1, aux2),3),4);
Note that, because of the normalization, the result is independent of A, assuming it doesn't contain any zero entries (if it does the result is undefined).

special add in matlab [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to Add a row vector to a column vector like matrix multiplication
I have a nx1 vector and a 1xn vector. I want to add them in a special manner like matrix multiplication in an efficient manner (vectorized):
Example:
A=[1 2 3]'
B=[4 5 6]
A \odd_add B =
[1+4 1+5 1+6
2+4 2+5 2+6
3+4 3+5 3+6
]
I have used bsxfun in MATLAB, but I think it is slow. Please help me...
As mentioned by #b3. this would be an appropriate place to use repmat. However in general, and especially if you are dealing with very large matrices, bsxfun normally makes a better substitute. In this case:
>> bsxfun(#plus, [1,2,3]', [4,5,6])
returns the same result, using about a third the memory in the large-matrix limit.
bsxfun basically applies the function in the first argument to every combination of items in the second and third arguments, placing the results in a matrix according to the shape of the input vectors.
I present a comparison of the different methods mentioned here. I am using the TIMEIT function to get robust estimates (takes care of warming up the code, average timing on multiple runs, ..):
function testBSXFUN(N)
%# data
if nargin < 1
N = 500; %# N = 10, 100, 1000, 10000
end
A = (1:N)';
B = (1:N);
%# functions
f1 = #() funcRepmat(A,B);
f2 = #() funcTonyTrick(A,B);
f3 = #() funcBsxfun(A,B);
%# timeit
t(1) = timeit( f1 );
t(2) = timeit( f2 );
t(3) = timeit( f3 );
%# time results
fprintf('N = %d\n', N);
fprintf('REPMAT: %f, TONY_TRICK: %f, BSXFUN: %f\n', t);
%# validation
v{1} = f1();
v{2} = f2();
v{3} = f3();
assert( isequal(v{:}) )
end
where
function C = funcRepmat(A,B)
N = numel(A);
C = repmat(A,1,N) + repmat(B,N,1);
end
function C = funcTonyTrick(A,B)
N = numel(A);
C = A(:,ones(N,1)) + B(ones(N,1),:);
end
function C = funcBsxfun(A,B)
C = bsxfun(#plus, A, B);
end
The timings:
>> for N=[10 100 1000 5000], testBSXFUN(N); end
N = 10
REPMAT: 0.000065, TONY_TRICK: 0.000013, BSXFUN: 0.000031
N = 100
REPMAT: 0.000120, TONY_TRICK: 0.000065, BSXFUN: 0.000085
N = 1000
REPMAT: 0.032988, TONY_TRICK: 0.032947, BSXFUN: 0.010185
N = 5000
REPMAT: 0.810218, TONY_TRICK: 0.824297, BSXFUN: 0.258774
BSXFUN is a clear winner.
In matlab vectorization, there is no substitute for Tony's Trick in terms of speed in comparison to repmat or any other built in Matlab function for that matter. I am sure that the following code must be fastest for your purpose.
>> A = [1 2 3]';
>> B = [4 5 6];
>> AB_sum = A(:,ones(3,1)) + B(ones(3,1),:);
The speed differential will be much more apparent (at LEAST an order of magnitude) for larger size of A and B. See this test I conducted some time ago to ascertain the superiority of Tony's Trick over repmatin terms of time consumption.
REPMAT is your friend:
>> A = [1 2 3]';
>> B = [4 5 6];
>> AplusB = repmat(A, 1, 3) + repmat(B, 3, 1)
AplusB =
5 6 7
6 7 8
7 8 9

How to use indices returned from 'min' function in Matlab?

How can I use the indices returned from 'min' function in matlab to retrieve its data in a 3D matrix on third dimension?
for example, I have the code below:
%a is a 3D matrix
[res, index] = min(a, [], 3);
An I want to access the min elements using index, something like:
a(index);
NOTE: I want not to use res variable
to get them all:
a=rand(3,2,3);
[res, index] = min(a, [], 3);
sizeA=size(a);
sizeA12 = prod(sizeA(1:2));
lin_idx = sub2ind([sizeA12 sizeA(3)],1:sizeA12,index(:)');
a(lin_idx)
ans =
0.0344 0.0971 0.171 0.695 0.0318 0.187
>> res(:)'
ans =
0.0344 0.0971 0.171 0.695 0.0318 0.187
More general approach
a=rand(3,2,3); % sample data
dim_min = 2; % dimension along to take the minimum
[res, index] = min(a, [], dim_min);
sizeA = size(a);
sizeAstart = prod(sizeA(1:dim_min-1));
sizeAend = prod(sizeA(dim_min+1:end));
idstart = repmat(1:sizeAstart,1,sizeAend);
idend = repmat(1:sizeAend ,1,sizeAstart);
lin_idx = sub2ind([sizeAstart sizeA(dim_min) sizeAend ],idstart,index(:)',idend);
a(lin_idx)
You can also reshape the result to get it in the same dimensions as the original matrix (with omission of the minimized dimension):
reshape(a(lin_idx),sizeA([1:dim_min-1 dim_min+1:end]))
Works for any size of data matrix or any value dim_min (as long as 1<=dim_min<=ndims(a) of course)
Unfortunatly I haven't got the required reputation points to comment Gunther's (almost six years old) answer, but there is an error here, which makes the "More general approach" incorrect:
The "idend"-index should not be duplicated with repmat, but with repelem instead, to get the required 1 1 1 2 2 2 3 3 3 order for this index. So here's a functional example of Gunther's approach:
More general approach (corrected idend)
a=rand(3,2,3); % sample data
dim_min = 2; % dimension along to take the minimum
[res, index] = min(a, [], dim_min);
sizeA = size(a);
sizeAstart = prod(sizeA(1:dim_min-1));
sizeAend = prod(sizeA(dim_min+1:end));
idstart = repmat(1:sizeAstart,1,sizeAend);
idend = repelem(1:sizeAend ,1,sizeAstart);
lin_idx = sub2ind([sizeAstart sizeA(dim_min) sizeAend ],idstart,index(:)',idend);
% check correct result
isequal(a(lin_idx)',res(:))