Smooth Excel-like plot with correct legend in Matlab - matlab

I have some sparse data and want to plot them as markers connected by a smooth, interpolated line - like the default behaviour of Microsoft Excel.
There are solutions to this problem easily found on the internet, but I find them unsatisfactory. What they do is: plot the sparse data as one data set drawing it as markers without lines, interpolate it with a method of choice and plot the interpolation as the second data set, with lines without markers.
The problem with these tricks is that in the legend the two data sets will be listed separately. I would expect a single data set depicted in the legend as a line crossing through a marker.
Is it possible in Matlab?

If you want to plot an interpolated line there are lots of ways to do that. You can try generating an interpolated line using the matlab interp1() function.
Let's create x and y data with no NaN.
x = randn(1,10)
y = randn(1,10)
If you want 1000 data points where previously you only had a few, that's pretty easy:
x2 = min(x):(max(x)-min(x))/1000:max(x)
y2 = interp1(x,y,x2,'cubic')
and you can plot your data and spline using
plot(x,y,'r+')
hold on
plot(x2,y2,'r-')
A custom legend is straightforward when you use handle graphics. You can plot a dummy data set with a red line passing through a marker using
h(1) = plot(NaN,NaN,'r-+')
lstring{1} = 'Data';
You can then add a legend that points to this data set using
legend(h,lstring)
You'll end up with something that looks roughly like this:
The nice thing about using handle graphics (i.e. the h) is you can throw whatever data series you want into the legend as h(end+1) and lstring{end+1}.

Related

How to add datatips in a fitted surface chart

I have created a fitted surface from x, y, z data points. How do I insert data tips for the min and max value in the chart?
defDM_fit = fit([def_X, def_Y],def_Z,'cubicinterp');
clf;
figure(2)
plot(defDM_fit,[def_X, def_Y],def_Z);
using the following test code is raising an error "Invalid argument. The object has been deleted or does not support data tips":
datatip(defDM_fit, def_X(1), def_Y(1), def_Z(1))
And I do not know how to manage that tips show up at min and max value in the chart by code.
Plotting a fitted surface created with fit outputs a 2x1 graphics array. The first element is the surface (Surface object), the second element is a Line object that holds the points on which your data was fitted. In order to add datatips, you will have to use one of these two objects, and more probably the Surface object, for example:
load franke
T = table(x,y,z);
f = fit([T.x, T.y],T.z,'linearinterp');
p = plot( f, [T.x, T.y], T.z );
datatip(p(1),T.x(1),T.y(1),T.z(1))
The first argument of datatip is a graphic object, not a surface/line fit object.
defDM_fit = fit([def_X, def_Y],def_Z,'cubicinterp');
figure(2)
p=plot(defDM_fit,[def_X, def_Y],def_Z);
datatip(p, def_X(1), def_Y(1), def_Z(1))
There are many other things that may be wrong with your approach, particularly because you are showing a surface plot (surf) in your example, yet your code uses a line plot (plot). I am not even sure what the arguments even are, the way you are putting them.
Consider reading the documentation of the functions you are using, as they come with examples on how to use them: https://uk.mathworks.com/help/curvefit/fit.html

Animated graph of ODE

I am attempting to create an animated graph by plotting specific points form 2 column vectors buy am having issues.
I have attempted to use the pause, drawnow, changing my vectors and am still having trouble with my code not working. I have got my vector in a for loop which specifies the points needing to be plotted.
Using ODE45 I have made a column vector with 2 rows.
grid on
func=plot(t,x);
%set(gca,'XLim',[0 tmax])
for i=1:length(x)
set(func,'XData',x(1,i),'YData',x(2,i));
drawnow
end
I expect the output to be an animated graph, but currently, all that I'm getting is either a nonanimated graph, or a bunch of errors saying that I am exceeding the array bounds.
You are using plot with a single point. The default plotting style for plot is to not show individual points, but to connect the input data with lines.
Either change the LineSpec property to e.g. 'o':
func = plot(x(1,1), x(2,1), 'o');
or use the scatter function to plot individual points:
func = scatter(x(1,1), x(2,1));

Making an accurate colorbar for a simple plot

