I have a problem while using Arrayfun in Matlab. I have a vector of angles and I want to avoid a for loop and I apply:
rmatrix1 = arrayfun(...
#(x) anglesLoop(iblade, iradius, jradius, ifrequency, x, rot), ...
angles, 'uniformoutput', false);
Where iblade = 1, iradius = 1, jradius = 1, ifrequency = 1 and rot = 0.5.
The function looks like:
%% Angles Loop
function rmatrix = anglesLoop(iblade, iradius, jradius, ifrequency, angles, rot)
global frequency blade
fshifted =(frequency(ifrequency)-angles*rot);
S11 = kaimal(fshifted);
S22 = S11;
r = distance(iradius,jradius,angles);
aux = (3/(2*pi)).*coherence(r).*exp(-1i*angles*ifrequency*blade(iblade)/3);
rmatrix = (sqrt(S11).*sqrt(S22).*aux.*exp(1i*2*pi.*angles.*1/3));
end
with the subfunctions
%% distance for coherence function
function r=distance(x1,x2,theta)
r = sqrt(x1^2+x2^2- 2*x1*x2*cos(theta));
end
And
%% Coherence
function gamma=coherence(r)
global frequency v10 L1
if r==0
gamma=1;
else
gamma=exp(-12.*sqrt((frequency.*r./v10).^2+(0.12.*r./L1).^2));
end
The problem is that when I apply the anglesLoop function in arrayfun I obtain a cell of 64 different arrays whereas I should obtain a vector of 64 which is the angles length.
rmatrix1 should be a vector of 64 elements. Can someone give me some recommendation?
I think the problem is you're asking for 'UniformOutput', false which causes the return to be a cell array. You then simply need to concatenate the contents of the cell array, which you can do with cell2mat. Here's a simpler example. I've got a row vector, and I'm applying a function that turns each element into a 2x2 matrix. I want to end up with all of these small matrices concatenated together.
rowVector = 1:5;
myFcn = #(x) [x, -x; -x, x];
separateMatricesCell = arrayfun(myFcn, rowVector, 'UniformOutput', false);
concatenatedMatrices = cell2mat(separateMatricesCell)
Related
I have been using the following custom function to perform the multiplication of a vector by a matrix, in which each element of the vector multiplies a 3x3 block within a (3xN)x(3) matrix:
function [B] = BlockScalar(v,A)
N=size(v,2);
B=zeros(3*N,3);
for i=1:N
B(3*i-2:3*i,:) = v(i).*A(3*i-2:3*i,:);
end
end
Similarly, when I want to multiply a collection of 3x3 matrices by a collection of 3x3 vectors, I use the following
function [B] = BlockMatrix(A,u)
N=size(u,2);
B=zeros(N,3);
for i=1:N
B(i,:) = A(3*i-2:3*i,:)*u(:,i);
end
end
Since I call them very often, these unfortunately, slow down the running of my code significantly. I was wondering if there was a more efficient (perhaps vectorised) version of the above operations.
In both instance, you are able to do away with the for-loops (although without testing, I cannot confirm if this will necessarily speed up your computation).
For the first function, you can do it as follows:
function [B] = BlockScalar(v,A)
% We create a vector N = [1,1,1,2,2,2,3,3,3,...,N,N,N]
N=ceil((1:size(A,1))/3);
% Use N to index v, and let matlab do the expansion
B = v(N).*A;
end
For the second function, we can make a block-diagonal matrix.
function [B] = BlockMatrix(A,u)
N=size(u,2)*3;
% We use a little meshgrid+sparse magic to convert A to a block matrix
[X,Y] = meshgrid(1:N,1:3);
% Use sparse matrices to speed up multiplication and save space
B = reshape(sparse(Y+(ceil((1:N)/3)-1)*3,X,A) * (u(:)),3,size(u,2))';
end
Note that if you are able to access the individual 3x3 matrices, you can potentially make this faster/simpler by using the native blkdiag:
function [B] = BlockMatrix(a,b,c,d,...,u)
% Where A = [a;b;c;d;...];
% We make one of the input matrices sparse to make the whole block matrix sparse
% This saves memory and potentially speeds up multiplication by a lot
% For small enough values of N, however, using sparse may slow things down.
reshape(blkdiag(sparse(a),b,c,d,...) * (u(:)),3,size(u,2))';
end
Here are vectorized solutions:
function [B] = BlockScalar(v,A)
N = size(v,2);
B = reshape(reshape(A,3,N,3) .* v, 3*N, 3);
end
function [B] = BlockMatrix(A,u)
N = size(u,2);
A_r = reshape(A,3,N,3);
B = (A_r(:,:,1) .* u(1,:) + A_r(:,:,2) .* u(2,:) + A_r(:,:,3) .* u(3,:)).';
end
function [B] = BlockMatrix(A,u)
N = size(u,2);
B = sum(reshape(A,3,N,3) .* permute(u, [3 2 1]) ,3).';
end
I have a constant 2D double matrix mat1. I also have a 2D cell array mat2 where every cell contains a 2D or 3D double matrix. These double matrices have the same number of rows and columns as mat1. I need to dot multiply (.*) mat1 with every slice of each double matrix within mat2. The result needs to be another cell array results with the same size as mat2, whereby the contatining double matrices must equal the double matrices of mat2 in terms of size.
Here's my code to generate mat1 and mat2 for illustrating purposes. I am struggling at the point where the multiplication should take place.
rowCells = 5;
colCells = 3;
rowTimeSeries = 300;
colTimeSeries = 5;
slices = [1;10];
% Create 2D double matrix
mat1 = rand(rowTimeSeries, colTimeSeries);
% Create 2D cell matrix comprisiong 2D and/or 3D double matrices
mat2 = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
slice = randsample(slices, 1, true);
mat2{r,c} = rand(rowTimeSeries, colTimeSeries, slice);
end
end
% Multiply (.*) mat1 with mat2 (every slice)
results = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
results{r,c} = ... % I am struggling here!!!
end
end
You could use bsxfun to remove the need for your custom function multiply2D3D, it works in a similar way! Updated code:
results = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
results{r,c} = bsxfun(#times, mat1, mat2{r,c});
end
end
This will work for 2D and 3D matrices where the number of rows and cols is the same in each of your "slices", so it should work in your case.
You also don't need to loop over the rows and the columns of your cell array separately. This loop has the same number of iterations, but it is one loop not two, so the code is a little more streamlined:
results = cell(size(mat2));
for n = 1:numel(mat2) % Loop over every element of mat2. numel(mat2) = rowCells*colCells
results{n} = bsxfun(#times, mat1, mat2{n});
end
I had almost the exact same answer as Wolfie but he beat me to it.
Anyway, here is a one liner that I think is slightly nicer:
nR = rowCells; % Number of Rows
nC = colCells; % Number of Cols
results = arrayfun(#(I) bsxfun(#times, mat1, mat2{I}), reshape(1:nR*nC,[],nC), 'un',0);
This uses arrayfun to perform the loop indexing and bsxfun for the multiplications.
A few advantages
1) Specifying 'UniformOutput' ('un') in arrayfun returns a cell array so the results variable is also a cell array and doesn't need to be initialised (in contrast to using loops).
2) The dimensions of the indexes determine the dimensions of results at the output, so they can match what you like.
3) The single line can be used directly as an input argument to a function.
Disadvantage
1) Can run slower than using for loops as Wolfie pointed out in the comments.
One solution I came up with is to outsource the multiplication of a 2D with a 3D matrix into a function. However, I am curious to know whether this is the most efficient way to solve this problem?
rowCells = 5;
colCells = 3;
rowTimeSeries = 300;
colTimeSeries = 5;
slices = [1;10];
% Create 2D double matrix
mat1 = rand(rowTimeSeries, colTimeSeries);
% Create 2D cell matrix comprisiong 2D and/or 3D double matrices
mat2 = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
slice = randsample(slices, 1, true);
mat2{r,c} = rand(rowTimeSeries, colTimeSeries, slice);
end
end
% Multiply (.*) mat1 with mat2 (every slice)
results = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
results{r,c} = multiply2D3D(mat1, mat2{r,c});
end
end
function vout = multiply2D3D(mat2D, mat3D)
%MULTIPLY2D3D multiplies a 2D double matrix with every slice of a 3D
% double matrix.
%
% INPUTs:
% mat2D:
% 2D double matrix
%
% mat3D:
% 3D double matrix where the third dimension is equal or greater than 1.
%
% OUTPUT:
% vout:
% 3D double matrix with the same size as mat3D. Every slice in vout
% is the result of a multiplication of mat2D with every individual slice
% of mat3D.
[rows, cols, slices] = size(mat3D);
vout = zeros(rows, cols, slices);
for s = 1 : slices
vout(:,:,s) = mat2D .* mat3D(:,:,s);
end
end
I have a vector x = [10,20,30,40] in Matlab, now I would like to apply y = rand(m,1) to each element in x, i.e generating:
y1 = rand(x(1),1);
y2 = rand(x(2),1);
y3 = rand(x(3),1);
y4 = rand(x(4),1);
The straight-forward way is to apply a for loop, but as we know that, for loop is not efficient in Matlab. So is that any other way to do that?
Follow-ups:
Applying a function to each element in vector in Matlab seems a very common problem, how can we handle such cases and avoid using for loop?
create a random vector (ys) it's size is sum of x elements then define a function to extract each y with index:
x=[10,20,30,40];
s = sum(x);
c = cumsum(x);
ys = rand(s, 1);
y = #(i) ys(c(i)-x(i) +1:c(i)) ;
%%%%example
y(3)
y(1)
First, to point out the obvious. You imply that y would be a numerical array, but this is impossible, because each element has a different size. (e.g. y1 is 10x1, but y2 is 20x1)
Therefore, the only output that makes sense here is if you expect y to be a cell array.
You can very straightforwardly use arrayfun (or cellfun etc) to do this:
>> x = [10, 20, 30, 40]
>> f = #(a) {rand(a, 1)}; % output is a "cell" element
>> y = arrayfun(f, x); % apply 'f' to each element of x: returns a 1x4 cell array
I have these 10 vectors in MATLAB, mean(alltmws{l}'), where l is from 1 to 10. The size of each of these vectors is 1X10001. Now I want to store all these values in one vector, one after the other, so that I can calculate and plot the overall mean. How can I do this concatenation? Any help will be greatly appreciated.
If you have, for example,
a{1} = rand(10,1);
a{2} = rand(10,1);
a{3} = rand(10,1);
You can do
A = [a{:}];
A = A(:)
EDIT: The question is ambiguous, but if it is the means that one wants to concatenate and plot, you can do:
% Create example data
data = {};
for k = 1:10
data{k} = rand(100,1);
end
% Compute and plot array of means
mu = []
for k = 1:length(data)
mu(k) = mean(data{k});
end
plot(mu)
If you have a 1x10 cell array, then you can directly do:
concatnatedArray=cell2mat(yourCellArray);
If you have a 10x1 cell array, first transpose it and then apply above technique. This will only work if all the vectors in each cell are of the same length, which the case for you.
Suppose I have D, an X-by-Y-by-Z data matrix. I also have M, an X-by-Y "masking" matrix. My goal is to set the elements (Xi,Yi,:) in D to NaN when (Xi,Yi) in M is false.
Is there any way to avoid doing this in a loop? I tried using ind2sub, but that fails:
M = logical(round(rand(3,3))); % mask
D = randn(3,3,2); % data
% try getting x,y pairs of elements to be masked
[x,y] = ind2sub(size(M),find(M == 0));
D_masked = D;
D_masked(x,y,:) = NaN; % does not work!
% do it the old-fashioned way
D_masked = D;
for iX = 1:size(M,1)
for iY = 1:size(M,2)
if ~M(iX,iY), D_masked(iX,iY,:) = NaN; end
end
end
I suspect I'm missing something obvious here. (:
You can do this by replicating your logical mask M across the third dimension using REPMAT so that it is the same size as D. Then, index away:
D_masked = D;
D_masked(repmat(~M,[1 1 size(D,3)])) = NaN;
If replicating the mask matrix is undesirable, there is another alternative. You can first find a set of linear indices for where M equals 0, then replicate that set size(D,3) times, then shift each set of indices by a multiple of numel(M) so it indexes a different part of D in the third dimension. I'll illustrate this here using BSXFUN:
D_masked = D;
index = bsxfun(#plus,find(~M),(0:(size(D,3)-1)).*numel(M));
D_masked(index) = NaN;
Reshape is basically for free, you can use it here for an efficient solution. reducing the whole to a 2d problem.
sz=size(D);
D=reshape(D,[],sz(3)); %reshape to 2d
D(isnan(M(:)),:)=nan; %perform the operation on the 2d matrix
D=reshape(D,sz); %reshape back to 3d
My Matlab is a bit rusty but I think logical indexing should work:
D_masked = D;
D_masked[ M ] = NaN;
(which probably can be combined into one statement with a conditional expression on the rhs...)