matlab: upsampling a zero contour - matlab

I am using the undocumented contours function in Matlab to obtain the zero contour of a function mapping R^2 to R. The function contours() calls contourc() to obtain the zero contour in terms of array indices, and contours() then performs linear interpolation to convert from (M,N) array indices to (Y,X) data coordinates. This works well and gives a zero contour which is essentially accurate to machine precision. However, attempting to interpolate along this zero contour to obtain additional points fails, apparently because the interpolated points deviate to a small extent from the true zero contour; the resulting error is too large for the intended application.
As a simple example, one can compute a zero contour of the peaks function, compute the function values along the contour, and then compute the function values at new points interpolated along the contour. The maximum error is roughly eps(max(Z(:)) for the points found by countours/countourc and about ten orders of magnitude higher for the interpolated points.
% compute the zero contours; keep part of one contour
[X,Y,Z] = peaks(999);
XY0 = contours(X, Y, Z, [0 0]);
XY0 = XY0(:,140:(XY0(2,1)+1)); % keep only ascending values in the first contour for interpolation
% interpolate points along the zero contour
x2y = griddedInterpolant(XY0(1,:), XY0(2,:), 'linear','none');
X0i = linspace(XY0(1,1), XY0(1,end), 1e4);
Y0i = x2y(X0i);
% compute values of the function along the zero contour
Zi = interp2(X,Y,Z, XY0(1,:), XY0(2,:), 'linear', NaN);
Z0i = interp2(X,Y,Z, X0i, Y0i, 'linear', NaN);
% plot results
figure;
subplot(1,3,1); plot(XY0(1,:), XY0(2,:), '.'); hold on; plot(X0i, Y0i, 'Linewidth',1);
xlabel('X_0'); ylabel('Y_0'); title('(X_0,Y_0), (X_{0i},Y_{0i})');
subplot(1,3,2); plot(XY0(1,:), Zi, '.');
xlabel('X_0'); ylabel('Z_0'); title('f(X_0,Y_0)');
subplot(1,3,3); plot(XY0(1,:), Zi, '.'); hold on; plot(X0i, Z0i, '.');
xlabel('X_0'); ylabel('Z_0'); title('f(X_{0i},Y_{0i})');
The error is almost certainly because the interpolant does not truly follow the zero contour (i.e., having the same tangent, curvature, and indeed all higher derivatives at each point). Each interpolated point thus deviates to a small degree from the path of the zero contour. This is true for all tested methods of interpolation. If the functional form of the zero contour were known, one could fit a function to the zero contour, which would likely give satisfactory results. However, for the application motivating this question, an analytical solution of the zero contour is not possible.
Given the inadequacy of interpolation to this task, how can new points along the zero contour be obtained? The points should be such that the value of the function is as close to zero as for the set of points returned by contourc. Increasing the grid resolution globally is not an option since the memory and computational expense increase with the square of the resolution. Locally sampling at high resolution in an iterative manner over consecutive short intervals of the zero contour is an option, but would take time to implement, and I'm not aware of existing code (e.g., in the FEX) to perform such a task. Also, since contourc is closed-source, adapting it to this task is not an option.

I solved this problem by taking the interpolated (X,Y) values as an approximation of the true zero contour and reinterpolating the Y coordinate of each pair with respect to Z values of the surface flanking the approximate zero in order to locate the Y coordinate at which Z=0 for each (X,Y) pair. In so doing, uniform spacing of the interpolated X values is preserved.
For each (iX,iY) pair, where iX and iY denote the (fractional) array indices of the X and Y coordinate grids for the points interpolated along the zero contour, the approximate iY values are reinterpolated with respect to the values of Z at positions flanking (in the Y dimension) the approximate zero, i.e., at (iX,floor(iY)) and (iX,ceil(iY)). The Z values at these flanking points are themselves obtained by performing bilinear interpolation of Z at these points.
After reinterpolation, the magnitude of the error is the same as that of the points originally returned by contourc/contours.

Related

How to make smooth plot with matrix that don't have the same column and line [duplicate]

Let's say we have the following data:
A1= [41.3251
18.2350
9.9891
36.1722
50.8702
32.1519
44.6284
60.0892
58.1297
34.7482
34.6447
6.7361
1.2960
1.9778
2.0422];
A2=[86.3924
86.4882
86.1717
85.8506
85.8634
86.1267
86.4304
86.6406
86.5022
86.1384
86.5500
86.2765
86.7044
86.8075
86.9007];
When I plot the above data using plot(A1,A2);, I get this graph:
Is there any way to make the graph look smooth like a cubic plot?
Yes you can. You can interpolate in between the keypoints. This will require a bit of trickery though. Blindly using interpolation with any of MATLAB's commands won't work because they require that the independent axes (the x-axis in your case) to increase. You can't do this with your data currently... at least out of the box. Therefore you'll have to create a dummy list of values that span from 1 up to as many elements as there are in A1 (or A2 as they're both equal in size) to create an independent axis and interpolate both arrays independently by specifying the dummy list with a finer spacing in resolution. This finer spacing is controlled by the total number of new points you want to introduce in the plot. These points will be defined within the range of the dummy list but the spacing in between each point will decrease as you increase the total number of new points. As a general rule, the more points you add the less spacing there will be and so the plot should be more smooth. Once you do that, plot the final values together.
Here's some code for you to run. We will be using interp1 to perform the interpolation for us and most of the work. The function linspace creates the finer grid of points in the dummy list to facilitate the interpolation. N would be the total number of desired points you want to plot. I've made it 500 for now meaning that 500 points will be used for interpolation using your original data. Experiment by increasing (or decreasing) the total number of points and seeing what effect this has in the smoothness of your data.
I'll also be using the Piecewise Cubic Hermite Interpolating Polynomial or pchip as the method of interpolation, which is basically cubic spline interpolation if you want to get technical. Assuming that A1 and A2 are already created:
%// Specify number of interpolating points
N = 500;
%// Specify dummy list of points
D = 1 : numel(A1);
%// Generate finer grid of points
NN = linspace(1, numel(A1), N);
%// Interpolate each set of points independently
A1interp = interp1(D, A1, NN, 'pchip');
A2interp = interp1(D, A2, NN, 'pchip');
%// Plot the data
plot(A1interp, A2interp);
I now get the following:

Smooth plot of non-dependent variable graph

Let's say we have the following data:
A1= [41.3251
18.2350
9.9891
36.1722
50.8702
32.1519
44.6284
60.0892
58.1297
34.7482
34.6447
6.7361
1.2960
1.9778
2.0422];
A2=[86.3924
86.4882
86.1717
85.8506
85.8634
86.1267
86.4304
86.6406
86.5022
86.1384
86.5500
86.2765
86.7044
86.8075
86.9007];
When I plot the above data using plot(A1,A2);, I get this graph:
Is there any way to make the graph look smooth like a cubic plot?
Yes you can. You can interpolate in between the keypoints. This will require a bit of trickery though. Blindly using interpolation with any of MATLAB's commands won't work because they require that the independent axes (the x-axis in your case) to increase. You can't do this with your data currently... at least out of the box. Therefore you'll have to create a dummy list of values that span from 1 up to as many elements as there are in A1 (or A2 as they're both equal in size) to create an independent axis and interpolate both arrays independently by specifying the dummy list with a finer spacing in resolution. This finer spacing is controlled by the total number of new points you want to introduce in the plot. These points will be defined within the range of the dummy list but the spacing in between each point will decrease as you increase the total number of new points. As a general rule, the more points you add the less spacing there will be and so the plot should be more smooth. Once you do that, plot the final values together.
Here's some code for you to run. We will be using interp1 to perform the interpolation for us and most of the work. The function linspace creates the finer grid of points in the dummy list to facilitate the interpolation. N would be the total number of desired points you want to plot. I've made it 500 for now meaning that 500 points will be used for interpolation using your original data. Experiment by increasing (or decreasing) the total number of points and seeing what effect this has in the smoothness of your data.
I'll also be using the Piecewise Cubic Hermite Interpolating Polynomial or pchip as the method of interpolation, which is basically cubic spline interpolation if you want to get technical. Assuming that A1 and A2 are already created:
%// Specify number of interpolating points
N = 500;
%// Specify dummy list of points
D = 1 : numel(A1);
%// Generate finer grid of points
NN = linspace(1, numel(A1), N);
%// Interpolate each set of points independently
A1interp = interp1(D, A1, NN, 'pchip');
A2interp = interp1(D, A2, NN, 'pchip');
%// Plot the data
plot(A1interp, A2interp);
I now get the following:

How to create a 2D-matrix out of my data for surf()?

I have a 25000x3-matrix, with each row containing a x-, a y- and a z-value. Now I wanted to do a graphical plot out of these. But for using for example surf(Z) I have to use a mxn-matrix as Z with m equal the size of x and n equal the size of y. How can I reshape the matrix I have to the needed mxn-matrix? The problem is that my x- and y-values are no ints, but floats, so I assume that I have to do a interpolation first. Is that true? My data plotted with plot3 looks like:
The fact that your x- and y- values are not integers is not a problem at all. The real question is: are your (x,y) points forming a grid, or not ?
If your points are forming a grid, then you have to reshape your columns to form m-by-n arrays. You may need to sort your data according to the first, then second column and then use the reshape function.
If your points are not forming a grid, then you will have to make an interpolation. By chance the scatterinterpolant class can nicely help you in doing so.
As you can see, the data you are providing is neither given in a gridded way, nor is the point cloud clean. You could however try to do the following:
Project the point cloud onto the x-y plane
Triangulate those points
Give the points their original z-coordinate back.
Plot the surface using trisurf
Here is a MATLAB code that does this:
%// Generate some points P
[X,Y] = ndgrid(0:30);
P = [X(:), Y(:), X(:).^2+Y(:)];
%%// Here the actual computation starts
[~,I] = unique(P(:,1:2),'rows'); %// Remove points with duplicate (x,y)-coords
P = P(I,:);
T = delaunay(P(:,1),P(:,2)); %// Triangulate the 2D-projection
surface = triangulation(T, P(:,1), P(:,2), P(:,3)); %// Project back to 3D
trisurf(surface); %// Plot
You may want to remove stray points first, though.

number of contours in contourf function

What is the default for the contours in a contourf function in matlab?
For example:
Z = peaks(20);
contourf(Z);
What do each of these contours represent? If I don't specify the second term in contourf e.g. contourf(Z,10) which would give 10 contour lines, how does matlab choose the number of contours?
You can look up the detailed algorithm for calculating the initial contour level step sizes from MATLABROOT\toolbox\matlab\specgraph\#specgraph\#contourgroup\refresh.m, around line 25.
Basically, Matlab divides the range into ~10 steps, but adjusts that number a bit depending on the exact value of the range of z-values.
There is no default. You are defining the number of contours by using:
Z=peaks(20);
This in effect returns an 20x20 [m,n] matrix of peaks which is stored in Z.
The ranges of the x-axis and y-axis are based on the size of array Z.
The number of contour lines and the values of the contour lines are taken from the minimum and maximum values of peaks inside the Z array.
The Z array is populated with the peaks() function which uses Normal Distribution (or Gaussian distribution).
As the documentation of the
contourf function says:
The number of contour lines and the values of the contour lines are chosen automatically based on the minimum and maximum values of Z. The ranges of the x-axis and y-axis are [1:n] and [1:m], where [m,n] = size(Z).

How to plot parametric functions between infinite limits?

How to plot these parametric functions with infinite limits to get a circle using matlab?
x(t)=2t/(1+t.^2)
y(t)=(1-t.^2)/(1+t.^2)
I don't know about infinite limits but
%Construct a vector of t ranging from a very small number to a very large number
t = -1000:0.1:1000;
%Create x and y vectors based on your formula (with a couple of extra dots for element wise division)
x =2*t./(1+t.^2);
y =(1-t.^2)./(1+t.^2);
%Just normal plotting now
plot(x,y)
gives me a circle. There will still be a tiny gap around (0, -1) however.