Correctly aligning labels for subgroups within a tiledlayout - matlab

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).

Related

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 create three Y-axis in one graph? [duplicate]

I have 4 sets of values: y1, y2, y3, y4 and one set x. The y values are of different ranges, and I need to plot them as separate curves with separate sets of values on the y-axis.
To put it simple, I need 3 y-axes with different values (scales) for plotting on the same figure.
Any help appreciated, or tips on where to look.
This is a great chance to introduce you to the File Exchange. Though the organization of late has suffered from some very unfortunately interface design choices, it is still a great resource for pre-packaged solutions to common problems. Though many here have given you the gory details of how to achieve this (#prm!), I had a similar need a few years ago and found that addaxis worked very well. (It was a File Exchange pick of the week at one point!) It has inspired later, probably better mods. Here is some example output:
(source: mathworks.com)
I just searched for "plotyy" at File Exchange.
Though understanding what's going on in important, sometimes you just need to get things done, not do them yourself. Matlab Central is great for that.
One possibility you can try is to create 3 axes stacked one on top of the other with the 'Color' properties of the top two set to 'none' so that all the plots are visible. You would have to adjust the axes width, position, and x-axis limits so that the 3 y axes are side-by-side instead of on top of one another. You would also want to remove the x-axis tick marks and labels from 2 of the axes since they will lie on top of one another.
Here's a general implementation that computes the proper positions for the axes and offsets for the x-axis limits to keep the plots lined up properly:
%# Some sample data:
x = 0:20;
N = numel(x);
y1 = rand(1,N);
y2 = 5.*rand(1,N)+5;
y3 = 50.*rand(1,N)-50;
%# Some initial computations:
axesPosition = [110 40 200 200]; %# Axes position, in pixels
yWidth = 30; %# y axes spacing, in pixels
xLimit = [min(x) max(x)]; %# Range of x values
xOffset = -yWidth*diff(xLimit)/axesPosition(3);
%# Create the figure and axes:
figure('Units','pixels','Position',[200 200 330 260]);
h1 = axes('Units','pixels','Position',axesPosition,...
'Color','w','XColor','k','YColor','r',...
'XLim',xLimit,'YLim',[0 1],'NextPlot','add');
h2 = axes('Units','pixels','Position',axesPosition+yWidth.*[-1 0 1 0],...
'Color','none','XColor','k','YColor','m',...
'XLim',xLimit+[xOffset 0],'YLim',[0 10],...
'XTick',[],'XTickLabel',[],'NextPlot','add');
h3 = axes('Units','pixels','Position',axesPosition+yWidth.*[-2 0 2 0],...
'Color','none','XColor','k','YColor','b',...
'XLim',xLimit+[2*xOffset 0],'YLim',[-50 50],...
'XTick',[],'XTickLabel',[],'NextPlot','add');
xlabel(h1,'time');
ylabel(h3,'values');
%# Plot the data:
plot(h1,x,y1,'r');
plot(h2,x,y2,'m');
plot(h3,x,y3,'b');
and here's the resulting figure:
I know of plotyy that allows you to have two y-axes, but no "plotyyy"!
Perhaps you can normalize the y values to have the same scale (min/max normalization, zscore standardization, etc..), then you can just easily plot them using normal plot, hold sequence.
Here's an example:
%# random data
x=1:20;
y = [randn(20,1)*1 + 0 , randn(20,1)*5 + 10 , randn(20,1)*0.3 + 50];
%# plotyy
plotyy(x,y(:,1), x,y(:,3))
%# orginial
figure
subplot(221), plot(x,y(:,1), x,y(:,2), x,y(:,3))
title('original'), legend({'y1' 'y2' 'y3'})
%# normalize: (y-min)/(max-min) ==> [0,1]
yy = bsxfun(#times, bsxfun(#minus,y,min(y)), 1./range(y));
subplot(222), plot(x,yy(:,1), x,yy(:,2), x,yy(:,3))
title('minmax')
%# standarize: (y - mean) / std ==> N(0,1)
yy = zscore(y);
subplot(223), plot(x,yy(:,1), x,yy(:,2), x,yy(:,3))
title('zscore')
%# softmax normalization with logistic sigmoid ==> [0,1]
yy = 1 ./ ( 1 + exp( -zscore(y) ) );
subplot(224), plot(x,yy(:,1), x,yy(:,2), x,yy(:,3))
title('softmax')
Multi-scale plots are rare to find beyond two axes... Luckily in Matlab it is possible, but you have to fully overlap axes and play with tickmarks so as not to hide info.
Below is a nice working sample. I hope this is what you are looking for (although colors could be much nicer)!
close all
clear all
display('Generating data');
x = 0:10;
y1 = rand(1,11);
y2 = 10.*rand(1,11);
y3 = 100.*rand(1,11);
y4 = 100.*rand(1,11);
display('Plotting');
figure;
ax1 = gca;
get(ax1,'Position')
set(ax1,'XColor','k',...
'YColor','b',...
'YLim',[0,1],...
'YTick',[0, 0.2, 0.4, 0.6, 0.8, 1.0]);
line(x, y1, 'Color', 'b', 'LineStyle', '-', 'Marker', '.', 'Parent', ax1)
ax2 = axes('Position',get(ax1,'Position'),...
'XAxisLocation','bottom',...
'YAxisLocation','left',...
'Color','none',...
'XColor','k',...
'YColor','r',...
'YLim',[0,10],...
'YTick',[1, 3, 5, 7, 9],...
'XTick',[],'XTickLabel',[]);
line(x, y2, 'Color', 'r', 'LineStyle', '-', 'Marker', '.', 'Parent', ax2)
ax3 = axes('Position',get(ax1,'Position'),...
'XAxisLocation','bottom',...
'YAxisLocation','right',...
'Color','none',...
'XColor','k',...
'YColor','g',...
'YLim',[0,100],...
'YTick',[0, 20, 40, 60, 80, 100],...
'XTick',[],'XTickLabel',[]);
line(x, y3, 'Color', 'g', 'LineStyle', '-', 'Marker', '.', 'Parent', ax3)
ax4 = axes('Position',get(ax1,'Position'),...
'XAxisLocation','bottom',...
'YAxisLocation','right',...
'Color','none',...
'XColor','k',...
'YColor','c',...
'YLim',[0,100],...
'YTick',[10, 30, 50, 70, 90],...
'XTick',[],'XTickLabel',[]);
line(x, y4, 'Color', 'c', 'LineStyle', '-', 'Marker', '.', 'Parent', ax4)
(source: pablorodriguez.info)
PLOTYY allows two different y-axes. Or you might look into LayerPlot from the File Exchange. I guess I should ask if you've considered using HOLD or just rescaling the data and using regular old plot?
OLD, not what the OP was looking for:
SUBPLOT allows you to break a figure window into multiple axes. Then if you want to have only one x-axis showing, or some other customization, you can manipulate each axis independently.
In your case there are 3 extra y axis (4 in total) and the best code that could be used to achieve what you want and deal with other cases is illustrated above:
clear
clc
x = linspace(0,1,10);
N = numel(x);
y = rand(1,N);
y_extra_1 = 5.*rand(1,N)+5;
y_extra_2 = 50.*rand(1,N)+20;
Y = [y;y_extra_1;y_extra_2];
xLimit = [min(x) max(x)];
xWidth = xLimit(2)-xLimit(1);
numberOfExtraPlots = 2;
a = 0.05;
N_ = numberOfExtraPlots+1;
for i=1:N_
L=1-(numberOfExtraPlots*a)-0.2;
axesPosition = [(0.1+(numberOfExtraPlots*a)) 0.1 L 0.8];
if(i==1)
color = [rand(1),rand(1),rand(1)];
figure('Units','pixels','Position',[200 200 1200 600])
axes('Units','normalized','Position',axesPosition,...
'Color','w','XColor','k','YColor',color,...
'XLim',xLimit,'YLim',[min(Y(i,:)) max(Y(i,:))],...
'NextPlot','add');
plot(x,Y(i,:),'Color',color);
xlabel('Time (s)');
ylab = strcat('Values of dataset 0',num2str(i));
ylabel(ylab)
numberOfExtraPlots = numberOfExtraPlots - 1;
else
color = [rand(1),rand(1),rand(1)];
axes('Units','normalized','Position',axesPosition,...
'Color','none','XColor','k','YColor',color,...
'XLim',xLimit,'YLim',[min(Y(i,:)) max(Y(i,:))],...
'XTick',[],'XTickLabel',[],'NextPlot','add');
V = (xWidth*a*(i-1))/L;
b=xLimit+[V 0];
x_=linspace(b(1),b(2),10);
plot(x_,Y(i,:),'Color',color);
ylab = strcat('Values of dataset 0',num2str(i));
ylabel(ylab)
numberOfExtraPlots = numberOfExtraPlots - 1;
end
end
The code above will produce something like this:

Multiple x-axis and y-axis with plots in MATLAB

I am trying to follow MATLAB's documentation here Graph with Multiple x-axes and y-axes to plot with 2 x and y-axes, but instead with plots rather than lines. This is what I have so far:
clear all; close all; clc;
% Arbitrary x's and y's
x1 = [10 20 30 40];
y1 = [1 2 3 4];
x2 = [100 200 300 400];
y2 = [105 95 85 75];
figure
plot(x1,y1,'Color','r')
ax1 = gca; % current axes
ax1.XColor = 'r';
ax1.YColor = 'r';
ax1_pos = ax1.Position; % position of first axes
ax2 = axes('Position',ax1_pos,...
'XAxisLocation','top',...
'YAxisLocation','right',...
'Color','none');
%line(x2,y2,'Parent',ax2,'Color','k') <--- This line works
plot(ax2, x2, y2) <--- This line doesn't work
I've looked the plot documentation 2-D line plot but cannot seem to get plot(ax,__) to help/do what I expect.
The figure ends up not plotting the second plot and the axes end up overlapping.
Any suggestions how to fix this and get 2 axes plotting to work?
I'm currently using MATLAB R2014b.
Finally figured this one out after trying to think about MATLAB's hierarchy of setting things.
The plot seems to reset the axis ax2 properties so setting them before plot doesn't make a difference. line doesn't do this it seems. So to get this to work with plots I did the following:
clear all; close all; clc;
% Arbitrary x's and y's
x1 = [10 20 30 40];
y1 = [1 2 3 4];
x2 = [100 200 300 400];
y2 = [105 95 85 75];
figure
plot(x1,y1,'o', 'MarkerEdgeColor', 'r', 'MarkerFaceColor', 'r')
ax2 = axes('Color','none'); % Create secondary axis
plot(ax2, x2,y2,'o', 'MarkerEdgeColor', 'b', 'MarkerFaceColor', 'b')
% Now set the secondary axis attributes
ax2.Color = 'none'; % Make the chart area transparent
ax2.XAxisLocation = 'top'; % Move the secondary x axis to the top
ax2.YAxisLocation = 'right'; % Move the secondary y axis to the right

non-homogenous grouped data in MATLAB plotyy()

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.

how to add markers at limits of fplot matlab?

I want to add markers at the limits of a function in matlab. I am plotting the function using fplot this is what I tried:
user_func = '2*x-3';
user_limits = '-2,2';
user_limits = regexp(user_limits, '\,', 'split');
user_limit(1) = str2num(user_limits{1});
user_limit(2) = str2num(user_limits{2});
h = fplot(func,limits);
I am trying to add markers at the limits only (size 10 color 'r'). any idea on how to do that?
thank you
Not sure if this is exactly what you are trying to accomplish but I modified your code slightly so I could plot the function (using an anonymous function):
user_func = #(x) 2*x-3;
user_limits = '-2,2';
user_limits = regexp(user_limits, '\,', 'split')
user_limit(1) = str2num(user_limits{1})
user_limit(2) = str2num(user_limits{2})
figure;fplot(user_func,[user_limit(1) user_limit(2)]);
Next, set the ticks at your locations and change the font size to 10 pt:
set(gca,'XTick',[user_limit(1) user_limit(2)],'FontSize',10);
Change the color of your labels to red:
set(gca, 'XColor', [1 0 0]);
set(gca, 'YColor', [1 0 0]);
Just so you can see the ticks, stretch the x-range a bit:
axis([-2.1 2.1 0 1]); axis 'auto y'
EDIT: After some additional input from the OP, the red tick markers can be plotted as shown below.
First let the x-position at the first limit be given by:
x1 = user_limit(1);
The y-value for first marker is then obtained from the anonymous function like this:
y1 = user_func(x1);
y2 = y1;
We have, y2 = y1, since you want the y-value where where your function first crosses the x-axis to be the same. Now make your plot like this (with x2 = user_limit(2)):
hold on;
plot(x1, y1, 'ro', x2, y2,'ro');
hold off;
giving a plot like: