Cannot position third y-axis on the right

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 件のコメント

dpb
dpb 2025 年 3 月 31 日
編集済み: dpb 2025 年 3 月 31 日
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.

サインインしてコメントする。

 採用された回答

dpb
dpb 2025 年 3 月 31 日
編集済み: dpb 2025 年 4 月 1 日

0 投票

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.

その他の回答 (3 件)

Benjamin Kraus
Benjamin Kraus 2025 年 3 月 31 日

1 投票

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 件のコメント

dpb
dpb 2025 年 4 月 1 日
編集済み: dpb 2025 年 4 月 1 日
Much more better approach, @Benjamin Kraus! I had just come back with the thought of whether could make tiled layout work similarly...but you beat me to it! :J)
With the conversion to categorical dates to save some extra manual labelling and an attempt to center the title...
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
f = figure;
tcl = tiledlayout(1,1);
ax1 = axes(tcl);
yyaxis left
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
ax2 = axes(tcl);
plot(ax2, Months, Temp_ABC, '-o', 'Color', '#191970', 'LineWidth', 1.5, 'MarkerSize', 4, 'MarkerFaceColor', '#191970');
ax2.Visible = 'off';
hold(ax2, 'on');
ax2.XLim = ax1.XLim;
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');
title(tcl,'Monthly energy production at ABC Reservoir against GHI and Temperature','FontSize',9)
I haven't explored all the ins and outs of tiledlayout; is there a way to have the title be shared across the whole width, the purpose of my extra axes in the last iteration? From the documentation, I had expected placing title((tcl,...) after the added panel would have done so, but doesn't seem to know anything about the additional one...is that owing to the zero-width aspect ratio,maybe?
Benjamin Kraus
Benjamin Kraus 2025 年 4 月 1 日
@dpb: The title alignment is because we are using the "east" tile for the third axes. The title is aligned with the main body of the tiled layout, and the "east" tile is meant for "decorations" like the legend or colorbar.
I tried a few different options to get the title alignment to include the side axle, and couldn't get any to work the way I would have liked. The issue is that tiledlayout tries really hard to make hte "main body" of each tile equal size, so if you try to move the ruler into a main tile, it causes weird spacing issues, where the narrow axes is center in a tile.
The only solution I could find is a bit more of a hack, which is to leverage tile spanning to make the main axes span multiple tiles.
tcl = tiledlayout(1, 8);
title(tcl,'Monthly energy production at ABC Reservoir against GHI and Temperature','FontSize',9)
ax1 = nexttile(tcl,[1 7]);
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 = 8;
ax2.PlotBoxAspectRatio = [eps 1 1]; % Make the axes really narrow.
ax2.YAxisLocation = 'right';
ylabel(ax2, 'Ruler 3')
dpb
dpb 2025 年 4 月 1 日
編集済み: dpb 2025 年 4 月 1 日
Thanks for the exploration, @Benjamin Kraus; that's basically what I inferred from the behavior and your "hack" is a little cleaner than the one I ended up with, although I poked around with subplot locally and did the same thing as yours above with spanning and the narrow second/third axes. I had not thought of nor seen the 'DataAspectRatio' trick for the minimal width to show only the axes; when had done similar in the past had actually set the width, instead. This is also much cleaner/simpler...
Would not a "suptitle" feature be a good enhancement for tiledlayouts if it isn't already in the "todo" list for consideration?
Benjamin Kraus
Benjamin Kraus 2025 年 4 月 1 日
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 2025 年 4 月 1 日
"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...

サインインしてコメントする。

Benjamin Kraus
Benjamin Kraus 2025 年 3 月 31 日
編集済み: Benjamin Kraus 2025 年 3 月 31 日

0 投票

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

1 件のコメント

Alexis
Alexis 2025 年 3 月 31 日
Thank you! such a simple fix I didn't realise it.

サインインしてコメントする。

dpb
dpb 2025 年 3 月 31 日
編集済み: dpb 2025 年 3 月 31 日

0 投票

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

カテゴリ

ヘルプ センター および File ExchangePrinting and Saving についてさらに検索

質問済み:

2025 年 3 月 31 日

編集済み:

dpb
2025 年 4 月 1 日

Community Treasure Hunt

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

Start Hunting!

Translated by