Convert a for loop into a vector (vectorization) - matlab

For those super experts out there, I was wondering if you see a quick way to convert the following "for" loop into a one-line vector calculation that is more efficient.
%Define:
%A size (n,1)
%B size (n,m)
%C size (n,1)
B = [2 200; 3 300; 4 400];
C = [1;2;1];
for j=1:n
A(j) = B( j, C(j) );
end
So to be clear, is there any alternative way to express A, as a function of B and C, without having to write a loop?

Yes, there is:
A = B(sub2ind([n,m], (1:n).', C));

It depends on functions A, B, and C, but this might work:
j = 1:n;
A = B(j, C(j));

Related

writing a matrix in a loop

I have a series of arrays of equal length, and want to make a matrix for each data point of these, and perform some sort of operation such a multiplying the matrices.
a=ones(1,10);
b=3*ones(1,10);
c=zeros(1,10);
for i=1:10
A(i)=[a(i) a(i);
b(i) b(i)];
B(i)=[c(i) c(i)];
C(i)=B(i)*A(i);
end
Is this possible without using cells?
A = zeros(2,2,length(a));
B = zeros(length(a),:);
C = zeros(size(B));
for i=1:10
A(:,:,i)=[a(i) a(i);
b(i) b(i)];
B(i,:)=[c(i) c(i)];
C(i,:)=B(i,:)*A(:,:,i);
end
Note you can make A and B without loops:
aa = permute(A, [3,2,1]);
bb = permute(B, [3,2,1]);
A = [aa,aa;bb,bb];
B = [c.', c.'];

MATLAB boxplot of high dimensional vectors with different lengths

I have been looking for a way to use boxplot for different length vectors. thanx for stackoverflow helpers, they give this solution:
A = randn(10, 1); B = randn(12, 1); C = randn(4, 1);
g = [repmat(1, [10, 1]) ; repmat(2, [12, 1]); repmat(3, [4, 1])];
figure; boxplot([A; B; C], g);
unfortunately, my data contains over 100 vectors with different lengths, I wonder if it can be done without repeating the repmat for over 100 times.
As long as your vectors have different lengths, store it in a cell array.
There are plenty was of doing it, here are 3 examples
1) "Naive" for loop
g = [];
vars_cell = {A, B, C, ....};
for it = 1 : length(vars_cell)
g = [g; repmat(it,size(vars_cell{it}))];
end
This way of doing it works but is very slow with big quantites of vectors or big vectors! It comes from the fact that you are re-defining g at each iteration, changing its size each time.
2) Not-naive for loop
vars_cell = {A, B, C, ....};
%find the sum of the length of all the vectors
total_l = sum(cellfun(#(x) length(x),vars_cell));
g = zeros(total_l,1);
acc = 1;
for it = 1 : length(vars_cell)
l = size(vars_cell{it});
g(acc:acc+l-1) = repmat(it,l);
acc = acc+l;
end
This method will be much faster than the 1st one because it defines g only once
3) The "one-liner"
vars_cell = {A, B, C, ....};
g = cell2mat(arrayfun(#(it) repmat(it, size(vars_cell{it})),1:length(vars_cell),'UniformOutput',0)');
This is qute equivalent to the 2nd solution, but if you like one line answers this is what you are looking for!

Use row indices of matrix as elements of new matrix? [duplicate]

For those super experts out there, I was wondering if you see a quick way to convert the following "for" loop into a one-line vector calculation that is more efficient.
%Define:
%A size (n,1)
%B size (n,m)
%C size (n,1)
B = [2 200; 3 300; 4 400];
C = [1;2;1];
for j=1:n
A(j) = B( j, C(j) );
end
So to be clear, is there any alternative way to express A, as a function of B and C, without having to write a loop?
Yes, there is:
A = B(sub2ind([n,m], (1:n).', C));
It depends on functions A, B, and C, but this might work:
j = 1:n;
A = B(j, C(j));

Vectorizing in Multiple Dimensions

I have the following for loop in my Matlab code I'd like to get rid of:
for i=1:size(thePolygon,3)
activeValues(:,i) = sum(normalVectors.*thePolygon(:,:,i),2);
checkValues(:,i) = sign(activeValues(:,i)-sum(normalVectors.*thePolygon3(:,:,i),2));
end
I tried replacing i with 1:size(thePolygon,3), but the dimensions don't line up for the element-by-element multiplication, and I'm not sure what else to try. Thanks in advance for any tips.
It's difficult to test without example data but:
activeValues = squeeze(sum(bsxfun(#times, normalVectors, thePolygon), 2))
OK so first some test data:
a = rand(3,3,3);
b = rand(3,3);
now we test yours
for i=1:size(a,3)
c(:,i) = sum(b.*a(:,:,i),2);
end
on my run I got:
c =
0.9773 1.0608 0.3673
0.6670 0.1597 0.7296
0.8372 1.1418 0.9828
and now mine:
squeeze(sum(bsxfun(#times, b, a), 2))
I get
ans =
0.9773 1.0608 0.3673
0.6670 0.1597 0.7296
0.8372 1.1418 0.9828
so I am assuming mine is correct.
Explanation:
bsxfun(#times, b, a) does broadcasting, it expands b along it's singleton dimension (in this case dim 3) to match the size of a and then applies the function (#times is just .*) element by element to the expanded b and a. This is identical (but better practice and faster) to going repmat(b, [1 1 size(a,3)]).*a.
Then we sum this 3D matrix column wize like you have, i.e. sum(x, 2) which returns a 1x3x3 result. But I want a 3x3 result so to get rid of the singelton dimension (i.e. dim 1 as it equals 1) I used squeeze
It should be trivial for you to now vectorize your second line in the same way.
BUT
tic; for k = 1:10000 c = squeeze(sum(bsxfun(#times, b, a), 2)); end; toc
Elapsed time is 0.394828 seconds.
tic; c = zeros(3,3); for k = 1:10000 for i=1:size(a,3)
c(:,i) = sum(b.*a(:,:,i),2);
end; end; toc
Elapsed time is 0.113199 seconds.
THE NON-VECTORIZED CODE IS FASTER IN MATLASB 2012b!!!!!!!

Generalized Matrix Product

I'm fairly new to MATLAB. Normal matrix multiplication of a M x K matrix by an K x N matrix -- C = A * B -- has c_ij = sum(a_ik * b_kj, k = 1:K). What if I want this to be instead c_ij = sum(op(a_ik, b_kj), k = 1:K) for some simple binary operation op? Is there any nice way to vectorize this in MATLAB (or maybe even a built-in function)?
EDIT: This is currently the best I can do.
% A is M x K, B is K x N
% op is min
C = zeros(M, N);
for i = 1:M:
C(i, :) = sum(bsxfun(#min, A(i, :)', B));
end
Listed in this post is a vectorized approach that persists with bsxfun by using permute to create singleton dimensions as needed by bsxfun to let the singleton-expansion do its work and thus essentially replacing the loop in the original post. Please be reminded that bsxfun is a memory hungry implementation, so expect speedup with it only until it is stretched too far. Here's the final solution code -
op = #min; %// Edit this with your own function/ operation
C = sum(bsxfun(op, permute(A,[1 3 2]),permute(B,[3 2 1])),3)
NB - The above solution was inspired by Removing four nested loops in Matlab.
if the operator can operate element-by-element (like .*):
if(size(A,2)~=size(B,1))
error(blah, blah, blah...);
end
C = zeros(size(A,1),size(B,2));
for i = 1:size(A,1)
for j = 1:size(B,2)
C(i,j) = sum(binaryOp(A(i,:)',B(:,j)));
end
end
You can always write the loops yourself:
A = rand(2,3);
B = rand(3,4);
op = #times; %# use your own function here
C = zeros(size(A,1),size(B,2));
for i=1:size(A,1)
for j=1:size(B,2)
for k=1:size(A,2)
C(i,j) = C(i,j) + op(A(i,k),B(k,j));
end
end
end
isequal(C,A*B)
Depending on your specific needs, you may be able to use bsxfun in 3D to trick the binary operator. See this answer for more infos: https://stackoverflow.com/a/23808285/1121352
Another alternative would be to use cellfun with a custom function:
http://matlabgeeks.com/tips-tutorials/computation-using-cellfun/