Contour plot with 3 vector - matlab

I've a matrix M 10201x3 where the first 2 column are constant used to compute the response Z in column 3. Example:
.... ... .................
0.0031 0.02 0.792729854583740
0.0031 0.03 0.802729845046997
0.0031 0.04 0.812729895114899
0.0031 0.05 0.822729885578156
.... ... .................
0.0034 0.02 0.867461800575256
0.0034 0.03 0.877461791038513
0.0034 0.04 0.887461841106415
0.0034 0.05 0.897461831569672
0.0034 0.06 0.907461822032929
.... ... .................
I wanna make a contour plot where X = M(:,1), Y = M(:,2) and Z = M(:,3) with different colors for different hights. I need to do the same thing in both 2D and 3D.

I assume your data is regular, and you know how many repeating x-elements you have.
Let's call the number of repeating x = L - or you'll be able to find that out.
You need to reshape your vectors:
X = reshape(X,[],L);
Y = reshape(Y,[],L);
Z = reshape(Z,[],L);
You need Z how it is, but just the first row of X and the first column of Y.
X = X(:,1);
Y = Y(1,:);
and then you can use contour:
contour(X,Y,Z);
There is no need for interpolation!
contour(X,Y,Z), contour(X,Y,Z,n), and contour(X,Y,Z,v) draw contour
plots of Z using X and Y to determine the x- and y-axis limits.
If X and Y are vectors, then the length of X must equal the number of
columns in Z and the length of Y must equal the number of rows in Z.
If X and Y are matrices, then their sizes must equal the size of Z.
Therefore shorter:
X = X(1:L:end);
Y = Y(1:L);
Z = reshape(Z,[],L);
contour(X,Y,Z);

I would suggest to transform this array to three 2D arrays using interpolation with function griddata(). Interpolation could be useful for nonregular data. First, we create grid of coordinates:
xq=min(X):(max(X)-min(X))/200:max(X);
yq=min(Y):(max(Y)-min(Y))/200:max(Y);
[Xq, Yq] = meshgrid(xq,yq);
Than, we use interpolating:
Zq =griddata(X,Y,Z,Xq,Yq);
Than you can plot:
countour(Xq,Yq,Zq)

Related

Methods for smoothing contour lines