I am trying to make a simple plot (for this example doing a plot of y=x^2 will suffice) where I want to set the colors of the points based on their magnitude given some colormap.
Following along my simple example say I had:
x = 1:10;
y = x.^2;
Use gscatter(x,y,jet(10)); legend hide; colorbar which produces a plot with the points colored but the colorbar does not agree with the colored values. (Can't post picture as this is my first post). Using a caxis([1,100]) command gives the right range but the colors are still off.
So I have two questions:
(1) How can I fix the colors to fit to a colorbar given a range? In my real data, I am looking at values that range from -50 to 50 in some instances and have many more data points.
(2) I want to create a different plot with the same points (but on different axes) and I want the colors of each point on this new plot to have the same colors as their counterparts in the previous plot. How can I, programmatically, extract the color from each point so I can plot it on two different sets of axes?
I would just move the points into a matrix and do an imagesc() command but they aren't spaced as integers or equally so simple scaling wouldn't work either.
Thanks for any help!
Regarding you first question, you need to interpolate the y values into a linear index to the colormap. Something like:
x = 1:10;
y = x.^4;
csize = 128;
cmap = jet(csize);
ind = interp1(linspace(min(y),max(y),csize),1:csize,y,'nearest');
scatter(x,y,14,cmap(ind,:),'filled')
colorbar
caxis([min(y) max(y)])
Using interp1 in this case is an overkill; you could calculate it directly. However, I think in this way it is clearer.
I think it also answers your 2nd question, since you have the index of the color of each data point, so you can use it again in the same way.

How to plot multiple (x,y) series on the same axes with Chaco?

I have several sets of (x,y) data that I'd like to plot as line plots on the same figure. I have no trouble with matplotlib doing this, but I cannot get the same results with Chaco. Code and output are shown below.
My matplotlib-based code looks like this:
for track in tracks:
xw = np.array(track['xw'])
yw = np.array(track['yw'])
plt.plot(xw, yw, 'b-')
if not plt.gca().yaxis_inverted():
plt.gca().invert_yaxis()
My Chaco-based code looks like this:
for track in tracks:
x = np.array(track['xw'])
y = np.array(track['yw'])
plot = create_line_plot((x,y), color='blue', width=1.0)
plot.origin = 'top left'
container.add(plot)
if track == tracks[0]:
add_default_grids(plot)
add_default_axes(plot)
My matplotlib-based output looks like this:
My chaco-based output looks like this:
The problem with my Chaco-based code above was that I was using an OverlayPlotContainer (container). Because of this, each plot (from create_line_plot) was being drawn with its own axes rather than each plot being drawn on the same set of axes. The following works:
pd = ArrayPlotData()
plot = Plot(pd)
for ii, track in enumerate(tracks):
x = np.array(track['xw'])
y = np.array(track['yw'])
x_key = 'x'+str(ii)
y_key = 'y'+str(ii)
pd.set_data(x_key, x)
pd.set_data(y_key, y)
plot.plot((x_key, y_key), color='blue', origin='top left')
Chaco and Matplotlib are not really trying to address the same types of problems. Matplotlib is better for bringing up a quick plot in a script and is very easy to use. Chaco is a plotting framework that allows:
a stronger architecture for dealing with larger datasets more smoothly
a frameworks that makes it easy to build a GUI around the plot (Traits)
the creation of custom interactive tools on top of plots
Leveraging that framework however requires more code, and the recommeded way to build plots in Chaco is to use its Object Oriented syntax. The simplest but realistic Chaco plot would be something like this. It requires an ArrayPlotData to hold the data and a Plot object to create a plotting area and hold different ways to render that data.
You want to have many line plots instead of just one. You can simply add a for loop inside the __init__ method to add every relevant numpy array inside the ArrayPlotData and for each pair of arrays, to call the plot method of the Plot object (but only 1 Plot object is needed). Something similar is done a little further down in the page.
Good luck,

matlab - can I use roipoly to get data from a scatter plot?

I want to select data using a polygonal shape. I understand roipoly does that for 'images'. is there something like this for scatter plots?
You can use data brushing to mark data on a scatter plot then extract it to the workspace. Look for the little brush symbol at the top of a figure window.
See Marking up graphs with Data Brushing from Matlab, and Accessing plot brushed data from the very useful Undocumented Matlab.
If you want to draw a complex polygon, you can use impoly and inpoly:
X = rand(200, 2);
scatter(X(:,1), X(:,2));
h = impoly();
% now you go and position the polygon, control returns once you've 'finsished' with it '
nodes = getPosition(h);
selected_indices = inpoly(X, nodes);