One legend for a group of subtightplots

조회 수: 3 (최근 30일)
Sim
Sim 2024년 4월 23일
댓글: Adam Danz 2024년 4월 25일
In the following plot, formed by 4 subtightplots, how can I have just one (outside) legend for the left plots, and just one right (outside) legend for the right plots?
addpath('/.../subtightplot');
subplot = @(m,n,p) subtightplot (m, n, p, [0.03 0], [0.04 0.03], [0.06 0.06]);
figure('position',[700 100 1200 1200],'Color',[1 1 1]);
hold on
for i = 1 : 4
subplot(2,2,i);
if i == 1 || i == 3 %%% left plots
hold on
t = 0:1:10;
plot(t,20.*cos(t),'^--','LineWidth',1,'Color','b')
plot(1:10,(1:10).^2,'^--','LineWidth',1,'Color','k')
plot(1:10,(1:10).^2.3,'^--','LineWidth',1,'Color','g')
hold off
else %%% right plots
hold on
plot(1:10,randi([-50,50],10,1),'o-','LineWidth',1,'Color','r')
plot(1:10,randi([-50,50],10,1),'o-','LineWidth',1,'Color','m')
plot(1:10,randi([-50,50],10,1),'o-','LineWidth',1,'Color','y')
hold off
end
legend('Box','off');
end
hold off
This is the my desired result:

채택된 답변

Adam Danz
Adam Danz 2024년 4월 23일
편집: Adam Danz 2024년 4월 23일
That FEX function subtightplot is very useful but since R2019b you can use tiledlayout instead and since R2020b, tiledlayout supports global legend placement.
Here's a demo.
fig = figure();
tcl = tiledlayout(fig,2,2,...
'TileSpacing','none',... % spacing between tiles
'Padding','compact', ... % spacing round perimeter
'TileIndexing', 'columnmajor'); % index by columns
ax1 = nexttile(tcl); % generate the next tile/axes
hLeft = plot(rand(20,3)+[0 2 4]); % return line handles
ax1.ColorOrder = [0 0 1; 0 0 0; 0 1 0];
ax1.LineStyleOrder = '--^';
ax2 = nexttile(tcl);
plot(rand(20,3)+[0 2 4]);
ax2.ColorOrder = ax1.ColorOrder;
ax2.LineStyleOrder = ax1.LineStyleOrder;
ax3 = nexttile(tcl);
hRight = plot(rand(20,3).*[1 1.2 1.5]);
ax3.ColorOrder = spring(3);
ax3.LineStyleOrder = '-o';
ax4 = nexttile(tcl);
plot(rand(20,3).*[1 1.2 1.5]);
ax4.ColorOrder = ax3.ColorOrder;
ax4.LineStyleOrder = ax3.LineStyleOrder;
linkaxes([ax1,ax2]) % link left axes limits
linkaxes([ax3,ax4]) % link right axes limits
legLeft = legend(hLeft); % create left legend
legLeft.Layout.Tile = 'west'; % put legend in left margin
legRight = legend(hRight); % create right legend
legRight.Layout.Tile = 'east'; % put legend in right margin
  댓글 수: 5
Adam Danz
Adam Danz 2024년 4월 23일
편집: Adam Danz 2024년 4월 23일
Well done! It looks like the data in the upper two axes are identical to the data in the lower two axes. If that's the case, just plot the data on the upper axes and use copyobj to copy the graphics objects to the lower axes.
I refactored your code to demonstrate this suggestion but I did not test it. Please take some time to understand what each line is doing.
% It looks like the data in the upper two axes are identical to the data in the lower two axes. If that's the case, just plot the data on the upper axes and use copyobj to copy the graphics objects to the lower axes.
fig = figure('position',[700 100 1200 1200],'Color',[1 1 1]);
tcl = tiledlayout(fig,2,2,...
'TileSpacing','none',...
'Padding','compact', ...
'TileIndexing', 'columnmajor');
ax = gobjects(1,4);
% Plot upper left axes
ax(1) = nexttile(tcl,1);
hold on
t = 0:1:10;
hLeft(1) = plot(t,20.*cos(t),'LineWidth',1,'Color','b');
hLeft(2) = plot(1:10,(1:10).^2,'LineWidth',1,'Color','k');
hLeft(3) = plot(1:10,(1:10).^2.3,'LineWidth',1,'Color','g');
hold off
% Plot upper right axes
ax(3) = nexttile(tcl,3);
hold on
hRight(1) = plot(1:10,randi([-50,50],10,1),'LineWidth',1,'Color','r');
hRight(2) = plot(1:10,randi([-50,50],10,1),'LineWidth',1,'Color','m');
hRight(3) = plot(1:10,randi([-50,50],10,1),'LineWidth',1,'Color','y');
hold off
% Copy content from upper left axes to lower left axes
ax(2) = nexttile(tcl,2);
copyobj(ax(1).Children,ax(2))
% Copy content from upper right axes to lower right axes
ax(4) = nexttile(tcl,4);
copyobj(ax(3).Children,ax(4))
% set axes properties for left axes
set(ax([1,2]), 'LineStyleOrder', '--^')
% set axes properties for right axes
set(ax([3,4]), 'LineStyleOrder', 'o-')
linkaxes(ax(1:2)) % link left axes limits
linkaxes(ax(3:4)) % link right axes limits
legLeft = legend(hLeft); % create left legend
legLeft.Layout.Tile = 'west'; % put legend in left margin
legRight = legend(hRight); % create right legend
legRight.Layout.Tile = 'east'; % put legend in right margin
Sim
Sim 2024년 4월 25일
Thanks a lot for your additional explanations and inputs :-)
About the "It looks like the data in the upper two axes are identical to the data in the lower two axes. If that's the case", well, in the real case, all the plots have different numbers, and I brought this example as it is, just to make things easier... :-)

