Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit454f97f

Browse files
committed
ENH: Subfigures
1 parentdd15dc0 commit454f97f

File tree

21 files changed

+2219
-1642
lines changed

21 files changed

+2219
-1642
lines changed

‎doc/api/figure_api.rst

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,5 @@
55
..currentmodule::matplotlib.figure
66

77
..automodule::matplotlib.figure
8-
:no-members:
9-
:no-inherited-members:
10-
11-
Classes
12-
-------
13-
14-
..autosummary::
15-
:toctree: _as_gen/
16-
:template: autosummary.rst
17-
:nosignatures:
18-
19-
Figure
20-
SubplotParams
21-
22-
Functions
23-
---------
24-
25-
..autosummary::
26-
:toctree: _as_gen/
27-
:template: autosummary.rst
28-
:nosignatures:
29-
30-
figaspect
8+
:members:
9+
:inherited-members:
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FigureBase class added, and Figure class made a child
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
The new subfigure feature motivated some re-organization of the
5+
`.figure.Figure` class, so that the new `.figure.SubFigure` class could have
6+
all the capabilities of a figure.
7+
8+
The `.figure.Figure` class is now a subclass of `.figure.FigureBase`, where
9+
`.figure.FigureBase` contains figure-level artist addition routines, and
10+
the `.figure.Figure` subclass just contains features that are unique to the
11+
outer figure.
12+
13+
Note that there are a new *transSubfigure* transform
14+
associated with the subfigure. This transform also exists for a
15+
`.Figure` instance, and is equal to *transFigure* in that case,
16+
so code that uses the transform stack that wants to place objects on either
17+
the parent figure or one of the subfigures should use *transSubfigure*.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
New subfigure functionality
2+
---------------------------
3+
New `.figure.Figure.add_subfigure` and `.figure.Figure.subfigures`
4+
functionalities allow creating virtual figures within figures. Similar
5+
nesting was previously done with nested gridspecs
6+
( see:doc:`/gallery/subplots_axes_and_figures/gridspec_nested`). However, this
7+
did not allow localized figure artists (i.e. a colorbar or suptitle) that
8+
only pertained to each subgridspec.
9+
10+
The new methods `.figure.Figure.add_subfigure` and `.figure.Figure.subfigures`
11+
are meant to rhyme with `.figure.Figure.add_subplot` and
12+
`.figure.Figure.subplots` and have most of the same arguments.
13+
14+
See:doc:`/gallery/subplots_axes_and_figures/subfigures`.
15+
16+
..note::
17+
18+
The subfigure functionality is experimental API as of v3.4.
19+
20+
..plot::
21+
22+
def example_plot(ax, fontsize=12, hide_labels=False):
23+
pc = ax.pcolormesh(np.random.randn(30, 30))
24+
if not hide_labels:
25+
ax.set_xlabel('x-label', fontsize=fontsize)
26+
ax.set_ylabel('y-label', fontsize=fontsize)
27+
ax.set_title('Title', fontsize=fontsize)
28+
return pc
29+
30+
31+
fig = plt.figure(constrained_layout=True, figsize=(10, 4))
32+
subfigs = fig.subfigures(1, 2, wspace=0.07)
33+
34+
axsLeft = subfigs[0].subplots(1, 2, sharey=True)
35+
subfigs[0].set_facecolor('0.75')
36+
for ax in axsLeft:
37+
pc = example_plot(ax)
38+
subfigs[0].suptitle('Left plots', fontsize='x-large')
39+
subfigs[0].colorbar(pc, shrink=0.6, ax=axsLeft, location='bottom')
40+
41+
axsRight = subfigs[1].subplots(3, 1, sharex=True)
42+
for nn, ax in enumerate(axsRight):
43+
pc = example_plot(ax, hide_labels=True)
44+
if nn == 2:
45+
ax.set_xlabel('xlabel')
46+
if nn == 1:
47+
ax.set_ylabel('ylabel')
48+
subfigs[1].colorbar(pc, shrink=0.6, ax=axsRight)
49+
subfigs[1].suptitle('Right plots', fontsize='x-large')
50+
51+
fig.suptitle('Figure suptitle', fontsize='xx-large')
52+
53+
plt.show()

‎examples/subplots_axes_and_figures/gridspec_nested.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
GridSpecs can be nested, so that a subplot from a parent GridSpec can
77
set the position for a nested grid of subplots.
88
9+
Note that the same functionality can be achieved more directly with
10+
`~.figure.FigureBase.subfigures`:
11+
:doc:`nested gridspecs</gallery/subplots_axes_and_figures/subfigures>`,
12+
913
"""
1014
importmatplotlib.pyplotasplt
1115
importmatplotlib.gridspecasgridspec
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
"""
2+
=================
3+
Figure subfigures
4+
=================
5+
6+
Sometimes it is desirable to have a figure with two different layouts in it.
7+
This can be achieved with
8+
:doc:`nested gridspecs</gallery/subplots_axes_and_figures/gridspec_nested>`,
9+
but having a virtual figure with its own artists is helpful, so
10+
Matplotlib also has "subfigures", accessed by calling
11+
`matplotlib.figure.Figure.add_subfigure` in a way that is analagous to
12+
`matplotlib.figure.Figure.add_subplot`, or
13+
`matplotlib.figure.Figure.subfigures` to make an array of subfigures. Note
14+
that subfigures can also have their own child subfigures.
15+
16+
.. note::
17+
``subfigure`` is new in v3.4, and the API is still provisional.
18+
19+
"""
20+
importmatplotlib.pyplotasplt
21+
importnumpyasnp
22+
23+
24+
defexample_plot(ax,fontsize=12,hide_labels=False):
25+
pc=ax.pcolormesh(np.random.randn(30,30),vmin=-2.5,vmax=2.5)
26+
ifnothide_labels:
27+
ax.set_xlabel('x-label',fontsize=fontsize)
28+
ax.set_ylabel('y-label',fontsize=fontsize)
29+
ax.set_title('Title',fontsize=fontsize)
30+
returnpc
31+
32+
# gridspec inside gridspec
33+
fig=plt.figure(constrained_layout=True,figsize=(10,4))
34+
subfigs=fig.subfigures(1,2,wspace=0.07)
35+
36+
axsLeft=subfigs[0].subplots(1,2,sharey=True)
37+
subfigs[0].set_facecolor('0.75')
38+
foraxinaxsLeft:
39+
pc=example_plot(ax)
40+
subfigs[0].suptitle('Left plots',fontsize='x-large')
41+
subfigs[0].colorbar(pc,shrink=0.6,ax=axsLeft,location='bottom')
42+
43+
axsRight=subfigs[1].subplots(3,1,sharex=True)
44+
fornn,axinenumerate(axsRight):
45+
pc=example_plot(ax,hide_labels=True)
46+
ifnn==2:
47+
ax.set_xlabel('xlabel')
48+
ifnn==1:
49+
ax.set_ylabel('ylabel')
50+
51+
subfigs[1].set_facecolor('0.85')
52+
subfigs[1].colorbar(pc,shrink=0.6,ax=axsRight)
53+
subfigs[1].suptitle('Right plots',fontsize='x-large')
54+
55+
fig.suptitle('Figure suptitle',fontsize='xx-large')
56+
57+
plt.show()
58+
59+
##############################################################################
60+
# It is possible to mix subplots and subfigures using
61+
# `matplotlib.figure.Figure.add_subfigure`. This requires getting
62+
# the gridspec that the subplots are laid out on.
63+
64+
fig,axs=plt.subplots(2,3,constrained_layout=True,figsize=(10,4))
65+
66+
# clear the left column for the subfigure:
67+
forainaxs[:,0]:
68+
gridspec=a.get_subplotspec().get_gridspec()
69+
a.remove()
70+
71+
# plot data in remaining axes:
72+
forainaxs[:,1:].flat:
73+
a.plot(np.arange(10))
74+
75+
# make the subfigure in the empy gridspec slots:
76+
subfig=fig.add_subfigure(gridspec[:,0])
77+
78+
axsLeft=subfig.subplots(1,2,sharey=True)
79+
subfig.set_facecolor('0.75')
80+
foraxinaxsLeft:
81+
pc=example_plot(ax)
82+
subfig.suptitle('Left plots',fontsize='x-large')
83+
subfig.colorbar(pc,shrink=0.6,ax=axsLeft,location='bottom')
84+
85+
fig.suptitle('Figure suptitle',fontsize='xx-large')
86+
plt.show()
87+
88+
##############################################################################
89+
# Subfigures can have different widths and heights. This is exactly the
90+
# same example as the first example, but *width_ratios* has been changed:
91+
92+
fig=plt.figure(constrained_layout=True,figsize=(10,4))
93+
subfigs=fig.subfigures(1,2,wspace=0.07,width_ratios=[2,1])
94+
95+
axsLeft=subfigs[0].subplots(1,2,sharey=True)
96+
subfigs[0].set_facecolor('0.75')
97+
foraxinaxsLeft:
98+
pc=example_plot(ax)
99+
subfigs[0].suptitle('Left plots',fontsize='x-large')
100+
subfigs[0].colorbar(pc,shrink=0.6,ax=axsLeft,location='bottom')
101+
102+
axsRight=subfigs[1].subplots(3,1,sharex=True)
103+
fornn,axinenumerate(axsRight):
104+
pc=example_plot(ax,hide_labels=True)
105+
ifnn==2:
106+
ax.set_xlabel('xlabel')
107+
ifnn==1:
108+
ax.set_ylabel('ylabel')
109+
110+
subfigs[1].set_facecolor('0.85')
111+
subfigs[1].colorbar(pc,shrink=0.6,ax=axsRight)
112+
subfigs[1].suptitle('Right plots',fontsize='x-large')
113+
114+
fig.suptitle('Figure suptitle',fontsize='xx-large')
115+
116+
plt.show()
117+
118+
##############################################################################
119+
# Subfigures can be also be nested:
120+
121+
fig=plt.figure(constrained_layout=True,figsize=(10,8))
122+
123+
fig.suptitle('fig')
124+
125+
subfigs=fig.subfigures(1,2,wspace=0.07)
126+
127+
subfigs[0].set_facecolor('coral')
128+
subfigs[0].suptitle('subfigs[0]')
129+
130+
subfigs[1].set_facecolor('coral')
131+
subfigs[1].suptitle('subfigs[1]')
132+
133+
subfigsnest=subfigs[0].subfigures(2,1,height_ratios=[1,1.4])
134+
subfigsnest[0].suptitle('subfigsnest[0]')
135+
subfigsnest[0].set_facecolor('r')
136+
axsnest0=subfigsnest[0].subplots(1,2,sharey=True)
137+
fornn,axinenumerate(axsnest0):
138+
pc=example_plot(ax,hide_labels=True)
139+
subfigsnest[0].colorbar(pc,ax=axsnest0)
140+
141+
subfigsnest[1].suptitle('subfigsnest[1]')
142+
subfigsnest[1].set_facecolor('g')
143+
axsnest1=subfigsnest[1].subplots(3,1,sharex=True)
144+
145+
axsRight=subfigs[1].subplots(2,2)
146+
147+
plt.show()

‎examples/subplots_axes_and_figures/subplots_demo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@
144144
# Still there remains an unused empty space between the subplots.
145145
#
146146
# To precisely control the positioning of the subplots, one can explicitly
147-
# create a `.GridSpec` with `.add_gridspec`, and then call its
147+
# create a `.GridSpec` with `.Figure.add_gridspec`, and then call its
148148
# `~.GridSpecBase.subplots` method. For example, we can reduce the height
149149
# between vertical subplots using ``add_gridspec(hspace=0)``.
150150
#

‎lib/matplotlib/_constrained_layout.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,10 @@ def do_constrained_layout(fig, renderer, h_pad, w_pad,
103103
# larger/smaller). This second reposition tends to be much milder,
104104
# so doing twice makes things work OK.
105105

106-
# make margins for all the axes andsubpanels in the
106+
# make margins for all the axes andsubfigures in the
107107
# figure. Add margins for colorbars...
108108
_make_layout_margins(fig,renderer,h_pad=h_pad,w_pad=w_pad,
109109
hspace=hspace,wspace=wspace)
110-
111110
_make_margin_suptitles(fig,renderer,h_pad=h_pad,w_pad=w_pad)
112111

113112
# if a layout is such that a columns (or rows) margin has no
@@ -207,8 +206,7 @@ def _make_layout_margins(fig, renderer, *, w_pad=0, h_pad=0,
207206
hspace=hspace,wspace=wspace)
208207
panel._layoutgrid.parent.edit_outer_margin_mins(margins,ss)
209208

210-
# for ax in [a for a in fig._localaxes if hasattr(a, 'get_subplotspec')]:
211-
foraxinfig.get_axes():
209+
foraxinfig._localaxes.as_list():
212210
ifnothasattr(ax,'get_subplotspec')ornotax.get_in_layout():
213211
continue
214212

@@ -223,7 +221,6 @@ def _make_layout_margins(fig, renderer, *, w_pad=0, h_pad=0,
223221
hspace=hspace,wspace=wspace)
224222
margin0=margin.copy()
225223
pos,bbox=_get_pos_and_bbox(ax,renderer)
226-
227224
# the margin is the distance between the bounding box of the axes
228225
# and its position (plus the padding from above)
229226
margin['left']+=pos.x0-bbox.x0
@@ -281,11 +278,15 @@ def _make_margin_suptitles(fig, renderer, *, w_pad=0, h_pad=0):
281278
# top level figure margin larger.
282279
forpanelinfig.panels:
283280
_make_margin_suptitles(panel,renderer,w_pad=w_pad,h_pad=h_pad)
284-
invTransFig=fig.transPanel.inverted().transform_bbox
285-
286-
w_pad,h_pad= (fig.transPanel-fig.transFigure).transform((w_pad,h_pad))
287281

288282
iffig._suptitleisnotNoneandfig._suptitle.get_in_layout():
283+
invTransFig=fig.transSubfigure.inverted().transform_bbox
284+
parenttrans=fig.transFigure
285+
w_pad,h_pad= (fig.transSubfigure-
286+
parenttrans).transform((w_pad,1-h_pad))
287+
w_pad,one= (fig.transSubfigure-
288+
parenttrans).transform((w_pad,1))
289+
h_pad=one-h_pad
289290
bbox=invTransFig(fig._suptitle.get_tightbbox(renderer))
290291
p=fig._suptitle.get_position()
291292
fig._suptitle.set_position((p[0],1-h_pad))
@@ -429,7 +430,7 @@ def _get_pos_and_bbox(ax, renderer):
429430
fig=ax.figure
430431
pos=ax.get_position(original=True)
431432
# pos is in panel co-ords, but we need in figure for the layout
432-
pos=pos.transformed(fig.transPanel-fig.transFigure)
433+
pos=pos.transformed(fig.transSubfigure-fig.transFigure)
433434
try:
434435
tightbbox=ax.get_tightbbox(renderer=renderer,for_layout_only=True)
435436
exceptTypeError:
@@ -446,7 +447,7 @@ def _reposition_axes(fig, renderer, *, w_pad=0, h_pad=0, hspace=0, wspace=0):
446447
"""
447448
Reposition all the axes based on the new inner bounding box.
448449
"""
449-
trans_fig_to_panel=fig.transFigure-fig.transPanel
450+
trans_fig_to_panel=fig.transFigure-fig.transSubfigure
450451
forpanelinfig.panels:
451452
bbox=panel._layoutgrid.get_outer_bbox()
452453
panel._redo_transform_rel_fig(
@@ -455,9 +456,7 @@ def _reposition_axes(fig, renderer, *, w_pad=0, h_pad=0, hspace=0, wspace=0):
455456
w_pad=w_pad,h_pad=h_pad,
456457
wspace=wspace,hspace=hspace)
457458

458-
# for ax in fig._localaxes:
459-
# if not hasattr(a, 'get_subplotspec'):
460-
foraxinfig.get_axes():
459+
foraxinfig._localaxes.as_list():
461460
ifnothasattr(ax,'get_subplotspec')ornotax.get_in_layout():
462461
continue
463462

@@ -512,7 +511,7 @@ def _reposition_colorbar(cbax, renderer, *, offset=None):
512511
gs=parents[0].get_gridspec()
513512
ncols,nrows=gs.ncols,gs.nrows
514513
fig=cbax.figure
515-
trans_fig_to_panel=fig.transFigure-fig.transPanel
514+
trans_fig_to_panel=fig.transFigure-fig.transSubfigure
516515

517516
cb_rspans,cb_cspans=_get_cb_parent_spans(cbax)
518517
bboxparent=gs._layoutgrid.get_bbox_for_cb(rows=cb_rspans,cols=cb_cspans)
@@ -563,7 +562,7 @@ def _reposition_colorbar(cbax, renderer, *, offset=None):
563562
pbcb=pbcb.translated(0,dy)
564563

565564
pbcb=trans_fig_to_panel.transform_bbox(pbcb)
566-
cbax.set_transform(fig.transPanel)
565+
cbax.set_transform(fig.transSubfigure)
567566
cbax._set_position(pbcb)
568567
cbax.set_aspect(aspect,anchor=anchor,adjustable='box')
569568
returnoffset

‎lib/matplotlib/axes/_axes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ def indicate_inset(self, bounds, inset_ax=None, *, transform=None,
449449

450450
# decide which two of the lines to keep visible....
451451
pos=inset_ax.get_position()
452-
bboxins=pos.transformed(self.figure.transFigure)
452+
bboxins=pos.transformed(self.figure.transSubfigure)
453453
rectbbox=mtransforms.Bbox.from_bounds(
454454
*bounds
455455
).transformed(transform)

‎lib/matplotlib/axes/_base.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,7 @@ def set_figure(self, fig):
673673
super().set_figure(fig)
674674

675675
self.bbox=mtransforms.TransformedBbox(self._position,
676-
fig.transFigure)
676+
fig.transSubfigure)
677677
# these will be updated later as data is added
678678
self.dataLim=mtransforms.Bbox.null()
679679
self._viewLim=mtransforms.Bbox.unit()
@@ -1652,8 +1652,10 @@ def apply_aspect(self, position=None):
16521652
self._set_position(position,which='active')
16531653
return
16541654

1655-
fig_width,fig_height=self.get_figure().get_size_inches()
1656-
fig_aspect=fig_height/fig_width
1655+
trans=self.get_figure().transSubfigure
1656+
bb=mtransforms.Bbox.from_bounds(0,0,1,1).transformed(trans)
1657+
# this is the physical aspect of the panel (or figure):
1658+
fig_aspect=bb.height/bb.width
16571659

16581660
ifself._adjustable=='box':
16591661
ifselfinself._twinned_axes:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp