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 件のコメント
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.
採用された回答
その他の回答 (3 件)
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 件のコメント
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?
@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')
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
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.
Benjamin Kraus
2025 年 3 月 31 日
編集済み: Benjamin Kraus
2025 年 3 月 31 日
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
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 Exchange で Printing and Saving についてさらに検索
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!