댓글을 달려면 로그인하십시오.

추가 답변 (1개)

Sim
Sim 2024년 4월 25일
편집: Sim 2024년 4월 25일
I found a way to add "side" legends still using subtightplot (not for grouped plots, but for single plots, that can occur more than once in the same large panel), instead of using tiledlayout. And I compared both codes here following. I have the feeling that subtightplot is still more flexible about the fine and manual adjustments of the spaces (around plots and between plots), if compared to tiledlayout (but this is just an opinion).
% tiledlayout
figure()
tcl = tiledlayout(2,3,...
'TileSpacing','none',...
'Padding','compact');
[X,Y,Z] = peaks(20);
ax = gobjects(1,6);
for i = 1:6
ax(i) = nexttile(tcl);
if i == 1 % Tile 1
hold on
for j = 1 : 3
hRight(j) = plot(1:10,randperm(10),'o--');
end
hold off
elseif i == 2 % Tile 2
contour(X,Y,Z)
elseif i == 3 % Tile 3
imagesc(Z);
elseif i == 4 % Tile 4
surf(X,Y,Z);
elseif i == 5 % Tile 5
plot3(X,Y,Z)
elseif i == 6 % Tile 6
hold on
for j = 1 : 3
hRight2(j) = plot(1:10,randperm(10));
end
hold off
end
end
% Legend
legLeft = legend(hRight); % create left legend
legLeft.Layout.Tile = 'east'; % put legend in left margin
legRight = legend(hRight2(1:3)); % create right legend
legRight.Layout.Tile = 'east'; % put legend in right margin
% subtightplot
figure()
addpath('/subtightplot');
subplot = @(m,n,p) subtightplot (m, n, p, [0.01 0], [0.01 0.01], [0.01 0.13]);
for i = 1 : 6
subplot(2,3,i);
if i == 1 % Tile 1
hold on
for j = 1 : 3
hRight(j) = plot(1:10,randperm(10),'o--');
end
hold off
elseif i == 2 % Tile 2
contour(X,Y,Z)
elseif i == 3 % Tile 3
imagesc(Z);
elseif i == 4 % Tile 4
surf(X,Y,Z);
elseif i == 5 % Tile 5
plot3(X,Y,Z)
elseif i == 6 % Tile 6
hold on
for j = 1 : 3
hRight2(j) = plot(1:10,randperm(10));
end
hold off
end
end
% Legend
h = legend(hRight);
pos = get(h,'Position');
posx = 0.88;
posy = 0.7;
set(h,'Position',[posx posy pos(3) pos(4)]);
h2 = legend(hRight2);
pos = get(h2,'Position');
posx = 0.88;
posy = 0.2;
set(h2,'Position',[posx posy pos(3) pos(4)]);
  댓글 수: 1
Adam Danz
Adam Danz 2024년 4월 25일
Great comparison, @Sim! Thanks for sharing. You're right that subtightplot has greater flexibiliy around spacing. In tiledlayout you can set the TileSpacing and Padding properties to a discrete list of enumerators but subtightplot lets you specify the gap and margins numerically.

댓글을 달려면 로그인하십시오.

카테고리

Help CenterFile Exchange에서 Specifying Target for Graphics Output에 대해 자세히 알아보기

Community Treasure Hunt

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

Start Hunting!

Translated by