Optimize Reorganization of Matrix without Loops - matlab

TL;DR: I am trying to optimize the following short code in Matlab. Because it involves loops over large matrices, it is too slow.
for i = 1:sz,
for j = 1:sz,
if X(j) == Q(i) && Y(j) == R(i),
S(i) = Z(j);
break
end
end
end
Specifics: Basically, I started with three vectors of x, y and z data that I wanted to plot as a surface. I generated a mesh of the x and y data and then made a matrix for the corresponding z values using
[X, Y] = meshgrid(x, y);
Z = griddata(x, y, z, X, Y);
Because the data is collected in random order, when generating the surface plot the connections are all wrong and the plot looks all triangulated like the following example.
So, to make sure Matlab was connecting the right dots, I then reorganized the X and Y matrices using
[R, R_indx] = sort(Y);
[Q, Q_indx] = sort(X, 2);
From here I thought it would be a simple problem of reorganizing the matrix Z based on the indices of the sorting for matrix X and Y. But I run into trouble because no matter how I use the indices, I cannot produce the correct matrix. For example, I tried
S = Z(R_indx); % to match the rearrangement of Y
S = S(Q_indx); % to match the rearrangement of X
and I got this barcode...
Running the first block of code gives me the "desired" result pictured here. However, this takes far too long as it is a double loop over a very large matrix.
Question: How can I optimize this rearrangement of the matrix Z without for loops?

Please have a look at the following solutions, and test both with your matrices. Do they perform faster? The array indexing solution does, what you asked for, i.e. the re-arrangement of the matrices. The vector indexing might be even better, since it sorts your original vectors instead of the matrices, and generates the output directly from there.
% Parameters.
dim = 4;
% Test input.
x = [2, -2, 5, 4];
y = [1, -4, 6, -2];
z = rand(dim);
[X, Y] = meshgrid(x, y);
Z = griddata(x, y, z, X, Y);
[R, R_indx] = sort(Y);
[Q, Q_indx] = sort(X, 2);
% Initialize output.
S = zeros(dim);
% Provided solution using loop.
for i = 1:numel(z)
for j = 1:numel(z)
if (X(j) == Q(i) && Y(j) == R(i))
S(i) = Z(j);
break
end
end
end
% Output.
S
% Solution using array indexing; output.
S_array = reshape(((X(:) == Q(:).') & (Y(:) == R(:).')).' * Z(:), dim, dim)
% Solution using vector indexing; output.
[r, r_indx] = sort(y);
[q, q_indx] = sort(x);
[X, Y] = meshgrid(q, r);
Z = griddata(q, r, z, X, Y);
idx = (ones(dim, 1) * ((q_indx - 1) * dim) + r_indx.' * ones(1, dim));
S_vector = Z(idx)
Example output:
S =
0.371424 0.744220 0.777214 0.778058
0.580353 0.686495 0.356647 0.577780
0.436699 0.217288 0.883900 0.800133
0.594355 0.405309 0.544806 0.085540
S_array =
0.371424 0.744220 0.777214 0.778058
0.580353 0.686495 0.356647 0.577780
0.436699 0.217288 0.883900 0.800133
0.594355 0.405309 0.544806 0.085540
S_vector =
0.371424 0.744220 0.777214 0.778058
0.580353 0.686495 0.356647 0.577780
0.436699 0.217288 0.883900 0.800133
0.594355 0.405309 0.544806 0.085540

Related

`streamline` not plotting this vector field

I am trying to test streamline with a very simple 3D vector field. I fill in a mesh using 3 for loops (not the best, but this is reminescent of a different expression for "v" which I couldn't easily vectorise). Then I define the vector field v as r. Simple radial field. My full code is below. quiver3 is fine, but unfortunately streamline gives me the following error:
Error using griddedInterpolant
Interpolation requires at least two
sample points in each dimension.
N = 5;
L = 2;
dl = 2*L/N;
for i = 1:N+1
for j = 1:N+1
for k = 1:N+1
x = -L + (i-1)*dl;
y = -L + (j-1)*dl;
z = -L + (k-1)*dl;
X(i,j,k) = x;
Y(i,j,k) = y;
Z(i,j,k) = z;
r = [x,y,z];
v = r-r0;
Vx(i,j,k) = v(1);
Vy(i,j,k) = v(2);
Vz(i,j,k) = v(3);
end
end
end
quiver3(X,Y,Z,Vx,Vy,Vz);
[sx,sy,sz] = meshgrid(0:0.1:1,0:1:5,0:1:5);
streamline(X,Y,Z,Vx,Vy,Vz,sx,sy,sz);
hold on;
streamslice(X,Y,Z,Vx,Vy,Vz,[],[],5);
pbaspect([1,1,1])
It returns back to gridded X, Y variables, if you use transposed version of X and Y, you will not get this interpolation error in the streamline function. To transpose a N-D array in MATLAB, use permute function, like:
X = permute(X, [2,1,3]); % to rearrange X and Y dimension
or just define correct form of X and Y at the first place [in the loop]

How to evaluate function of two variables with different x and y vectors

I've been trying to evaluate a function in matlab. I want my x vector to go from 0 to 1000 and my y vector to go from 0 to 125. They should both have a length of 101.
The equation to be evaluated is z(x,y) = ay + bx, with a=10 and b=20.
a = 10;
b = 20;
n = 101;
dx = 10; % Interval length
dy = 1.25;
x = zeros(1,n);
y = zeros(1,n);
z = zeros(n,n);
for i = 1:n;
x(i) = dx*(i-1);
y(i) = dy*(i-1);
for j = 1:n;
z(i,j) = a*dy*(j-1) + b*dx*(j-1);
end
end
I get an answer, but I don't know if I did it correctly with the indices in the nested for loop?
See MATLAB's linspace function.
a=10;
b=20;
n=101;
x=linspace(0,1000,n);
y=linspace(0,125,n);
z=a*y+b*x;
This is easier and takes care of the interval spacing for you. From the linspace documentation,
y = linspace(x1,x2,n) generates n points. The spacing between the points is (x2-x1)/(n-1).
Edit:
As others have pointed out, my solution above makes a vector, not a matrix which the OP seems to want. As #obchardon pointed out, you can use meshgrid to make that 2D grid of x and y points to generate a matrix of z. Updated approached would be:
a=10;
b=20;
n=101;
x=linspace(0,1000,n);
y=linspace(0,125,n);
[X,Y] = meshgrid(x,y);
z=a*Y+b*X;
(you may swap the order of x and y depending on if you want each variable along the row or column of z.)

faster way for "for loop" in matlab

I want to fill the unit circle with dots. But the code I have written is very slow. Is there an alternative that Matlab can run faster?
y = linspace(-1, 1, 200);
x = linspace(-1, 1, 200);
for i = 1: length(x)
for j = 1: length(y)
if (x(i)^2 + y(j)^2 < 1)
plot(x(i),y(j),'.');
end
end
end
Rather than looping through every permutation of x and y, you can use meshgrid to create two matrices (xx and yy) where each corresponding value in the two is a unique combination of x and y values. You can then use these matrices to evaluate your conditional (xx.^2 + yy.^2 < 1) at once. This will result in a logical array the size of xx that we can use to plot just the points that were inside of the unit circle.
[xx,yy] = meshgrid(x, y);
inside = xx.^2 + yy.^2 < 1;
% Now plot just these points
plot(xx(inside), yy(inside), '.');

Apply a function to each element of a vector, avoiding using for loop

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

MATLAB - Evaluate a function at each point in meshgrid?

I wish to plot the heat map of a bivariate (independent) Gaussian. To plot it over a 2D square, I did
joint_pdf = #(m, s) normpdf(m, 1, 1)*normpdf(s, 1, 1);
[x, y] = meshgrid(0:0.1:10, 0:0.1:10);
prob_map = zeros(numel(x), numel(y));
for idx1 = 1:size(prob_map, 1)
for idx2 = 1:size(prob_map, 2)
prob_map(idx1, idx2) = joint_pdf(x(idx1), y(idx2));
end
end
image(prob_map);
This is very very slow. Is there a way of avoiding the looping?
One can hack into normpdf.m and get all the elements of prob_map in a vectorized manner and thus also avoid those many function calls, which must make it much more efficient. I like to call this hacked approach as using the "raw version" of normpdf's implementation. Here's the final code -
%// Define arrays for inputting into meshgrid
array1 = 0:0.1:10;
array2 = 0:0.1:10;
[x, y] = meshgrid(array1, array2);
%// Define parameteres for normpdf
mu = 1;
sigma = 1;
%// Use "raw version" of normpdf to calculate all prob_map elements in one go
dim1 = exp(-0.5 * ((x(:) - mu)./sigma).^2) ./ (sqrt(2*pi) .* sigma);
dim2 = exp(-0.5 * ((y(:) - mu)./sigma).^2) ./ (sqrt(2*pi) .* sigma);
prob_map = bsxfun(#times,dim1,dim2.');
If you are interested in further speeding it up, you can pre-calculate few more stuffs surrounding the x(:) and y(:) in dim1 and dim2 respectively!