I have a database P with columns X, Y and Z:
x=0:1:50;
r=3.*rand(1,51);
P=[cos(x')+r',sin(x')+r',sin(x'+r').*cos(x')+r'];
P = sortrows(P,[1,2]);
N = 500;
xv = linspace(min(P(:,1)), max(P(:,1)), N);
yv = linspace(min(P(:,2)), max(P(:,2)), N);
[X,Y] = ndgrid(xv, yv);
Z = griddata(P(:,1), P(:,2), P(:,3), X, Y);
contourf(X, Y, Z, 35)
With the code above, I get the following subplot (right):
This "angularity" arises due to the addition of a vector r of random values ​​to the data. How to smooth out this angularity and make the graph smoother?
I tried to increase N to 2500. It did not give a significant result (in fact, it stopped changing significantly after N=1500).
To smooth your 2D data you can use a 2D convolution, with the operator conv2
With your example data:
n = 10 ;
kernel = ones(n)/n.^2 ;
Zs = conv2(Z, kernel,'same') ;
contourf(X, Y, Zs, 35) ;
title(sprintf('Filter size: n=%d',n))
will yield:
You have to adjust the filter size (the parameter n) until you get the desired result. For example with n=20 you will get:
And for n=50:
A few things to keep in mind:
Since you have NaN in your matrix Z, the filtering will erode the border of you initial domain. The stronger the filtering/smoothing (higher n), the more erosion you will notice until very little non-NaN data is left .
For it to be a smoothing operation and not another transform or filtering, the convolution kernel has to be built so (i) all the elements are of equal value, and (ii) the total sum of the elements should be 1.
A quick demonstration with n=2:
>> n=2
n =
2
>> kernel = ones(n)/n.^2
kernel =
0.25 0.25
0.25 0.25
>> sum(sum(kernel))
ans =
1

discretise domain of x and y with 0.01 separation between points

I am looking to calculate an array from a formula using x and y variables, the domain of x is (0,50) and y is (0,30) . I am asked to discretise the domain of x and y with 0.01 separation between points, then compute L(x,y) (which I have a formula for)(This will be points of a graph, ultimately I'm looking for the min lengths between points)
I'm not sure what I need to define in my script because if I define x and y as arrays with 0.01 separation they end up being uneven and unable to calculate as the arrays are uneven
%change these values for A, B and C positions
Ax=10;
Ay=5;
Bx=15;
By=25;
Cx=40;
Cy=10;
x = 0:0.01:50; % Array of values for x from 0-50 spaced at 0.01
y = 0:0.01:30; % Array of values for y from 0-30 spaced at 0.01
%length of point P from A, B and C and display
Lpa=sqrt((Ax-x).^2+(Ay-y).^2);
Lpb=sqrt((Bx-x).^2+(By-y).^2);
Lpc=sqrt((Cx-x).^2+(Cy-y).^2);
L=Lpa+Lpb+Lpc
I am getting an error telling me the two matrix are not even which makes sense to not work but I'm not sure how to define a matrix that will result in the minimum x and y values I am after.
Any help would be greatly appreciated.
You want to calculate L for each possible pair of x and y. In other words, for the first value of x = 0, you will calculate L for all y values from 0 to 30, then for next value of x = 0.01, you will do the same and so on.
MATLAB has a really cool function called meshgrid to create a matrix for every pair of x and y. So after generating x and y, change your code to the following to get a 2D matrix for L -
[X, Y] = meshgrid(x, y)
%length of point P from A, B and C and display
Lpa = sqrt((Ax - X).^2 + (Ay - Y).^2);
Lpb = sqrt((Bx - X).^2 + (By - Y).^2);
Lpc = sqrt((Cx - X).^2 + (Cy - Y).^2);
L = Lpa + Lpb + Lpc

Set surf minimum for matlab

I have a function which takes a voxel representation of a 3D landscape and can plot a X-Y section to show the middle of the landscape. The voxel representation is stored in a 3 dimensional matrix with a number that represents something important. Obviously the matrix is
1,1,1
2,2,2
in terms of accessing the elements but the actual 3D locations are found in the following method:
(index-1)*resolution+0.5*resolution+minPos;
where resolution is the grid size :
resolution
<-->
__ __ __
|__|__|__|
<- Min pos
and minPos is where the grid starts.
Now in terms of the actual question, i would like to extract a single X-Y section of this voxel representation and display it as a surf. This can be done by just doing this:
surf(voxel(:, :, section))
however then you get this:
The obvious problem is that the grid will start at 0 because that is how the matrix representation is. How can i set the minimum and cell size for surf, ie so that the grid will start at the minimum (shown above) and will have the grid spacing of resolution (shown above).
Read the documentation of surf, you can also provide x and y coordinates corresponding to your data points.
surf(X,Y,Z)
X and Y can be either vectors or matrices:
surf(X,Y,Z) uses Z for the color data and surface height. X and Y are vectors or matrices defining the x and y components of a surface. If X and Y are vectors, length(X) = n and length(Y) = m, where [m,n] = size(Z). In this case, the vertices of the surface faces are (X(j), Y(i), Z(i,j)) triples. To create X and Y matrices for arbitrary domains, use the meshgrid function
Example
Z=[ 0 1 2 3;
7 6 5 4;
8 9 10 11];
x=[-1 0 1 2];
y=[-2 0 2];
surf(x,y,Z);
Of course you have to match Z, x and y matrices/vectors as clearly described in the doc^^
Just remember that elements in columns of Z are surf'ed as values along the y-axis, elements in rows of Z are surf'ed as values along the x-axis. This is clearly to be seen in the example picture.
Solution
I think you switched the x and y-axis around, which you can fix by just transposing z:
s = size(voxel);
xi = (minPosX:resolution:(minPosX+resolution*s(1)-1));
yi = (minPosY:resolution:(minPosY+resolution*s(2)-1));
z = (voxel(:,:,section));
surf(xi, yi, z');
or that you're picking the wrong numbers for constructing xi and yi and it should be this instead:
xi = (minPosX:resolution:(minPosX+resolution*s(2)-1));
yi = (minPosY:resolution:(minPosY+resolution*s(1)-1));
z = (voxel(:,:,section));
surf(xi, yi, z);
So it was easy enough to do:
lets say we have a 3D matrix "voxel";
s = size(voxel);
xi = (minPosX:resolution:(minPosX+resolution*s(1)-1));
yi = (minPosY:resolution:(minPosY+resolution*s(2)-1));
z = (voxel(:,:,section));
[x y] = meshgrid(xi, yi);
x = x';
y = y';
surf(x, y, z);
Provides the following plot:
This is rotated which is annoying, I cant seem to get it to rotate back (I could just visualise around the other way but that's ok)

Interpolation between components of a Matrix in MATLAB

In a project that I am doing I need to reach floating indexed elements of a matrix. That is to say for instance I want to reach the (16.25,1) th element of a matrix. That might seem odd at the first glance. However, by (16.25,1), I mean the interpolation between (16,1) and (17,1) with weights of .25 and .75 respectively.
Is there a built-in function for that?
Many thanks,
Safak
You can use interp2:
Z = randi(10,10); % 10 x 10 random matrix with integers from 1 to 10
Z(1:2,1:2)
%ans =
% 2 4
% 7 6
% use interp2 to interpolate at row 1.5, col 2
z = interp2(Z,1.5,2)
% z = 6.5000
You can use 2-D interpolation:
ZI = interp2(Z,XI,YI) assumes that X = 1:n and Y = 1:m, where [m,n] = size(Z)
where Z is your matrix, and XI & YI are your fractional indices.

Ellipse around the data in MATLAB

I would like to reproduce the following figure in MATLAB:
There are two classes of points with X and Y coordinates. I'd like to surround each class with an ellipse with one parameter of standard deviation, which determine how far the ellipse will go along the axis.
The figure was created with another software and I don't exactly understand how it calculates the ellipse.
Here is the data I'm using for this figure. The 1st column is class, 2nd - X, 3rd - Y. I can use gscatter to draw the points itself.
A = [
0 0.89287 1.54987
0 0.69933 1.81970
0 0.84022 1.28598
0 0.79523 1.16012
0 0.61266 1.12835
0 0.39950 0.37942
0 0.54807 1.66173
0 0.50882 1.43175
0 0.68840 1.58589
0 0.59572 1.29311
1 1.00787 1.09905
1 1.23724 0.98834
1 1.02175 0.67245
1 0.88458 0.36003
1 0.66582 1.22097
1 1.24408 0.59735
1 1.03421 0.88595
1 1.66279 0.84183
];
gscatter(A(:,2),A(:,3),A(:,1))
FYI, here is the SO question on how to draw ellipse. So, we just need to know all the parameters to draw it.
Update:
I agree that the center can be calculated as the means of X and Y coordinates. Probably I have to use principal component analysis (PRINCOMP) for each class to determine the angle and shape. Still thinking...
Consider the code:
%# generate data
num = 50;
X = [ mvnrnd([0.5 1.5], [0.025 0.03 ; 0.03 0.16], num) ; ...
mvnrnd([1 1], [0.09 -0.01 ; -0.01 0.08], num) ];
G = [1*ones(num,1) ; 2*ones(num,1)];
gscatter(X(:,1), X(:,2), G)
axis equal, hold on
for k=1:2
%# indices of points in this group
idx = ( G == k );
%# substract mean
Mu = mean( X(idx,:) );
X0 = bsxfun(#minus, X(idx,:), Mu);
%# eigen decomposition [sorted by eigen values]
[V D] = eig( X0'*X0 ./ (sum(idx)-1) ); %#' cov(X0)
[D order] = sort(diag(D), 'descend');
D = diag(D);
V = V(:, order);
t = linspace(0,2*pi,100);
e = [cos(t) ; sin(t)]; %# unit circle
VV = V*sqrt(D); %# scale eigenvectors
e = bsxfun(#plus, VV*e, Mu'); %#' project circle back to orig space
%# plot cov and major/minor axes
plot(e(1,:), e(2,:), 'Color','k');
%#quiver(Mu(1),Mu(2), VV(1,1),VV(2,1), 'Color','k')
%#quiver(Mu(1),Mu(2), VV(1,2),VV(2,2), 'Color','k')
end
EDIT
If you want the ellipse to represent a specific level of standard deviation, the correct way of doing is by scaling the covariance matrix:
STD = 2; %# 2 standard deviations
conf = 2*normcdf(STD)-1; %# covers around 95% of population
scale = chi2inv(conf,2); %# inverse chi-squared with dof=#dimensions
Cov = cov(X0) * scale;
[V D] = eig(Cov);
I'd try the following approach:
Calculate the x-y centroid for the center of the ellipse (x,y in the linked question)
Calculate the linear regression fit line to get the orientation of the ellipse's major axis (angle)
Calculate the standard deviation in the x and y axes
Translate the x-y standard deviations so they're orthogonal to the fit line (a,b)
I'll assume there is only one set of points given in a single matrix, e.g.
B = A(1:10,2:3);
you can reproduce this procedure for each data set.
Compute the center of the ellipsoid, which is the mean of the points. Matlab function: mean
Center your data. Matlab function bsxfun
Compute the principal axis of the ellipsoid and their respective magnitude. Matlab function: eig
The successive steps are illustrated below:
Center = mean(B,1);
Centered_data = bsxfun(#minus,B,Center);
[AX,MAG] = eig(Centered_data' * Centered_data);
The columns of AX contain the vectors describing the principal axis of the ellipsoid while the diagonal of MAG contains information on their magnitude.
To plot the ellipsoid, scale each principal axis with the square root of its magnitude.