non-homogenous grouped data in MATLAB plotyy() - matlab

I have to plot 1 line plot and 3 grouped scatter plots in a single plot window.
The following is the code I tried,
figure;
t1=0:0.1:10;
X = 2*sin(t1);
ts = 0:1:10;
Y1 = randi([0 1],length(ts),1);
Y2 = randi([0 1],length(ts),1);
Y3 = randi([0 1],length(ts),1);
plotyy(t1,X,[ts',ts',ts'],[Y1,Y2,Y3],'plot','scatter');
%plotyy(t1,X,[ts',ts',ts'],[Y1,Y2,Y3],'plot','plot');
The following are my questions,
The above code works if I replace 'scatter' by 'plot' (see commented out line), but 'scatter' works only for 1 data set and not for 3. Why?
How to individually assign colors to the 3 grouped scatter plots or plots?

Read the error message you're given:
Error using scatter (line 44) X and Y must be vectors of the same
length.
If you look at the documentation for scatter you'll see that the inputs must be vectors and you're attempting to pass arrays.
One option is to stack the vectors:
plotyy(t1,X,[ts';ts';ts'],[Y1;Y2;Y3],'plot','scatter');
But I don't know if this is what you're looking for, it certainly doesn't look like the commented line. You'll have to clarify what you want the final plot to look like.
As for the second question, I would honestly recommend not using plotyy. I may be biased but I've found it far to finicky for my tastes. The method I like to use is to stack multiple axes and plot to each one. This gives me full control over all of my graphics objects and plots.
For example:
t1=0:0.1:10;
X = 2*sin(t1);
ts = 0:1:10;
Y1 = randi([0 1],length(ts),1);
Y2 = randi([0 1],length(ts),1);
Y3 = randi([0 1],length(ts),1);
% Create axes & store handles
h.myfig = figure;
h.ax1 = axes('Parent', h.myfig, 'Box', 'off');
h.ax2 = axes('Parent', h.myfig, 'Position', h.ax1.Position, 'Color', 'none', 'YAxisLocation', 'Right');
% Preserve axes formatting
hold(h.ax1, 'on');
hold(h.ax2, 'on');
% Plot data
h.plot(1) = plot(h.ax1, t1, X);
h.scatter(1) = scatter(h.ax2, ts', Y1);
h.scatter(2) = scatter(h.ax2, ts', Y2);
h.scatter(3) = scatter(h.ax2, ts', Y3);
Gives you:
And now you have full control over all of the axes and line properties. Note that this assumes you have R2014b or newer in order to use the dot notation for accessing the Position property of h.ax1. If you are running an older version you can use get(h.ax1, 'Position') instead.

Related

Correctly aligning labels for subgroups within a tiledlayout

I want to put 5 plots in a figure using tiledlayout, 2x2 plots at the top then a plot at the bottom that spans two columns. There should be two sets of axis labels: one for the 2x2 plot and another for the bottom plot. My MWE is
x = linspace(-2,2,100);
y1 = sin(x);
y2 = cos(x);
y3 = sin(x).*cos(2*x);
y4 = sin(2*x).*cos(x);
y5 = y3 + y4;
figure('PaperUnits', 'inches', 'PaperSize', [4 6], 'PaperPosition', [0 0 4 6]);
t = tiledlayout(3,2,'TileSpacing','compact','Padding','compact');
nexttile; plot(x,y1); title('y_1');
nexttile; plot(x,y2); title('y_2');
nexttile; plot(x,y3); title('y_3');
nexttile; plot(x,y4); title('y_4');
xlabel(t,'time t_\alpha')
ylabel(t,'amplitude \alpha')
nexttile([1 2]); plot(x,y5); title('y_5');
xlabel('time t_\beta')
ylabel('amplitude \beta')
saveas(gcf,'myfig.jpg')
This gives me the following figure:
I want the amplitude \alpha ylabel and the time t_alpha xlabel to be aligned correctly for the 2x2 plots (as indicated by my red annotations). Is there a way to do this?
I'm using MATLAB R2020a. Note that the tiledlayout function was introduced in R2019b.
An approximation of what you wanted can be achieved by the approach I mentioned in my comment (using a "nested" tiledlayout). The steps necessary to make it work are:
Creating an external vertical layout.
Reserving the first two rows for the nested tiledlayout, by asking a 2-by-1 nexttile, making the resulting axes hidden, and creating a uipanel in its place. Calling uipanel is necessary because the parent of a tiledlayout cannot be another tiledlayout, but it can be a Panel.
Plotting everything in the appropriate panels, and applying the labels accordingly.
x = linspace(-2,2,100);
y1 = sin(x);
y2 = cos(x);
y3 = sin(x).*cos(2*x);
y4 = sin(2*x).*cos(x);
y5 = y3 + y4;
hF = figure('PaperUnits', 'inches', 'PaperSize', [4 6], 'PaperPosition', [0 0 4 6]);
tOut = tiledlayout(hF,3,1);
hAx = nexttile(tOut, [2,1]); hAx.Visible = 'off';
hP = uipanel(hF, 'Position', hAx.OuterPosition, 'BorderWidth', 0);
tIn = tiledlayout(hP,2,2,'TileSpacing','compact','Padding','compact');
nexttile(tIn); plot(x,y1); title('y_1');
nexttile(tIn); plot(x,y2); title('y_2');
nexttile(tIn); plot(x,y3); title('y_3');
nexttile(tIn); plot(x,y4); title('y_4');
xlabel(tIn,'time t_\alpha')
ylabel(tIn,'amplitude \alpha')
hAx = nexttile(tOut); plot(x,y5); title('y_5');
xlabel(hAx, 'time t_\beta')
ylabel(hAx, 'amplitude \beta')
Resulting in:
From here you can try tweaking the individual GUI elements (layouts, axes, panel) to get a result closer to what you wanted.
A completely different route I can suggest is starting with your code and adding the labels for the top 4 plots using the annotation function (instead of xlabel and ylabel).

How do I plot more than two functions onto the same graph with very different ranges in Octave?

I can't find any info on how to do this on the Internet other than to use plotyy which only seems to work for two functions.
From Matlab documentation:
Use Right y-Axis for Two Data Sets
Plot three data sets using a graph with two y-axes. Plot one set of
data associated with the left y-axis. Plot two sets of data associated
with the right y-axis by using two-column matrices.
x = linspace(0,10);
y1 = 200*exp(-0.05*x).*sin(x);
y2 = 0.8*exp(-0.5*x).*sin(10*x);
y3 = 0.2*exp(-0.5*x).*sin(10*x);
plotyy(x,y1,[x',x'],[y2',y3']);
In my opinion, the way to do this that confers the most manual control is to create three overlapping axes with the plots you need, and only display the axis for the topmost one. You could even create 'empty' axes just so you they can serve as the only axis with defined 'limits' in the x and y axes.
Example:
ax1 = axes();
X1 = linspace(0,8*pi, 100); Y1 = sin(X1);
plot(X1, Y1, 'r', 'linewidth', 10);
ax2 = axes();
h = ezplot(#(x) x .* sin(x), [-100, 100]); set(h, 'color', 'w');
ax3 = axes();
image()
%% place them on top of each other by calling them in the order you want
axes(ax3); % bottommost
axes(ax1);
axes(ax2); % topmost
set(ax1, 'visible', 'off');
set(ax2, 'visible', 'off');
set(ax3, 'visible', 'on'); % this is the axes who's limits will show

How to specify axes when using the function `fit`

I have two axes: one for viewing images and the other for plotting graphs. I get this error when I try to specify which axes I want to plot the data on: Error using plot. A numeric or double convertible argument is expected when trying plot(handles.axis,curve,x,y).
figure
handles.axis = gca;
x = 1:10;
y = 1:10;
curve = fit(x',y','linearinterp');
plot(curve,x,y) % works fine
plot(handles.axis,curve,x,y) % doesn't work
plot(curve,x,y,'Parent',handles.axis) % doesn't work
You can paste this example into Matlab to try it out. How can the code be corrected in order to specify the axes?
plot in the curve fitting toolbox is not the same as MATLAB's base plot. Though there is a documented syntax for specifying the parent axes for sfit objects, there doesn't seem to be one for cfit objects, which would be returned by your fit call in this case.
However, from the documentation we see that:
plot(cfit) plots the cfit object over the domain of the current axes, if any
So if the current axis is set prior to the plot call it should work as desired. This can be done either by modifying the figure's CurrentAxes property or by calling axes with the handle of an axes object as an input.
% Set up GUI
h.f = figure;
h.ax(1) = axes('Parent', h.f, 'Units', 'Normalized', 'Position', [0.07 0.1 0.4 0.85]);
h.ax(2) = axes('Parent', h.f, 'Units', 'Normalized', 'Position', [0.55 0.1 0.4 0.85]);
% Set up curve fit
x = 1:10;
y = 1:10;
curve = fit(x', y', 'linearinterp'); % Returns cfit object
axes(h.ax(2)); % Set right axes as CurrentAxes
% h.f.CurrentAxes = h.ax(2); % Set right axes as CurrentAxes
plot(curve, x, y);
I refine my answer as follows:
It looks that the plot function in matlab does not like a fit object after an axis followed by two vectors. In such case, I would do something like this:
x = 1:10;
y = 1:10;
figure % new figure
ax1 = subplot(2,1,1);
ax2 = subplot(2,1,2);
curve = fit(x',y','linearinterp');
plot(ax1,x,curve(x));
hold on;plot(ax1,x,y,'o') % works fine
plot(ax2,x,curve(x));
hold on;plot(ax2,x,y,'o') % works fine
Actually the trick is to provide x and then curve(x) as two vectors without giving the whole fit-object to the plot function.

How to make a matlab legend recognize multiple scatter plots?

I want to place three scatter plots in the same figure window and have a legend that describes them. The scatter plots all load in the same window just fine, but the legend only recognizes the last series. In other words, the legend shows a red marker (the color for the last series) for each of its entries.
How do I make the legend recognize each scatter and not just the last one? I've tried a bunch of different things, and none of them seem to work. Thanks!
The picture is the plot for one of my datasets, note the legend.
s10 = scatter3(x1, y1, z1, 'b'); hold on;
s1 = scatter3(x2, y2, z2, 'g'); hold on;
s01 = scatter3(x3, y3, z3, 'r'); hold on;
legend([s10,s1,s01], {'10ms', '1ms', '0.1ms'})
% Every legend entry is red (pertains to the last series)
I can't reproduce the problem. Using your code above with random data seems to work (I did fix a typo, you need a comma after the first argument to legend):
x1 = rand(10, 1); y1 = rand(10, 1); z1 = rand(10, 1);
x2 = rand(10, 1); y2 = rand(10, 1); z2 = rand(10, 1);
x3 = rand(10, 1); y3 = rand(10, 1); z3 = rand(10, 1);
s10 = scatter3(x1, y1, z1, 'b'); hold on;
s1 = scatter3(x2, y2, z2, 'g'); hold on;
s01 = scatter3(x3, y3, z3, 'r'); hold on;
legend([s10,s1,s01], {'Series 10', 'Series 1', 'Series 01'})
For me this seems to be associated with the recent version of Matlab (R2015b); in this version I get the same problem as you with the legend entries showing only one color (in contrast to the other answers that can't reproduce the problem). But if I roll back to a previous version (R2010b), the problem goes away. I'm not sure if you have that option, but it might help diagnose the precise issue.
If the previous answer doesn't work, you can also exploit dynamic legends. This answer is what is inspiring this post: Dynamic Legend (Updates in every recursion). This is a rather undocumented feature but it does work very well. Basically, after each plot, you are able to dynamically update what the legend looks like without having to make one call to legend that has all of them together.
As such, try something like this. I'll borrow some of the previous answer's code to get me started:
x1 = rand(10, 1); y1 = rand(10, 1); z1 = rand(10, 1);
x2 = rand(10, 1); y2 = rand(10, 1); z2 = rand(10, 1);
x3 = rand(10, 1); y3 = rand(10, 1); z3 = rand(10, 1);
scatter3(x1, y1, z1, 'b', 'DisplayName', '10ms'); hold on;
legend('-DynamicLegend');
scatter3(x2, y2, z2, 'g', 'DisplayName', '1ms'); hold on;
legend('-DynamicLegend');
scatter3(x3, y3, z3, 'r','DisplayName', '0.1ms'); hold on;
legend('-DynamicLegend');
Call scatter3, then make sure that you use the 'DisplayName' flag and place what you would normally put in the appropriate legend spot. After each call to scatter3 after, you use the legend('-DynamicLegend'); command to signal to MATLAB that the legend entries will be forthcoming... you're going to specify them in the 'DisplayName' flag.
When you do that, this is the figure I get:
As a minor note, I can't reproduce your plot either. I get the same plot as the previous answer.
This issue is caused by a Matlab bug affecting version R2015b. It was fixed in R2016a. There is a bugreport here, which contains a patch and 3 alternative workarounds.
Here are the workarounds in case the link goes dead:
If you are unable to install the patch, there are three alternative workarounds:
If the CData of each scatter plot is an RGB triplet, then assign the MarkerEdgeColor or MarkerFaceColor of each scatter plot to the value of the CData:
s1 = scatter(1:10,1:10);
hold on
s2 = scatter(2:11,1:10);
s1.MarkerEdgeColor = s1.CData;
s2.MarkerEdgeColor = s2.CData;
legend('show');
Assign an RGB triplet to the MarkerEdgeColor or MarkerFaceColor of each scatter plot:
s1 = scatter(1:10,1:10);
hold on
s2 = scatter(2:11,1:10);
s1.MarkerEdgeColor = [0 0.4470 0.7410];
s2.MarkerEdgeColor = [0.8500 0.3250 0.0980];
legend('show');
Use this workaround when all the points within each scatter plot are the same color.
Call the legend function with two or more output arguments:
s1 = scatter(1:10,1:10,[],1:10);
hold on
s2 = scatter(2:11,1:10,[],26:35);
[h, ~] = legend('show');
Use this workaround when the points within each scatter plot are different colors.

Plots without the 'plot()' function

I am trying to learn how the graphic objects work in MATLAB. I tried to create a plot without using the plot function but I am confused why it is not working.
AFIK, when I use the plot function it creates figure, axis, line objects and then sets the property of each object accordingly. I tried to do so but all I'm getting is a white/blank figure.
I'm trying to plot a sine wave so my X and Y data are:
x = 0:0.1:2*pi;
y = sin(x);
This is my main code:
figH = figure();
axis([-2, 2, -2, 2]);
lineH = findobj(figH, 'type', 'line');
set(lineH, 'XData', x,...
'YData', y,...
'Color', 'r');
The weird thing is that when I type
get(lineH)
I'm not getting anything back. I appreciate tips and comments.
You need to create the line before you can find it and change a property.
e.g.
hLine = line ( x, y, .... );
% Then you can modify the properties, i.e.
set ( hLine, 'XData', x );
% etc...
Edit:
Its a good idea to create and store each of your objects directly (rather than allowing the command to find the appropriate figure, axes etc....)
hFig = figure;
hAx = axes ( 'parent', hFig );
hLine = line ( hAx, x, y, .... );