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

Commit5752a94

Browse files
committed
ENH: Subfigures
1 parentaa82b50 commit5752a94

File tree

21 files changed

+2075
-1607
lines changed

21 files changed

+2075
-1607
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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Axes3D no longer adds itself to figure
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
New `.Axes3D` objects previously added themselves to figures when they were
5+
created, which lead to them being added twice if
6+
``fig.add_subplot(111, projection='3d')`` was called. Now ``ax = Axes3d(fig)``
7+
will need to be explicitly added to the figure with ``fig.add_axes(ax)``, as
8+
also needs to be done for normal `.axes.Axes`.
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 ``.Subfigure.transSubfigure` transform
14+
associated with the subfigure. This transform also exists for a
15+
`.Figure` instance, and is equal to `.Figure.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 `.Figure.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. This sort
5+
of nesting was mostly done with nested gridspecs
6+
: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/subpanels>`,
12+
913
"""
1014
importmatplotlib.pyplotasplt
1115
importmatplotlib.gridspecasgridspec
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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))
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+
33+
# gridspec inside gridspec
34+
fig=plt.figure(constrained_layout=True,figsize=(10,4))
35+
subfigs=fig.subfigures(1,2,wspace=0.07)
36+
37+
axsLeft=subfigs[0].subplots(1,2,sharey=True)
38+
subfigs[0].set_facecolor('0.75')
39+
foraxinaxsLeft:
40+
pc=example_plot(ax)
41+
subfigs[0].suptitle('Left plots',fontsize='x-large')
42+
subfigs[0].colorbar(pc,shrink=0.6,ax=axsLeft,location='bottom')
43+
44+
axsRight=subfigs[1].subplots(3,1,sharex=True)
45+
fornn,axinenumerate(axsRight):
46+
pc=example_plot(ax,hide_labels=True)
47+
ifnn==2:
48+
ax.set_xlabel('xlabel')
49+
ifnn==1:
50+
ax.set_ylabel('ylabel')
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()

‎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: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ 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)
@@ -207,8 +207,7 @@ def _make_layout_margins(fig, renderer, *, w_pad=0, h_pad=0,
207207
hspace=hspace,wspace=wspace)
208208
panel._layoutgrid.parent.edit_outer_margin_mins(margins,ss)
209209

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

@@ -223,7 +222,6 @@ def _make_layout_margins(fig, renderer, *, w_pad=0, h_pad=0,
223222
hspace=hspace,wspace=wspace)
224223
margin0=margin.copy()
225224
pos,bbox=_get_pos_and_bbox(ax,renderer)
226-
227225
# the margin is the distance between the bounding box of the axes
228226
# and its position (plus the padding from above)
229227
margin['left']+=pos.x0-bbox.x0
@@ -281,9 +279,10 @@ def _make_margin_suptitles(fig, renderer, *, w_pad=0, h_pad=0):
281279
# top level figure margin larger.
282280
forpanelinfig.panels:
283281
_make_margin_suptitles(panel,renderer,w_pad=w_pad,h_pad=h_pad)
284-
invTransFig=fig.transPanel.inverted().transform_bbox
282+
invTransFig=fig.transSubfigure.inverted().transform_bbox
285283

286-
w_pad,h_pad= (fig.transPanel-fig.transFigure).transform((w_pad,h_pad))
284+
w_pad,h_pad= (fig.transSubfigure-
285+
fig.transFigure).transform((w_pad,h_pad))
287286

288287
iffig._suptitleisnotNoneandfig._suptitle.get_in_layout():
289288
bbox=invTransFig(fig._suptitle.get_tightbbox(renderer))
@@ -429,7 +428,7 @@ def _get_pos_and_bbox(ax, renderer):
429428
fig=ax.figure
430429
pos=ax.get_position(original=True)
431430
# pos is in panel co-ords, but we need in figure for the layout
432-
pos=pos.transformed(fig.transPanel-fig.transFigure)
431+
pos=pos.transformed(fig.transSubfigure-fig.transFigure)
433432
try:
434433
tightbbox=ax.get_tightbbox(renderer=renderer,for_layout_only=True)
435434
exceptTypeError:
@@ -446,7 +445,7 @@ def _reposition_axes(fig, renderer, *, w_pad=0, h_pad=0, hspace=0, wspace=0):
446445
"""
447446
Reposition all the axes based on the new inner bounding box.
448447
"""
449-
trans_fig_to_panel=fig.transFigure-fig.transPanel
448+
trans_fig_to_panel=fig.transFigure-fig.transSubfigure
450449
forpanelinfig.panels:
451450
bbox=panel._layoutgrid.get_outer_bbox()
452451
panel._redo_transform_rel_fig(
@@ -455,9 +454,7 @@ def _reposition_axes(fig, renderer, *, w_pad=0, h_pad=0, hspace=0, wspace=0):
455454
w_pad=w_pad,h_pad=h_pad,
456455
wspace=wspace,hspace=hspace)
457456

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

@@ -512,7 +509,7 @@ def _reposition_colorbar(cbax, renderer, *, offset=None):
512509
gs=parents[0].get_gridspec()
513510
ncols,nrows=gs.ncols,gs.nrows
514511
fig=cbax.figure
515-
trans_fig_to_panel=fig.transFigure-fig.transPanel
512+
trans_fig_to_panel=fig.transFigure-fig.transSubfigure
516513

517514
cb_rspans,cb_cspans=_get_cb_parent_spans(cbax)
518515
bboxparent=gs._layoutgrid.get_bbox_for_cb(rows=cb_rspans,cols=cb_cspans)
@@ -563,7 +560,7 @@ def _reposition_colorbar(cbax, renderer, *, offset=None):
563560
pbcb=pbcb.translated(0,dy)
564561

565562
pbcb=trans_fig_to_panel.transform_bbox(pbcb)
566-
cbax.set_transform(fig.transPanel)
563+
cbax.set_transform(fig.transSubfigure)
567564
cbax._set_position(pbcb)
568565
cbax.set_aspect(aspect,anchor=anchor,adjustable='box')
569566
returnoffset

‎lib/matplotlib/axes/_axes.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,31 @@
3939
_log=logging.getLogger(__name__)
4040

4141

42+
class_InsetLocator:
43+
"""
44+
Axes locator for `.Axes.inset_axes`.
45+
46+
The locater is a callable object used in `.Axes.set_aspect` to compute the
47+
axes location depending on the renderer.
48+
"""
49+
50+
def__init__(self,bounds,transform):
51+
"""
52+
*bounds* (a ``[l, b, w, h]`` rectangle) and *transform* together
53+
specify the position of the inset axes.
54+
"""
55+
self._bounds=bounds
56+
self._transform=transform
57+
58+
def__call__(self,ax,renderer):
59+
# Subtracting transSubfigure will typically rely on inverted(),
60+
# freezing the transform; thus, this needs to be delayed until draw
61+
# time as transSubfigure may otherwise change after this is evaluated.
62+
returnmtransforms.TransformedBbox(
63+
mtransforms.Bbox.from_bounds(*self._bounds),
64+
self._transform-ax.figure.transSubfigure)
65+
66+
4267
# The axes module contains all the wrappers to plotting functions.
4368
# All the other methods should go in the _AxesBase class.
4469

@@ -449,7 +474,7 @@ def indicate_inset(self, bounds, inset_ax=None, *, transform=None,
449474

450475
# decide which two of the lines to keep visible....
451476
pos=inset_ax.get_position()
452-
bboxins=pos.transformed(self.figure.transFigure)
477+
bboxins=pos.transformed(self.figure.transSubfigure)
453478
rectbbox=mtransforms.Bbox.from_bounds(
454479
*bounds
455480
).transformed(transform)

‎lib/matplotlib/axes/_base.py

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

668668
self.bbox=mtransforms.TransformedBbox(self._position,
669-
fig.transFigure)
669+
fig.transSubfigure)
670670
# these will be updated later as data is added
671671
self.dataLim=mtransforms.Bbox.null()
672672
self._viewLim=mtransforms.Bbox.unit()
@@ -1645,8 +1645,10 @@ def apply_aspect(self, position=None):
16451645
self._set_position(position,which='active')
16461646
return
16471647

1648-
fig_width,fig_height=self.get_figure().get_size_inches()
1649-
fig_aspect=fig_height/fig_width
1648+
trans=self.get_figure().transSubfigure
1649+
bb=mtransforms.Bbox.from_bounds(0,0,1,1).transformed(trans)
1650+
# this is the physical aspect of the panel (or figure):
1651+
fig_aspect=bb.height/bb.width
16501652

16511653
ifself._adjustable=='box':
16521654
ifselfinself._twinned_axes:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp