Cannot position third y-axis on the right

17 views (last 30 days)
Alexis
Alexis on 31 Mar 2025
Edited: dpb on 1 Apr 2025
Hello, I wanted to plot a graph with 2 y-axis on the right side and further out so it doesn't overlap with the previous right y-axis. I used these codes, but the second y-axis (ax2) kept positioning on the left no matter how. Can anyone help me with it?
Months = string({'Jan','Feb','Mar','Apr','May','Jun',...
'Jul','Aug','Sep','Oct','Nov','Dec'});
Mtn_num = 1:12;
Elec_ABC = [1765.151 1939.867 2576.350 2253.700 2247.799 2495.007...
2583.614 2580.019 2609.666 2594.936 1768.655 1979.734];
Temp_ABC = [14.7 16.45 19.41 22.01 24.99 27.56 27.9 27.89 26.86 24.22 20.96 15.92];
GHI_ABC = [87.2 99.9 144.4 134.7 142.1 165.5 168.8 162.8 152.1 137.2 89.3 94.6];
% Plot on the left and right y axes
figure;
ax1 = axes;
yyaxis left;
bar(Elec_ABC,0.6,'FaceColor','#ffa07a');
ylabel('Electricity production / MWh');
ax1.XTickMode = 'manual';
ax1.XTickLabel = Months;
ax1.YLim = [min(ax1.YTick), max(ax1.YTick)];
ax1.YColor = 'k';
grid(ax1,'on');
yyaxis right;
plot(Mtn_num, GHI_ABC, '-x', 'Color', '#507d2a', 'LineWidth', 1.5, 'MarkerSize', 8, 'MarkerFaceColor', '#507d2a');
ylabel('Global Horizontal Irradiation / kWh/m²');
ax1.YColor = '#507d2a';
ax1.YLim = [0 200];
ytick = ax1.YTick;
% create 2nd, transparent axes
ax2 = axes('Position', ax1.Position, 'Color', 'none');
ax2.YAxisLocation = 'right';
plot(ax2, Mtn_num, Temp_ABC, '-o', 'Color', '#191970', 'LineWidth', 1.5, 'MarkerSize', 4, 'MarkerFaceColor', '#191970');
ylabel(ax2, 'Temperature / °C');
ax2.Color = 'none';
ax2.YColor = '#191970';
ax2.XColor = 'none';
hold(ax2, 'on');
ax2.XLim = ax1.XLim;
ax2.YLimMode = 'manual';
ax2.YLim = [0 30];
yl = ax2.YLim;
ax2.YTick = linspace(yl(1), yl(2), length(ytick));
ax2.YTickMode = 'manual';
ax2.YTick = 0:5:30;
% horzontally offset y tick labels
ax2.YTickLabel = strcat({' '}, ax2.YTickLabel);
title('Monthly enery production at ABC Reservoir against GHI and Temperature')
  1 Comment
dpb
dpb on 31 Mar 2025
Edited: dpb on 31 Mar 2025
There are a number of File Exchange submittals that will make this job easier...
Each approaches is somewhat differently, but save you the trouble of the additional axis creation and resizing the previous.
ADDENDUM
Although I wasn't thinking about the bar, though; not aware of any that handle that...although could probably fake one to do so.

Sign in to comment.

Accepted Answer

dpb
dpb on 31 Mar 2025
Edited: dpb on 1 Apr 2025
Just a slightly amended version; I investigated the use of some of the FEX multi-axis submittals to see if might be any simpler. All of them I looked at were actually more trouble to fiddle with because of the bar() than this turned out to be, but I had converted to illustrate the use of categorical in that as well so just ended up looking at the nits of this version a little more.
This one retains spreading the title over the entire window instead of being centered over the smaller, reduced axes. It fiddles with the vertical position and reduces the font size to make fit in the default window size without clipping. Those adjustments can be tweaked for the specific target...
Months=datetime(year(now),[1:12],1,'Format','MMM');
Months=categorical(Months,Months,'ordinal',1);
Elec_ABC = [1765.151 1939.867 2576.350 2253.700 2247.799 2495.007...
2583.614 2580.019 2609.666 2594.936 1768.655 1979.734];
Temp_ABC = [14.7 16.45 19.41 22.01 24.99 27.56 27.9 27.89 26.86 24.22 20.96 15.92];
GHI_ABC = [87.2 99.9 144.4 134.7 142.1 165.5 168.8 162.8 152.1 137.2 89.3 94.6];
% Plot on the left and right y axes
figure;
ax1 = axes;
yyaxis left;
% make axis labels a little smaller for more room for extra axes
%disp([ax1.XAxis.FontSize ax1.YAxis.FontSize])
set([ax1.XAxis;ax1.YAxis],{'FontSize'},{[9]});
%disp([ax1.XAxis.FontSize ax1.YAxis.FontSize])
hB=bar(Months,Elec_ABC,0.6,'FaceColor','#ffa07a');
ylabel('Electricity production / MWh');
ax1.YLim = [min(ax1.YTick), max(ax1.YTick)];
ax1.YColor = 'k';
grid(ax1,'on');
yyaxis right;
plot(Months, GHI_ABC,'-x','Color','#507d2a','LineWidth',1.5,'MarkerSize',8,'MarkerFaceColor','#507d2a');
ylabel('Global Horizontal Irradiation / kWh/m²');
ax1.YColor = '#507d2a';
ax1.YLim = [0 200];
ytick = ax1.YTick;
% create 2nd, transparent axes
% modify to leave room for displaying the axis labels/ticks inside figure boundaries
WIDTH_FACTOR=0.90; % width reduction factor...salt to suit
posn0=ax1.Position; % retrieve current position
posn=posn0; % working copy
posn(3)=posn(3)*WIDTH_FACTOR; % reduce width onlyax1.Position=posn;
ax1.Position=posn; % shrink the present axes
ax2=axes('Position', posn, 'Color', 'none'); % create at reduced width
ax2.YAxisLocation = 'right';
% place hold on here per BK's observation
hold(ax2,'on')
set([ax2.XAxis;ax2.YAxis],{'FontSize'},{[9]});
plot(ax2,Months,Temp_ABC,'-o','Color','#191970','LineWidth',1.5,'MarkerSize', 4,'MarkerFaceColor','#191970');
ylabel(ax2, 'Temperature / °C');
ax2.Color = 'none';
ax2.YColor = '#191970';
ax2.XColor = 'none';
ax2.XLim = ax1.XLim;
ax2.YLimMode = 'manual';
ax2.YLim = [0 30];
yl = ax2.YLim;
ax2.YTick = 0:5:30;
% horzontally offset y tick labels
LABEL_POS_ADJ=1.13; % adjustment for label
LABEL_FIELD_WIDTH=17; % adjustment for label tick
ax2.YAxis.Label.Position=ax2.YAxis.Label.Position.*[LABEL_POS_ADJ 1 1];
ax2.YAxis.TickLabelFormat=sprintf('%%%dg',LABEL_FIELD_WIDTH);
% now the final piece de resistance -- center the big title over whole thing
ax3=axes('Position',posn0,'Color','none','visible','off'); % overall original size
hold(ax3,'on')
set([ax3.XAxis;ax3.YAxis],{'Visible'},{'off'}) % turn x,y axes off
ax3.Visible='on'; % and turn on so title shows
hT=title(ax3,'Monthly energy production at ABC Reservoir versus GHI and Temperature','FontSize',9.5);
hT.Position=hT.Position.*[1 1.01 1];
The default location of the title is on top of the axes upper box probably because the 'hold on' was set before the title was drawn so the inner size wasn't reduced to account for there being a title. I didn't try to go back to that point although it's possible that might solve the problem although when the axes are shrunk to provide the room for the RH labels, it will then be referred to those axes so it would only be a temporary fix just to see if the height is adjusted.

More Answers (3)

