extract 3D matrix's columns based on "surface" values - vectorization - matlab

I have an NxMxK matrix A
A = [1 2 1; 1 1 2];
A = cat(3, A, [3 3 3; 3 3 3])
A(:,:,1) =
1 2 1
1 1 2
A(:,:,2) =
3 3 3
3 3 3
and I want to create a YxK 2D matrix B where K is the number of elements of A(:,:,1)==2:
k=0;
for ii=1:size(A,1)
for jj=1:size(A,2)
if A(ii,jj)==2
k=k+1;
B(k,:) = A(ii,jj,:);
end
end
end
Is there a way of vectorizing this code?
My attempt was to find the indices of A(:,:,1)==2 and then try to select the whole column but I do not know how to do it:
inds = find(A(:,:,1)==2)
B = A(inds,:) %this line does not make sense
EDIT
Preallocating B helps:
inds=find(A(:,:,1)==2);
B=NaN(numel(inds),size(A,3));
k=0;
for ii=1:size(A,1)
for jj=1:size(A,2)
if A(ii,jj)==2
k=k+1;
B(k,:) = squeeze(A(ii,jj,:));
end
end
end
But still not vectorized.

You can reshape matrix A to a (N*M)xK 2D matrix.
A = [1 2 1; 1 1 2];
A = cat(3, A, [3 3 3; 3 3 3]);
A_ = reshape(A,numel(A(:,:,1)),size(A,3));
B = A_(A_(:,1)==2,:);

Your first attempt at vectorization is almost right. Just don't use find, but use the logical matrix for indexing.
inds = A(:,:,1)==2;
The inds matrix is 2D, not 3D, so we use repmat to repeat its values along the 3rd dimension:
K = size(A,3);
inds = repmat(inds,1,1,K); % or simply cat(3,inds,inds) if K==2
B = A(inds);
The result is a column vector of size Y*K, not a matrix of size YxK, we can use reshape to fix that:
B = reshape(B,[],K);
I guess this answer is similar to Anthony's, except the indexing and the reshaping are reversed. I didn't really notice the similarity until after I wrote it down. I guess also Anthony's is a little shorter. :/

Related

Multiply each column in a matrix by corresponding row in another and sum results in Matlab

Lets say I have matrices A = [1 2; 3 4], B = [4 3; 2 1]. I want to multiply each column from matrix A ([1; 3], [2; 4]) by the corresponding row in matrix B ([4 3], [2 1]) and sum resulting matrices. I have came up with the following code:
C = zeros(size(A));
for i = 1 : size(A, 1)
C = C + A(:, i) * B(i, :);
end
Could it be rewritten using some math trick or matlab function to get rid of the for loop?
I see there is unclarity in my question regarding the result I want. The result should exactly mimic provided Matlab code, therefore I seek one matrix which is given by the matrix summation of the intermediate matrices that are created by multiplying each column vector with corresponding row vector from both matrices. For this specific example, it would be given by
C = A(:, 1) * B(1, :) + A(:, 2) * B(2, :);
I am just looking for some generic, for-loop less version for any matrices of compatible dimensions.
I just tried out my suggestion in the comments, and it seems to work with this octave tester:
Short form (only works in Octave):
A = [1 2; 3 4], B = [4 3; 2 1]
X = sum((A * B)(:))
Long form (Matlab):
A = [1 2; 3 4]
B = [4 3; 2 1]
C = A * B % Stop here if you want the exact result from your Matlab code
x = sum(C(:)) % To get the sum of the resulting matrix
Sources:
https://www.tutorialspoint.com/matlab/matlab_matrix_multiplication.htm
https://www.mathworks.com/matlabcentral/newsreader/view_thread/51252
Update, based on your update:
Output of A * B:
8 5
20 13
Output of your code:
8 5
20 13
It appears that
C = zeros(size(A));
for i = 1 : size(A, 1)
C = C + A(:, i) * B(i, :);
end
is equivalent to the matrix multiplication
C = A*B
for square matrices A and B.
You can also do this in MATLAB, to get the sum.
C=ones(1,2)*A*B*ones(2,1)
The general form would be
C=ones(1,size(A,1))*(A*B)*ones(size(B,2),1);
EDIT
I see you updated your question for clarity. The matrix product can be calculated directly
C = A*B;
as pointed out by jodag.
This works provided you follow rules of linear algebra where inner dimensions of your matrices are the same (i.e. when number of columns in A match the number of rows in B; size(A,2)==size(B,1)).

how to get an incremental power matrix in matlab

I wanted to compute the following matrix in Matlab:
g=[I
A
.
.
.
A^N]
I used the following program in Matlab:
A=[2 3;4 1];
s=A;
for n=1:1:50
s(n)=A.^n;
end
g=[eye(1,1),s];
I am getting the following error:
In an assignment A(I) = B, the number of elements in B and I must be the same.
Error in s_x_calcu_v1 (line 5)
s(n)=A.^n;
The problem is that you are trying to assign a matrix to a single element. In matlab calling s(n) mean you get the nth element of s, regardless of the dimensions of s. You can use a three dimensional matrix
N = 50;
A=[2 3;4 1];
[nx,ny] = size(A);
s(nx,ny,N) = 0; %makes s a nx x ny x N matrix
for n=1:1:N
s(:,:,n)=A.^n; %Colon to select all elements of that dimension
end
g=cat(3, eye(size(A)) ,s); %Add the I matrix of same size as A
Or a vectorized version
s = bsxfun(#power, A(:), 1:N);
s = reshape(s,2,2,N);
g = cat(3, eye(size(A)) ,s);
And a third solution using cumprod
s = repmat(A(:), [1 N]);
s = cumprod(s,2);
s = reshape(s,2,2,N);
g = cat(3, eye(size(A)) ,s);
Your s array is a 2-by-2 array, you cannot index it to store the result of your compuation at each step of your loop.
For this, the simpler is probably to define s as a cell:
% --- Definitions
A = [2 3;4 1];
N = 50;
% --- Preparation
s = cell(N,1);
% --- Computation
for n=1:N
s{n} = A.^n;
end
Best,
When you loop from 1 to N computing each time A.^n you are doing LOTS of redundant computations! Note that
A.^n = (A.^(n-1)).*A; %//element-wise power
A^n = (A^n) * A; %// matrix power
Therefore,
A = [2 3;4 1];
N = 50;
s = cell(N+1,1);
s{1} = eye(size(A,1));
for ii=1:N
s{ii+1} = s{ii}.*A; %// no powers, just product!
end
g = vertcat( s{:} );
BTW, the same holds if you want to compute matrix power (instead of element-wise powers), all you need is changing to s{ii+1} = s{ii}*A;

Obtain 3-D matrix from multiplication of one 1-D matrix and one 2-D matrix [duplicate]

As always trying to learn more from you, I was hoping I could receive some help with the following code.
I need to accomplish the following:
1) I have a vector:
x = [1 2 3 4 5 6 7 8 9 10 11 12]
2) and a matrix:
A =[11 14 1
5 8 18
10 8 19
13 20 16]
I need to be able to multiply each value from x with every value of A, this means:
new_matrix = [1* A
2* A
3* A
...
12* A]
This will give me this new_matrix of size (12*m x n) assuming A (mxn). And in this case (12*4x3)
How can I do this using bsxfun from matlab? and, would this method be faster than a for-loop?
Regarding my for-loop, I need some help here as well... I am not able to storage each "new_matrix" as the loop runs :(
for i=x
new_matrix = A.*x(i)
end
Thanks in advance!!
EDIT: After the solutions where given
First solution
clear all
clc
x=1:0.1:50;
A = rand(1000,1000);
tic
val = bsxfun(#times,A,permute(x,[3 1 2]));
out = reshape(permute(val,[1 3 2]),size(val,1)*size(val,3),[]);
toc
Output:
Elapsed time is 7.597939 seconds.
Second solution
clear all
clc
x=1:0.1:50;
A = rand(1000,1000);
tic
Ps = kron(x.',A);
toc
Output:
Elapsed time is 48.445417 seconds.
Send x to the third dimension, so that singleton expansion would come into effect when bsxfun is used for multiplication with A, extending the product result to the third dimension. Then, perform the bsxfun multiplication -
val = bsxfun(#times,A,permute(x,[3 1 2]))
Now, val is a 3D matrix and the desired output is expected to be a 2D matrix concatenated along the columns through the third dimension. This is achieved below -
out = reshape(permute(val,[1 3 2]),size(val,1)*size(val,3),[])
Hope that made sense! Spread the bsxfun word around! woo!! :)
The kron function does exactly that:
kron(x.',A)
Here is my benchmark of the methods mentioned so far, along with a few additions of my own:
function [t,v] = testMatMult()
% data
%{
x = [1 2 3 4 5 6 7 8 9 10 11 12];
A = [11 14 1; 5 8 18; 10 8 19; 13 20 16];
%}
x = 1:50;
A = randi(100, [1000,1000]);
% functions to test
fcns = {
#() func1_repmat(A,x)
#() func2_bsxfun_3rd_dim(A,x)
#() func2_forloop_3rd_dim(A,x)
#() func3_kron(A,x)
#() func4_forloop_matrix(A,x)
#() func5_forloop_cell(A,x)
#() func6_arrayfun(A,x)
};
% timeit
t = cellfun(#timeit, fcns, 'UniformOutput',true);
% check results
v = cellfun(#feval, fcns, 'UniformOutput',false);
isequal(v{:})
%for i=2:numel(v), assert(norm(v{1}-v{2}) < 1e-9), end
end
% Amro
function B = func1_repmat(A,x)
B = repmat(x, size(A,1), 1);
B = bsxfun(#times, B(:), repmat(A,numel(x),1));
end
% Divakar
function B = func2_bsxfun_3rd_dim(A,x)
B = bsxfun(#times, A, permute(x, [3 1 2]));
B = reshape(permute(B, [1 3 2]), [], size(A,2));
end
% Vissenbot
function B = func2_forloop_3rd_dim(A,x)
B = zeros([size(A) numel(x)], 'like',A);
for i=1:numel(x)
B(:,:,i) = x(i) .* A;
end
B = reshape(permute(B, [1 3 2]), [], size(A,2));
end
% Luis Mendo
function B = func3_kron(A,x)
B = kron(x(:), A);
end
% SergioHaram & TheMinion
function B = func4_forloop_matrix(A,x)
[m,n] = size(A);
p = numel(x);
B = zeros(m*p,n, 'like',A);
for i=1:numel(x)
B((i-1)*m+1:i*m,:) = x(i) .* A;
end
end
% Amro
function B = func5_forloop_cell(A,x)
B = cell(numel(x),1);
for i=1:numel(x)
B{i} = x(i) .* A;
end
B = cell2mat(B);
%B = vertcat(B{:});
end
% Amro
function B = func6_arrayfun(A,x)
B = cell2mat(arrayfun(#(xx) xx.*A, x(:), 'UniformOutput',false));
end
The results on my machine:
>> t
t =
0.1650 %# repmat (Amro)
0.2915 %# bsxfun in the 3rd dimension (Divakar)
0.4200 %# for-loop in the 3rd dim (Vissenbot)
0.1284 %# kron (Luis Mendo)
0.2997 %# for-loop with indexing (SergioHaram & TheMinion)
0.5160 %# for-loop with cell array (Amro)
0.4854 %# arrayfun (Amro)
(Those timings can slightly change between different runs, but this should give us an idea how the methods compare)
Note that some of these methods are going to cause out-of-memory errors for larger inputs (for example my solution based on repmat can easily run out of memory). Others will get significantly slower for larger sizes but won't error due to exhausted memory (the kron solution for instance).
I think that the bsxfun method func2_bsxfun_3rd_dim or the straightforward for-loop func4_forloop_matrix (thanks to MATLAB JIT) are the best solutions in this case.
Of course you can change the above benchmark parameters (size of x and A) and draw your own conclusions :)
Just to add an alternative, you maybe can use cellfun to achieve what you want. Here's an example (slightly modified from yours):
x = randi(2, 5, 3)-1;
a = randi(3,3);
%// bsxfun 3D (As implemented in the accepted solution)
val = bsxfun(#and, a, permute(x', [3 1 2])); %//'
out = reshape(permute(val,[1 3 2]),size(val,1)*size(val,3),[]);
%// cellfun (My solution)
val2 = cellfun(#(z) bsxfun(#and, a, z), num2cell(x, 2), 'UniformOutput', false);
out2 = cell2mat(val2); % or use cat(3, val2{:}) to get a 3D matrix equivalent to val and then permute/reshape like for out
%// compare
disp(nnz(out ~= out2));
Both give the same exact result.
For more infos and tricks using cellfun, see: http://matlabgeeks.com/tips-tutorials/computation-using-cellfun/
And also this: https://stackoverflow.com/a/1746422/1121352
If your vector x is of lenght = 12 and your matrix of size 3x4, I don't think that using one or the other would change much in term of time. If you are working with higher size matrix and vector, now that might become an issue.
So first of all, we want to multiply a vector with a matrix. In the for-loop method, that would give something like that :
s = size(A);
new_matrix(s(1),s(2),numel(x)) = zeros; %This is for pre-allocating. If you have a big vector or matrix, this will help a lot time efficiently.
for i = 1:numel(x)
new_matrix(:,:,i)= A.*x(i)
end
This will give you 3D matrix, with each 3rd dimension being a result of your multiplication. If this is not what you are looking for, I'll be adding another solution which might be more time efficient with bigger matrixes and vectors.

matlab remove for loop in matrix computation

I'm working on a problem on Matlab according to Matrix. I think my code could be improved by remove the for loop. But I really don't know how to fix this one. Can anyone help me, please?
the code is:
K = 3;
X = [1 2; 3 4; 5 6; 7 8];
idx = [1;2;3;1];
for i = 1:K
ids = (idx == i);
centroids(i,:) = sum(bsxfun(#times, X, ids))./ sum(ids);
end
in this code, data points X is 4x2. There are K=3 centroids, so the centroids is a matrix of 3x2. This code is part of a K-mean function, which is using data points and their closest centroids to find new position of centroids.
I want to make the code as something without the FOR loop, maybe beginning like this:
ids = bsxfun(#eq, idx, 1:K);
centroids = ..............
You can avoid the bsxfun by using logical indexing, this seems to be a worthwhile performance increase, at least for small matrices X. It is best for small K, and for a small number of rows of X.
K = 3;
X = [1 2; 3 4; 5 6; 7 8];
idx = [1;2;3;1];
centroids=zeros(K,2);
for i = 1:K
ids = (idx == i);
centroids(i,:) = sum(X(ids,:),1)./sum(ids);
end
If X has a large number of rows, this method is fastest:
K = 3;
X = [1 2; 3 4; 5 6; 7 8];
idx = [1;2;3;1];
centroids=zeros(K,2);
t=bsxfun(#eq,idx,1:K);
centroids=bsxfun(#rdivide,t.'*X,sum(t).');
And if K is very large, Luis' accumarray method is fastest.
You could apply accumarray. Note that accumarray only works when X is a column. So, if X has two columns, you can call accumarray twice:
centroids(:,1) = accumarray(idx, X(:,1), [], #mean)
centroids(:,2) = accumarray(idx, X(:,2), [], #mean)
Alternatively, if X contains two columns of real numbers, you can use complex to "pack" the two columns into one complex column, and then unpack the results:
centroids = accumarray(idx, complex(X(:,1),X(:,2)), [], #mean);
centroids = [ real(centroids) imag(centroids)];
If X has an arbitrary number of columns, possibly with complex numbers, you can loop over columns:
centroids = NaN(K, size(X,2)); %// preallocate
for col = 1:size(X,2);
centroids(:,col) = accumarray(idx, X(:,col), [], #mean);
end

outer product of matrix rows (e.g. A(i,:)^T * A(i,:) for all rows i) + mean of same element in all submatrices created

I'm trying to calculate the outer product of all rows in a matrix. So far I'm doing the following:
A = rand(10,8);
[N J] = size(A);
for i = 1:N,
B(((i-1)*J+1):J+((i-1)*J),:) = A(i,:)'*A(i,:)
end
I then take the mean of the same elements of the same row in each submatrix created above.
for j=1:J,
C(1:J,j) = accumarray(repmat((1:J)',N,1),B(:,j)).*(1/N);
end
Now this works, but my input matrix will eventually be a number of magnitudes larger, so a vectorized version would be nice. I assume there is some way to do this with permute and bsxfun but the solutions for outer product I have seen so far don't seem to apply here.
Sure. You can compute C directly as
C = mean(bsxfun(#times, permute(A, [2 3 1]), permute(A, [3 2 1])), 3);
Or, if you really need the B variable, you can do it this way:
B_3D = bsxfun(#times, A.', permute(A,[3 1 2])); %'// 3D array
B = reshape(B_3D, numel(A), []);
C = mean(permute(B_3D, [1 3 2]), 3);