Multiplying a small matrix by a big matrix - matlab

I'm trying to multiply every element in a small matrix (let's say 2x2) with every position in a big matrix (let's say 4x4), element by element.
So I want:
1 2 3 4 1 0 3 0
1 0 1 2 3 4 0 0 0 0
0 0 'x' 1 2 3 4 = 1 0 3 0
1 2 3 4 0 0 0 0
The small matrix is applied as many times as it fits, and the multiplication is element by element. I've tried a bunch of loops, but that doesn't feel right in MATLAB, there must be prettier ways of doing it?

One possibility is to use repmat to repeat the small matrix as many times as necessary:
C = repmat(A,size(B,1)/size(A,1),size(B,2)/size(A,2)).*B
Another possibility, which avoids repmat: cut up the large matrix, arrange the pieces in the third and fourth dimensions, and use bsxfun to do the multiplication:
[m n] = size(A);
[M N] = size(B);
T = permute(reshape(B,M,n,[]), [2 1 3]);
T = permute(reshape(T,n,m,[],size(T,3)),[2 1 3 4]);
C = cell2mat(squeeze(mat2cell(bsxfun(#times,T,A),m,n,ones(1,M/m),ones(1,N/n))));
(The two lines T = ... do the cutting, and are due to A. Donda.)
The advantage of this approach is that, if memory is an issue, you can overwrite B instead of defining T, thus saving memory:
[m n] = size(A);
[M N] = size(B);
B = permute(reshape(B,M,n,[]),[2 1 3]);
B = permute(reshape(B,n,m,[],size(B,3)),[2 1 3 4]);
C = cell2mat(squeeze(mat2cell(bsxfun(#times,B,A),m,n,ones(1,M/m),ones(1,N/n))));

If you have the image processing toolbox, you can try blkproc:
>> A = magic(4)
A =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
>> B = [1 0; 0 0];
>> C = blkproc(A,size(B),#(x) x.*B)
C =
16 0 3 0
0 0 0 0
9 0 6 0
0 0 0 0

Related

Transform a matrix to a stacked vector where all zeroes after the last non-zero value per row are removed

I have a matrix with some zero values I want to erase.
a=[ 1 2 3 0 0; 1 0 1 3 2; 0 1 2 5 0]
>>a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
However, I want to erase only the ones after the last non-zero value of each line.
This means that I want to retain 1 2 3 from the first line, 1 0 1 3 2 from the second and 0 1 2 5 from the third.
I want to then store the remaining values in a vector. In the case of the example this would result in the vector
b=[1 2 3 1 0 1 3 2 0 1 2 5]
The only way I figured out involves a for loop that I would like to avoid:
b=[];
for ii=1:size(a,1)
l=max(find(a(ii,:)));
b=[b a(ii,1:l)];
end
Is there a way to vectorize this code?
There are many possible ways to do this, here is my approach:
arotate = a' %//rotate the matrix a by 90 degrees
b=flipud(arotate) %//flips the matrix up and down
c= flipud(cumsum(b,1)) %//cumulative sum the matrix rows -and then flip it back.
arotate(c==0)=[]
arotate =
1 2 3 1 0 1 3 2 0 1 2 5
=========================EDIT=====================
just realized cumsum can have direction parameter so this should do:
arotate = a'
b = cumsum(arotate,1,'reverse')
arotate(b==0)=[]
This direction parameter was not available on my 2010b version, but should be there for you if you are using 2013a or above.
Here's an approach using bsxfun's masking capability -
M = size(a,2); %// Save size parameter
at = a.'; %// Transpose input array, to be used for masked extraction
%// Index IDs of last non-zero for each row when looking from right side
[~,idx] = max(fliplr(a~=0),[],2);
%// Create a mask of elements that are to be picked up in a
%// transposed version of the input array using BSXFUN's broadcasting
out = at(bsxfun(#le,(1:M)',M+1-idx'))
Sample run (to showcase mask usage) -
>> a
a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
>> M = size(a,2);
>> at = a.';
>> [~,idx] = max(fliplr(a~=0),[],2);
>> bsxfun(#le,(1:M)',M+1-idx') %// mask to be used on transposed version
ans =
1 1 1
1 1 1
1 1 1
0 1 1
0 1 0
>> at(bsxfun(#le,(1:M)',M+1-idx')).'
ans =
1 2 3 1 0 1 3 2 0 1 2 5

Permute the Matrix with Given Index

Given A is symmetry matrix with size n and
A =
1 2 3 4 5 % The Position
1 [0 5 2 4 1
2 5 0 3 0 2
3 2 3 0 0 0
4 4 0 0 0 5
5 1 2 0 5 0]
B is a row vector that permute the matrix A row and column
B = [2 4 1 5 3]
The output that I want is
C =
2 4 1 5 3 % The New Position given by Matrix B
2 [0 0 5 2 3
4 0 0 4 5 0
1 5 4 0 1 2
5 2 5 1 0 0
3 3 0 2 0 0]
I can get the output by using simple for loop
index = [2,4,1,5,3];
C = zeros(5,5);
for i = 1:5
for j = 1:5
% Position of in square matrix n
% (i,j) = (i-1)*n + j
C(i,j) = A((index(i)-1)*5+index(j));
end
end
However, if I want to permute a matrix with size 80x80, then I need to run 1600 times in order to get the output.
Is there any simple trick to do it instead of using for loop?
You should be able to rearrange your matrices as follows:
C = A(index,index);
This rearranges each dimension according to the index variable independently.

take from matrix non-zero rows per column

I have a matrix A. Suppose it is:
A=[1 0 8;
0 0 2;
3 0 5;
4 8 0;
0 5 3;
6 1 3;
1 6 5;
0 7 1]
and I want to get the non-zero rows subscripts per column in a new matrix.
In my example that will be,
B = [ 1 3 4 6 7 0 0 0;
4 5 6 7 8 0 0 0;
1 2 3 5 6 7 8 0]
In terms of just size, if A=(m,n), B will be B=(n,m) (the transpose). In terms of content, B contains the subscripts of the non-zero rows in A as described above.
Here is one way:
mask = ~sort(~A); %// destination of row indexes in output
[ii,~]=find(A); %// get the row indexes
B = zeros(size(A));
B(mask) = ii; B=B.' %'//write indexes to output and transpose
I think this does what you want (get the non-zero row indices per column). It's very similar to this other question:
[r c] = size(A);
M = bsxfun(#times, A~=0, 1:size(A,2)).'; %'// substitute values by indices
[~, rows] = sort(M~=0,'descend'); %//'' push zeros to the end
cols = repmat(1:r,c,1);
ind = sub2ind([c r],rows(:),cols(:));
B = repmat(NaN,c,r);
B(:) = M(ind).';
B = B.';
Result:
>> B
B =
1 3 4 6 7 0 0 0
4 5 6 7 8 0 0 0
1 2 3 5 6 7 8 0

Find the first N non-zero elements in each row of a matrix

I have a matrix in MATLAB with zeroes and I would like to get another matrix with the first N non-zero elements in each row. Let's say for example N = 3, and the matrix is
A = [ 0 0 2 0 6 7 9;
3 2 4 7 0 0 6;
0 1 0 3 4 8 6;
1 2 0 0 0 1 3]
I'd like the result to be:
B = [2 6 7;
3 2 4;
1 3 4;
1 2 1]
I have a huge matrix so I would like to do it without a loop, could you please help me? Thanks a lot!
Since MATLAB stores a matrix according to column-major order, I first transpose A, bubble up the non-zeros, and pick the first N lines, and transpose back:
N = 3;
A = [ 0 0 2 0 6 7 9;
3 2 4 7 0 0 6;
0 1 0 3 4 8 6;
1 2 0 0 0 1 3];
Transpose and preallocate output B
At = A';
B = zeros(size(At));
At =
0 3 0 1
0 2 1 2
2 4 0 0
0 7 3 0
6 0 4 0
7 0 8 1
9 6 6 3
Index zeros
idx = At == 0;
idx =
1 0 1 0
1 0 0 0
0 0 1 1
1 0 0 1
0 1 0 1
0 1 0 0
0 0 0 0
Bubble up the non-zeros
B(~sort(idx)) = At(~idx);
B =
2 3 1 1
6 2 3 2
7 4 4 1
9 7 8 3
0 6 6 0
0 0 0 0
0 0 0 0
Select first N rows and transpose back
B(1:N,:)'
You can do the bubbling in row-major order, but you would need to retrieve the row and column subscripts with find, and do some sorting and picking there. It becomes more tedious and less readable.
Using accumarray with no loops:
N = 3;
[ii,jj] = find(A); [ii,inds]=sort(ii); jj = jj(inds);
lininds = ii+size(A,1)*(jj-1);
C = accumarray(ii,lininds,[],#(x) {A(x(1:N)')}); %' cell array output
B = vertcat(C{:})
B =
2 6 7
3 2 4
1 3 4
1 2 1
Usually I don't go with a for loop solution, but this is fairly intuitive:
N = 3;
[ii,jj] = find(A);
B = zeros(size(A,1),N);
for iRow = 1:size(A,1),
nzcols = jj(ii==iRow);
B(iRow,:) = A(iRow,nzcols(1:N));
end
Since you are guaranteed to have more than N nonzeros per row of A, that should get the job done.
One-liner solution:
B = cell2mat(cellfun(#(c) c(1:N), arrayfun(#(k) nonzeros(A(k,:)), 1:size(A,1), 'uni', false), 'uni', false)).'
Not terribly elegant or efficient, but so much fun!
N = 3;
for ii=1:size(A,1);
B(ii,:) = A( ii,find(A(ii,:),N) );
end
Actually , you can do it like the code blow:
N=3
for n=1:size(A,1)
[a b]=find(A(n,:)>0,N);
B(n,:)=A(n,transpose(b));
end
Then I think this B matrix will be what you want.

How to group rows with same column values?

Given the matrix with coordinates in 3D space and values for two variables (say a and b) in two matrices I would like to merge rows for same points into a common matrix.
To clearly explain the matter, let's say we have matrices
A=[posX, posY, posZ, a]
and
B=[posX, posY, posZ, b]
and would like to combine them into
AB = [posX, posY, posZ, a, b]
for example
A = [0 0 1 1; 0 1 0 4; 5 0 12 8];
B = [0 0 0 5; 0 1 0 3; 5 11 7 7];
would give
AB = [0 0 0 0 5; 0 0 1 1 0; 0 1 0 4 3; 5 0 12 8 0; 5 11 7 0 7];
In order to do that I first created
ATemp = [A, zeros(length(A,0)]
and
BTemp = [B(:, [1 2 3]), zeros(length(B),1), B(:,4)]
and then tried to use functions accumarray and grpstats but haven't managed to form the AB matrix.
I would be very thankful if anyone suggested the way to get the desired matrix.
AB=union(A(:,1:3),B(:,1:3),'rows');
AB(ismember(AB,A(:,1:3),'rows'),4)=A(:,4);
AB(ismember(AB(:,1:3),B(:,1:3),'rows'),5)=B(:,4)
[edit] This solution is only valid if each (x,y,z)-point occurs only once in each matrix. If there are several, there is a dimension mismatch in the second line (and/or the third).