Benjamin Kraus
Benjamin Kraus on 31 Mar 2025
Here is another approach for adding a third y-axis that leverages tiledlayout.
tcl = tiledlayout(1,1);
ax1 = axes(tcl);
yyaxis(ax1,'left');
ylabel(ax1, 'Ruler 1');
yyaxis(ax1,'right');
ylabel(ax1, 'Ruler 2');
% Create a second axes just to draw the ruler.
ax2 = axes(tcl);
ax2.Layout.Tile = 'east';
ax2.PlotBoxAspectRatio = [eps 1 1]; % Make the axes really narrow.
ax2.YAxisLocation = 'right';
ylabel(ax2, 'Ruler 3')
Here is your original script, modified to use this approach:
Months = string({'Jan','Feb','Mar','Apr','May','Jun',...
'Jul','Aug','Sep','Oct','Nov',"Dec"});
Mtn_num = 1:12;
Elec_ABC = [1765.151 1939.867 2576.350 2253.700 2247.799 2495.007 ...
2583.614 2580.019 2609.666 2594.936 1768.655 1979.734];
Temp_ABC = [14.7 16.45 19.41 22.01 24.99 27.56 27.9 27.89 26.86 24.22 20.96 15.92];
GHI_ABC = [87.2 99.9 144.4 134.7 142.1 165.5 168.8 162.8 152.1 137.2 89.3 94.6];
% Plot on the left and right y axes
f = figure;
tcl = tiledlayout(1,1);
ax1 = axes(tcl);
yyaxis left
bar(Elec_ABC,0.6,'FaceColor','#ffa07a');
ylabel('Electricity production / MWh');
ax1.XTickMode = 'manual';
ax1.XTick = Mtn_num;
ax1.XTickLabel = Months;
ax1.YLim = [min(ax1.YTick), max(ax1.YTick)];
ax1.YColor = 'k';
grid(ax1,'on');
yyaxis right
plot(Mtn_num, GHI_ABC, '-x', 'Color', '#507d2a', 'LineWidth', 1.5, 'MarkerSize', 8, 'MarkerFaceColor', '#507d2a');
ylabel('Global Horizontal Irradiation / kWh/m²');
ax1.YColor = '#507d2a';
ax1.YLim = [0 200];
ytick = ax1.YTick;
title(tcl,'Monthly enery production at ABC Reservoir against GHI and Temperature','FontSize',9)
% create 2nd, transparent axes
ax2 = axes(tcl);
plot(ax2, Mtn_num, Temp_ABC, '-o', 'Color', '#191970', 'LineWidth', 1.5, 'MarkerSize', 4, 'MarkerFaceColor', '#191970');
ax2.Visible = 'off';
hold(ax2, 'on');
ax2.XLim = ax1.XLim;
ax2.YLimMode = 'manual';
ax2.YLim = [0 30];
yl = ax2.YLim;
% Create a 3rd axes that has zero width, just to draw the Y-Ticks.
ax3 = axes(tcl);
ax3.Color = 'none';
ax3.YColor = '#191970';
ax3.XColor = 'none';
ax3.YAxisLocation = 'right';
ax3.Layout.Tile = 'east';
ax3.PlotBoxAspectRatio = [eps 1 1];
ax3.YLim = yl;
ax3.YTick = linspace(yl(1), yl(2), length(ytick));
ax3.YTickMode = 'manual';
ylabel(ax3, 'Temperature / °C');
  5 Comments
Benjamin Kraus
Benjamin Kraus on 1 Apr 2025
We have sgtitle, which was meant to be the "suptitle" replacement that is built-in to core MATLAB, but using sgtitle with tiledlayout is equivalent to calling title on tiledlayout. I think what is missing here is a setting for the tiledlayout to include the decorations when automatically computing the title alignment. I'll capture this request in our system.
dpb
dpb on 1 Apr 2025
"We have sgtitle, which was meant to be the "suptitle" replacement ..."
I knew there was one, but I could not for the life of me find/remember what is was/is named...and at least in R2021b that have here, it's not in the 'See Also' list...

Sign in to comment.


Benjamin Kraus
Benjamin Kraus on 31 Mar 2025
Edited: Benjamin Kraus on 31 Mar 2025
The issue you are running into is that calling plot resets your YAxisLocation.
ax = axes;
ax.YAxisLocation = 'right';
plot(ax,1:10)
ax.YAxisLocation % left
You either need to:
  • Turn on hold before you call plot or,
  • Set YAxisLocation after calling plot.
ax = axes;
plot(ax,1:10)
ax.YAxisLocation = 'right';
ax.YAxisLocation % right
or
ax = axes;
ax.YAxisLocation = 'right';
hold(ax,'on')
plot(ax,1:10)
ax.YAxisLocation % right

dpb
dpb on 31 Mar 2025
Edited: dpb on 31 Mar 2025
Some minor readjustmenst to provide room inside the default figure...
Can adjust just factors I picked for the reduction in width and then paired with that the adjust of the position of the second axes label and ticklabels. This use properties of the label text position and the format string for the tick labels instead of adjusting the string itself...same effect, alternate way to produce the same effect since, unfortumately for this exercise, can't set the postion ot the ticklabels.
I'm not sure why the title appears to be on top of the axes...I didn't pursue that detail. One further refininement that would help the title this way would be to create yet another axis of the default original size (that is, without the width reduction) and write the title to it...that would make the entire figure/axis width available and center it "more better".
Months = string({'Jan','Feb','Mar','Apr','May','Jun',...
'Jul','Aug','Sep','Oct','Nov','Dec'});
Mtn_num = 1:12;
Elec_ABC = [1765.151 1939.867 2576.350 2253.700 2247.799 2495.007...
2583.614 2580.019 2609.666 2594.936 1768.655 1979.734];
Temp_ABC = [14.7 16.45 19.41 22.01 24.99 27.56 27.9 27.89 26.86 24.22 20.96 15.92];
GHI_ABC = [87.2 99.9 144.4 134.7 142.1 165.5 168.8 162.8 152.1 137.2 89.3 94.6];
% Plot on the left and right y axes
figure;
ax1 = axes;
yyaxis left;
bar(Elec_ABC,0.6,'FaceColor','#ffa07a');
ylabel('Electricity production / MWh');
ax1.XTickMode = 'manual';
ax1.XTickLabel = Months;
ax1.YLim = [min(ax1.YTick), max(ax1.YTick)];
ax1.YColor = 'k';
grid(ax1,'on');
yyaxis right;
plot(Mtn_num, GHI_ABC, '-x', 'Color', '#507d2a', 'LineWidth', 1.5, 'MarkerSize', 8, 'MarkerFaceColor', '#507d2a');
ylabel('Global Horizontal Irradiation / kWh/m²');
ax1.YColor = '#507d2a';
ax1.YLim = [0 200];
ytick = ax1.YTick;
% create 2nd, transparent axes
% modify to leave room for displaying the axis labels/ticks inside figure boundaries
WIDTH_FACTOR=0.85; % width reduction factor...salt to suit
posn=ax1.Position; % retrieve current position
posn(3)=posn(3)*WIDTH_FACTOR; % reduce width only
ax1.Position=posn; % shrink the presnet axes
ax2 = axes('Position', posn, 'Color', 'none'); % at reduced with
ax2.YAxisLocation = 'right';
% place hold on here per BK's observation
hold(ax2,'on')
plot(ax2, Mtn_num, Temp_ABC, '-o', 'Color', '#191970', 'LineWidth', 1.5, 'MarkerSize', 4, 'MarkerFaceColor', '#191970');
ylabel(ax2, 'Temperature / °C');
ax2.Color = 'none';
ax2.YColor = '#191970';
ax2.XColor = 'none';
ax2.XLim = ax1.XLim;
ax2.YLimMode = 'manual';
ax2.YLim = [0 30];
yl = ax2.YLim;
ax2.YTick = linspace(yl(1), yl(2), length(ytick));
ax2.YTickMode = 'manual';
ax2.YTick = 0:5:30;
% horzontally offset y tick labels
%ax2.YTickLabel = strcat({' '}, ax2.YTickLabel);
LABEL_POS_ADJ=1.15; % adjustment for label tickax2.YAxis.Label.Position=ax2.YAxis.Label.Position.*[1.15 1 1];
LABEL_FIELD_WIDTH=15; % adjustment for label tick
ax2.YAxis.Label.Position=ax2.YAxis.Label.Position.*[LABEL_POS_ADJ 1 1];
ax2.YAxis.TickLabelFormat=sprintf('%%%dg',LABEL_FIELD_WIDTH);
hT=title(ax1,'Monthly energy production at ABC Reservoir vs GHI and Temperature','fontsize',9);

Categories

Find more on Printing and Saving in Help Center and File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!