1r"""
2:mod:`~matplotlib.gridspec` contains classes that help to layout multiple
3`~.axes.Axes` in a grid-like pattern within a figure.
4
5The `GridSpec` specifies the overall grid structure. Individual cells within
6the grid are referenced by `SubplotSpec`\s.
7
8Often, users need not access this module directly, and can use higher-level
9methods like `~.pyplot.subplots`, `~.pyplot.subplot_mosaic` and
10`~.Figure.subfigures`. See the tutorial :ref:`arranging_axes` for a guide.
11"""
12
13import copy
14import logging
15from numbers import Integral
16
17import numpy as np
18
19import matplotlib as mpl
20from matplotlib import _api, _pylab_helpers, _tight_layout
21from matplotlib.transforms import Bbox
22
23_log = logging.getLogger(__name__)
24
25
26class GridSpecBase:
27 """
28 A base class of GridSpec that specifies the geometry of the grid
29 that a subplot will be placed.
30 """
31
32 def __init__(self, nrows, ncols, height_ratios=None, width_ratios=None):
33 """
34 Parameters
35 ----------
36 nrows, ncols : int
37 The number of rows and columns of the grid.
38 width_ratios : array-like of length *ncols*, optional
39 Defines the relative widths of the columns. Each column gets a
40 relative width of ``width_ratios[i] / sum(width_ratios)``.
41 If not given, all columns will have the same width.
42 height_ratios : array-like of length *nrows*, optional
43 Defines the relative heights of the rows. Each row gets a
44 relative height of ``height_ratios[i] / sum(height_ratios)``.
45 If not given, all rows will have the same height.
46 """
47 if not isinstance(nrows, Integral) or nrows <= 0:
48 raise ValueError(
49 f"Number of rows must be a positive integer, not {nrows!r}")
50 if not isinstance(ncols, Integral) or ncols <= 0:
51 raise ValueError(
52 f"Number of columns must be a positive integer, not {ncols!r}")
53 self._nrows, self._ncols = nrows, ncols
54 self.set_height_ratios(height_ratios)
55 self.set_width_ratios(width_ratios)
56
57 def __repr__(self):
58 height_arg = (f', height_ratios={self._row_height_ratios!r}'
59 if len(set(self._row_height_ratios)) != 1 else '')
60 width_arg = (f', width_ratios={self._col_width_ratios!r}'
61 if len(set(self._col_width_ratios)) != 1 else '')
62 return '{clsname}({nrows}, {ncols}{optionals})'.format(
63 clsname=self.__class__.__name__,
64 nrows=self._nrows,
65 ncols=self._ncols,
66 optionals=height_arg + width_arg,
67 )
68
69 nrows = property(lambda self: self._nrows,
70 doc="The number of rows in the grid.")
71 ncols = property(lambda self: self._ncols,
72 doc="The number of columns in the grid.")
73
74 def get_geometry(self):
75 """
76 Return a tuple containing the number of rows and columns in the grid.
77 """
78 return self._nrows, self._ncols
79
80 def get_subplot_params(self, figure=None):
81 # Must be implemented in subclasses
82 pass
83
84 def new_subplotspec(self, loc, rowspan=1, colspan=1):
85 """
86 Create and return a `.SubplotSpec` instance.
87
88 Parameters
89 ----------
90 loc : (int, int)
91 The position of the subplot in the grid as
92 ``(row_index, column_index)``.
93 rowspan, colspan : int, default: 1
94 The number of rows and columns the subplot should span in the grid.
95 """
96 loc1, loc2 = loc
97 subplotspec = self[loc1:loc1+rowspan, loc2:loc2+colspan]
98 return subplotspec
99
100 def set_width_ratios(self, width_ratios):
101 """
102 Set the relative widths of the columns.
103
104 *width_ratios* must be of length *ncols*. Each column gets a relative
105 width of ``width_ratios[i] / sum(width_ratios)``.
106 """
107 if width_ratios is None:
108 width_ratios = [1] * self._ncols
109 elif len(width_ratios) != self._ncols:
110 raise ValueError('Expected the given number of width ratios to '
111 'match the number of columns of the grid')
112 self._col_width_ratios = width_ratios
113
114 def get_width_ratios(self):
115 """
116 Return the width ratios.
117
118 This is *None* if no width ratios have been set explicitly.
119 """
120 return self._col_width_ratios
121
122 def set_height_ratios(self, height_ratios):
123 """
124 Set the relative heights of the rows.
125
126 *height_ratios* must be of length *nrows*. Each row gets a relative
127 height of ``height_ratios[i] / sum(height_ratios)``.
128 """
129 if height_ratios is None:
130 height_ratios = [1] * self._nrows
131 elif len(height_ratios) != self._nrows:
132 raise ValueError('Expected the given number of height ratios to '
133 'match the number of rows of the grid')
134 self._row_height_ratios = height_ratios
135
136 def get_height_ratios(self):
137 """
138 Return the height ratios.
139
140 This is *None* if no height ratios have been set explicitly.
141 """
142 return self._row_height_ratios
143
144 def get_grid_positions(self, fig):
145 """
146 Return the positions of the grid cells in figure coordinates.
147
148 Parameters
149 ----------
150 fig : `~matplotlib.figure.Figure`
151 The figure the grid should be applied to. The subplot parameters
152 (margins and spacing between subplots) are taken from *fig*.
153
154 Returns
155 -------
156 bottoms, tops, lefts, rights : array
157 The bottom, top, left, right positions of the grid cells in
158 figure coordinates.
159 """
160 nrows, ncols = self.get_geometry()
161 subplot_params = self.get_subplot_params(fig)
162 left = subplot_params.left
163 right = subplot_params.right
164 bottom = subplot_params.bottom
165 top = subplot_params.top
166 wspace = subplot_params.wspace
167 hspace = subplot_params.hspace
168 tot_width = right - left
169 tot_height = top - bottom
170
171 # calculate accumulated heights of columns
172 cell_h = tot_height / (nrows + hspace*(nrows-1))
173 sep_h = hspace * cell_h
174 norm = cell_h * nrows / sum(self._row_height_ratios)
175 cell_heights = [r * norm for r in self._row_height_ratios]
176 sep_heights = [0] + ([sep_h] * (nrows-1))
177 cell_hs = np.cumsum(np.column_stack([sep_heights, cell_heights]).flat)
178
179 # calculate accumulated widths of rows
180 cell_w = tot_width / (ncols + wspace*(ncols-1))
181 sep_w = wspace * cell_w
182 norm = cell_w * ncols / sum(self._col_width_ratios)
183 cell_widths = [r * norm for r in self._col_width_ratios]
184 sep_widths = [0] + ([sep_w] * (ncols-1))
185 cell_ws = np.cumsum(np.column_stack([sep_widths, cell_widths]).flat)
186
187 fig_tops, fig_bottoms = (top - cell_hs).reshape((-1, 2)).T
188 fig_lefts, fig_rights = (left + cell_ws).reshape((-1, 2)).T
189 return fig_bottoms, fig_tops, fig_lefts, fig_rights
190
191 @staticmethod
192 def _check_gridspec_exists(figure, nrows, ncols):
193 """
194 Check if the figure already has a gridspec with these dimensions,
195 or create a new one
196 """
197 for ax in figure.get_axes():
198 gs = ax.get_gridspec()
199 if gs is not None:
200 if hasattr(gs, 'get_topmost_subplotspec'):
201 # This is needed for colorbar gridspec layouts.
202 # This is probably OK because this whole logic tree
203 # is for when the user is doing simple things with the
204 # add_subplot command. For complicated layouts
205 # like subgridspecs the proper gridspec is passed in...
206 gs = gs.get_topmost_subplotspec().get_gridspec()
207 if gs.get_geometry() == (nrows, ncols):
208 return gs
209 # else gridspec not found:
210 return GridSpec(nrows, ncols, figure=figure)
211
212 def __getitem__(self, key):
213 """Create and return a `.SubplotSpec` instance."""
214 nrows, ncols = self.get_geometry()
215
216 def _normalize(key, size, axis): # Includes last index.
217 orig_key = key
218 if isinstance(key, slice):
219 start, stop, _ = key.indices(size)
220 if stop > start:
221 return start, stop - 1
222 raise IndexError("GridSpec slice would result in no space "
223 "allocated for subplot")
224 else:
225 if key < 0:
226 key = key + size
227 if 0 <= key < size:
228 return key, key
229 elif axis is not None:
230 raise IndexError(f"index {orig_key} is out of bounds for "
231 f"axis {axis} with size {size}")
232 else: # flat index
233 raise IndexError(f"index {orig_key} is out of bounds for "
234 f"GridSpec with size {size}")
235
236 if isinstance(key, tuple):
237 try:
238 k1, k2 = key
239 except ValueError as err:
240 raise ValueError("Unrecognized subplot spec") from err
241 num1, num2 = np.ravel_multi_index(
242 [_normalize(k1, nrows, 0), _normalize(k2, ncols, 1)],
243 (nrows, ncols))
244 else: # Single key
245 num1, num2 = _normalize(key, nrows * ncols, None)
246
247 return SubplotSpec(self, num1, num2)
248
249 def subplots(self, *, sharex=False, sharey=False, squeeze=True,
250 subplot_kw=None):
251 """
252 Add all subplots specified by this `GridSpec` to its parent figure.
253
254 See `.Figure.subplots` for detailed documentation.
255 """
256
257 figure = self.figure
258
259 if figure is None:
260 raise ValueError("GridSpec.subplots() only works for GridSpecs "
261 "created with a parent figure")
262
263 if not isinstance(sharex, str):
264 sharex = "all" if sharex else "none"
265 if not isinstance(sharey, str):
266 sharey = "all" if sharey else "none"
267
268 _api.check_in_list(["all", "row", "col", "none", False, True],
269 sharex=sharex, sharey=sharey)
270 if subplot_kw is None:
271 subplot_kw = {}
272 # don't mutate kwargs passed by user...
273 subplot_kw = subplot_kw.copy()
274
275 # Create array to hold all Axes.
276 axarr = np.empty((self._nrows, self._ncols), dtype=object)
277 for row in range(self._nrows):
278 for col in range(self._ncols):
279 shared_with = {"none": None, "all": axarr[0, 0],
280 "row": axarr[row, 0], "col": axarr[0, col]}
281 subplot_kw["sharex"] = shared_with[sharex]
282 subplot_kw["sharey"] = shared_with[sharey]
283 axarr[row, col] = figure.add_subplot(
284 self[row, col], **subplot_kw)
285
286 # turn off redundant tick labeling
287 if sharex in ["col", "all"]:
288 for ax in axarr.flat:
289 ax._label_outer_xaxis(skip_non_rectangular_axes=True)
290 if sharey in ["row", "all"]:
291 for ax in axarr.flat:
292 ax._label_outer_yaxis(skip_non_rectangular_axes=True)
293
294 if squeeze:
295 # Discarding unneeded dimensions that equal 1. If we only have one
296 # subplot, just return it instead of a 1-element array.
297 return axarr.item() if axarr.size == 1 else axarr.squeeze()
298 else:
299 # Returned axis array will be always 2-d, even if nrows=ncols=1.
300 return axarr
301
302
303class GridSpec(GridSpecBase):
304 """
305 A grid layout to place subplots within a figure.
306
307 The location of the grid cells is determined in a similar way to
308 `.SubplotParams` using *left*, *right*, *top*, *bottom*, *wspace*
309 and *hspace*.
310
311 Indexing a GridSpec instance returns a `.SubplotSpec`.
312 """
313 def __init__(self, nrows, ncols, figure=None,
314 left=None, bottom=None, right=None, top=None,
315 wspace=None, hspace=None,
316 width_ratios=None, height_ratios=None):
317 """
318 Parameters
319 ----------
320 nrows, ncols : int
321 The number of rows and columns of the grid.
322
323 figure : `.Figure`, optional
324 Only used for constrained layout to create a proper layoutgrid.
325
326 left, right, top, bottom : float, optional
327 Extent of the subplots as a fraction of figure width or height.
328 Left cannot be larger than right, and bottom cannot be larger than
329 top. If not given, the values will be inferred from a figure or
330 rcParams at draw time. See also `GridSpec.get_subplot_params`.
331
332 wspace : float, optional
333 The amount of width reserved for space between subplots,
334 expressed as a fraction of the average axis width.
335 If not given, the values will be inferred from a figure or
336 rcParams when necessary. See also `GridSpec.get_subplot_params`.
337
338 hspace : float, optional
339 The amount of height reserved for space between subplots,
340 expressed as a fraction of the average axis height.
341 If not given, the values will be inferred from a figure or
342 rcParams when necessary. See also `GridSpec.get_subplot_params`.
343
344 width_ratios : array-like of length *ncols*, optional
345 Defines the relative widths of the columns. Each column gets a
346 relative width of ``width_ratios[i] / sum(width_ratios)``.
347 If not given, all columns will have the same width.
348
349 height_ratios : array-like of length *nrows*, optional
350 Defines the relative heights of the rows. Each row gets a
351 relative height of ``height_ratios[i] / sum(height_ratios)``.
352 If not given, all rows will have the same height.
353
354 """
355 self.left = left
356 self.bottom = bottom
357 self.right = right
358 self.top = top
359 self.wspace = wspace
360 self.hspace = hspace
361 self.figure = figure
362
363 super().__init__(nrows, ncols,
364 width_ratios=width_ratios,
365 height_ratios=height_ratios)
366
367 _AllowedKeys = ["left", "bottom", "right", "top", "wspace", "hspace"]
368
369 def update(self, **kwargs):
370 """
371 Update the subplot parameters of the grid.
372
373 Parameters that are not explicitly given are not changed. Setting a
374 parameter to *None* resets it to :rc:`figure.subplot.*`.
375
376 Parameters
377 ----------
378 left, right, top, bottom : float or None, optional
379 Extent of the subplots as a fraction of figure width or height.
380 wspace, hspace : float, optional
381 Spacing between the subplots as a fraction of the average subplot
382 width / height.
383 """
384 for k, v in kwargs.items():
385 if k in self._AllowedKeys:
386 setattr(self, k, v)
387 else:
388 raise AttributeError(f"{k} is an unknown keyword")
389 for figmanager in _pylab_helpers.Gcf.figs.values():
390 for ax in figmanager.canvas.figure.axes:
391 if ax.get_subplotspec() is not None:
392 ss = ax.get_subplotspec().get_topmost_subplotspec()
393 if ss.get_gridspec() == self:
394 ax._set_position(
395 ax.get_subplotspec().get_position(ax.figure))
396
397 def get_subplot_params(self, figure=None):
398 """
399 Return the `.SubplotParams` for the GridSpec.
400
401 In order of precedence the values are taken from
402
403 - non-*None* attributes of the GridSpec
404 - the provided *figure*
405 - :rc:`figure.subplot.*`
406
407 Note that the ``figure`` attribute of the GridSpec is always ignored.
408 """
409 if figure is None:
410 kw = {k: mpl.rcParams["figure.subplot."+k]
411 for k in self._AllowedKeys}
412 subplotpars = SubplotParams(**kw)
413 else:
414 subplotpars = copy.copy(figure.subplotpars)
415
416 subplotpars.update(**{k: getattr(self, k) for k in self._AllowedKeys})
417
418 return subplotpars
419
420 def locally_modified_subplot_params(self):
421 """
422 Return a list of the names of the subplot parameters explicitly set
423 in the GridSpec.
424
425 This is a subset of the attributes of `.SubplotParams`.
426 """
427 return [k for k in self._AllowedKeys if getattr(self, k)]
428
429 def tight_layout(self, figure, renderer=None,
430 pad=1.08, h_pad=None, w_pad=None, rect=None):
431 """
432 Adjust subplot parameters to give specified padding.
433
434 Parameters
435 ----------
436 figure : `.Figure`
437 The figure.
438 renderer : `.RendererBase` subclass, optional
439 The renderer to be used.
440 pad : float
441 Padding between the figure edge and the edges of subplots, as a
442 fraction of the font-size.
443 h_pad, w_pad : float, optional
444 Padding (height/width) between edges of adjacent subplots.
445 Defaults to *pad*.
446 rect : tuple (left, bottom, right, top), default: None
447 (left, bottom, right, top) rectangle in normalized figure
448 coordinates that the whole subplots area (including labels) will
449 fit into. Default (None) is the whole figure.
450 """
451 if renderer is None:
452 renderer = figure._get_renderer()
453 kwargs = _tight_layout.get_tight_layout_figure(
454 figure, figure.axes,
455 _tight_layout.get_subplotspec_list(figure.axes, grid_spec=self),
456 renderer, pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect)
457 if kwargs:
458 self.update(**kwargs)
459
460
461class GridSpecFromSubplotSpec(GridSpecBase):
462 """
463 GridSpec whose subplot layout parameters are inherited from the
464 location specified by a given SubplotSpec.
465 """
466 def __init__(self, nrows, ncols,
467 subplot_spec,
468 wspace=None, hspace=None,
469 height_ratios=None, width_ratios=None):
470 """
471 Parameters
472 ----------
473 nrows, ncols : int
474 Number of rows and number of columns of the grid.
475 subplot_spec : SubplotSpec
476 Spec from which the layout parameters are inherited.
477 wspace, hspace : float, optional
478 See `GridSpec` for more details. If not specified default values
479 (from the figure or rcParams) are used.
480 height_ratios : array-like of length *nrows*, optional
481 See `GridSpecBase` for details.
482 width_ratios : array-like of length *ncols*, optional
483 See `GridSpecBase` for details.
484 """
485 self._wspace = wspace
486 self._hspace = hspace
487 if isinstance(subplot_spec, SubplotSpec):
488 self._subplot_spec = subplot_spec
489 else:
490 raise TypeError(
491 "subplot_spec must be type SubplotSpec, "
492 "usually from GridSpec, or axes.get_subplotspec.")
493 self.figure = self._subplot_spec.get_gridspec().figure
494 super().__init__(nrows, ncols,
495 width_ratios=width_ratios,
496 height_ratios=height_ratios)
497
498 def get_subplot_params(self, figure=None):
499 """Return a dictionary of subplot layout parameters."""
500 hspace = (self._hspace if self._hspace is not None
501 else figure.subplotpars.hspace if figure is not None
502 else mpl.rcParams["figure.subplot.hspace"])
503 wspace = (self._wspace if self._wspace is not None
504 else figure.subplotpars.wspace if figure is not None
505 else mpl.rcParams["figure.subplot.wspace"])
506
507 figbox = self._subplot_spec.get_position(figure)
508 left, bottom, right, top = figbox.extents
509
510 return SubplotParams(left=left, right=right,
511 bottom=bottom, top=top,
512 wspace=wspace, hspace=hspace)
513
514 def get_topmost_subplotspec(self):
515 """
516 Return the topmost `.SubplotSpec` instance associated with the subplot.
517 """
518 return self._subplot_spec.get_topmost_subplotspec()
519
520
521class SubplotSpec:
522 """
523 The location of a subplot in a `GridSpec`.
524
525 .. note::
526
527 Likely, you will never instantiate a `SubplotSpec` yourself. Instead,
528 you will typically obtain one from a `GridSpec` using item-access.
529
530 Parameters
531 ----------
532 gridspec : `~matplotlib.gridspec.GridSpec`
533 The GridSpec, which the subplot is referencing.
534 num1, num2 : int
535 The subplot will occupy the *num1*-th cell of the given
536 *gridspec*. If *num2* is provided, the subplot will span between
537 *num1*-th cell and *num2*-th cell **inclusive**.
538
539 The index starts from 0.
540 """
541 def __init__(self, gridspec, num1, num2=None):
542 self._gridspec = gridspec
543 self.num1 = num1
544 self.num2 = num2
545
546 def __repr__(self):
547 return (f"{self.get_gridspec()}["
548 f"{self.rowspan.start}:{self.rowspan.stop}, "
549 f"{self.colspan.start}:{self.colspan.stop}]")
550
551 @staticmethod
552 def _from_subplot_args(figure, args):
553 """
554 Construct a `.SubplotSpec` from a parent `.Figure` and either
555
556 - a `.SubplotSpec` -- returned as is;
557 - one or three numbers -- a MATLAB-style subplot specifier.
558 """
559 if len(args) == 1:
560 arg, = args
561 if isinstance(arg, SubplotSpec):
562 return arg
563 elif not isinstance(arg, Integral):
564 raise ValueError(
565 f"Single argument to subplot must be a three-digit "
566 f"integer, not {arg!r}")
567 try:
568 rows, cols, num = map(int, str(arg))
569 except ValueError:
570 raise ValueError(
571 f"Single argument to subplot must be a three-digit "
572 f"integer, not {arg!r}") from None
573 elif len(args) == 3:
574 rows, cols, num = args
575 else:
576 raise _api.nargs_error("subplot", takes="1 or 3", given=len(args))
577
578 gs = GridSpec._check_gridspec_exists(figure, rows, cols)
579 if gs is None:
580 gs = GridSpec(rows, cols, figure=figure)
581 if isinstance(num, tuple) and len(num) == 2:
582 if not all(isinstance(n, Integral) for n in num):
583 raise ValueError(
584 f"Subplot specifier tuple must contain integers, not {num}"
585 )
586 i, j = num
587 else:
588 if not isinstance(num, Integral) or num < 1 or num > rows*cols:
589 raise ValueError(
590 f"num must be an integer with 1 <= num <= {rows*cols}, "
591 f"not {num!r}"
592 )
593 i = j = num
594 return gs[i-1:j]
595
596 # num2 is a property only to handle the case where it is None and someone
597 # mutates num1.
598
599 @property
600 def num2(self):
601 return self.num1 if self._num2 is None else self._num2
602
603 @num2.setter
604 def num2(self, value):
605 self._num2 = value
606
607 def get_gridspec(self):
608 return self._gridspec
609
610 def get_geometry(self):
611 """
612 Return the subplot geometry as tuple ``(n_rows, n_cols, start, stop)``.
613
614 The indices *start* and *stop* define the range of the subplot within
615 the `GridSpec`. *stop* is inclusive (i.e. for a single cell
616 ``start == stop``).
617 """
618 rows, cols = self.get_gridspec().get_geometry()
619 return rows, cols, self.num1, self.num2
620
621 @property
622 def rowspan(self):
623 """The rows spanned by this subplot, as a `range` object."""
624 ncols = self.get_gridspec().ncols
625 return range(self.num1 // ncols, self.num2 // ncols + 1)
626
627 @property
628 def colspan(self):
629 """The columns spanned by this subplot, as a `range` object."""
630 ncols = self.get_gridspec().ncols
631 # We explicitly support num2 referring to a column on num1's *left*, so
632 # we must sort the column indices here so that the range makes sense.
633 c1, c2 = sorted([self.num1 % ncols, self.num2 % ncols])
634 return range(c1, c2 + 1)
635
636 def is_first_row(self):
637 return self.rowspan.start == 0
638
639 def is_last_row(self):
640 return self.rowspan.stop == self.get_gridspec().nrows
641
642 def is_first_col(self):
643 return self.colspan.start == 0
644
645 def is_last_col(self):
646 return self.colspan.stop == self.get_gridspec().ncols
647
648 def get_position(self, figure):
649 """
650 Update the subplot position from ``figure.subplotpars``.
651 """
652 gridspec = self.get_gridspec()
653 nrows, ncols = gridspec.get_geometry()
654 rows, cols = np.unravel_index([self.num1, self.num2], (nrows, ncols))
655 fig_bottoms, fig_tops, fig_lefts, fig_rights = \
656 gridspec.get_grid_positions(figure)
657
658 fig_bottom = fig_bottoms[rows].min()
659 fig_top = fig_tops[rows].max()
660 fig_left = fig_lefts[cols].min()
661 fig_right = fig_rights[cols].max()
662 return Bbox.from_extents(fig_left, fig_bottom, fig_right, fig_top)
663
664 def get_topmost_subplotspec(self):
665 """
666 Return the topmost `SubplotSpec` instance associated with the subplot.
667 """
668 gridspec = self.get_gridspec()
669 if hasattr(gridspec, "get_topmost_subplotspec"):
670 return gridspec.get_topmost_subplotspec()
671 else:
672 return self
673
674 def __eq__(self, other):
675 """
676 Two SubplotSpecs are considered equal if they refer to the same
677 position(s) in the same `GridSpec`.
678 """
679 # other may not even have the attributes we are checking.
680 return ((self._gridspec, self.num1, self.num2)
681 == (getattr(other, "_gridspec", object()),
682 getattr(other, "num1", object()),
683 getattr(other, "num2", object())))
684
685 def __hash__(self):
686 return hash((self._gridspec, self.num1, self.num2))
687
688 def subgridspec(self, nrows, ncols, **kwargs):
689 """
690 Create a GridSpec within this subplot.
691
692 The created `.GridSpecFromSubplotSpec` will have this `SubplotSpec` as
693 a parent.
694
695 Parameters
696 ----------
697 nrows : int
698 Number of rows in grid.
699
700 ncols : int
701 Number of columns in grid.
702
703 Returns
704 -------
705 `.GridSpecFromSubplotSpec`
706
707 Other Parameters
708 ----------------
709 **kwargs
710 All other parameters are passed to `.GridSpecFromSubplotSpec`.
711
712 See Also
713 --------
714 matplotlib.pyplot.subplots
715
716 Examples
717 --------
718 Adding three subplots in the space occupied by a single subplot::
719
720 fig = plt.figure()
721 gs0 = fig.add_gridspec(3, 1)
722 ax1 = fig.add_subplot(gs0[0])
723 ax2 = fig.add_subplot(gs0[1])
724 gssub = gs0[2].subgridspec(1, 3)
725 for i in range(3):
726 fig.add_subplot(gssub[0, i])
727 """
728 return GridSpecFromSubplotSpec(nrows, ncols, self, **kwargs)
729
730
731class SubplotParams:
732 """
733 Parameters defining the positioning of a subplots grid in a figure.
734 """
735
736 def __init__(self, left=None, bottom=None, right=None, top=None,
737 wspace=None, hspace=None):
738 """
739 Defaults are given by :rc:`figure.subplot.[name]`.
740
741 Parameters
742 ----------
743 left : float
744 The position of the left edge of the subplots,
745 as a fraction of the figure width.
746 right : float
747 The position of the right edge of the subplots,
748 as a fraction of the figure width.
749 bottom : float
750 The position of the bottom edge of the subplots,
751 as a fraction of the figure height.
752 top : float
753 The position of the top edge of the subplots,
754 as a fraction of the figure height.
755 wspace : float
756 The width of the padding between subplots,
757 as a fraction of the average Axes width.
758 hspace : float
759 The height of the padding between subplots,
760 as a fraction of the average Axes height.
761 """
762 for key in ["left", "bottom", "right", "top", "wspace", "hspace"]:
763 setattr(self, key, mpl.rcParams[f"figure.subplot.{key}"])
764 self.update(left, bottom, right, top, wspace, hspace)
765
766 def update(self, left=None, bottom=None, right=None, top=None,
767 wspace=None, hspace=None):
768 """
769 Update the dimensions of the passed parameters. *None* means unchanged.
770 """
771 if ((left if left is not None else self.left)
772 >= (right if right is not None else self.right)):
773 raise ValueError('left cannot be >= right')
774 if ((bottom if bottom is not None else self.bottom)
775 >= (top if top is not None else self.top)):
776 raise ValueError('bottom cannot be >= top')
777 if left is not None:
778 self.left = left
779 if right is not None:
780 self.right = right
781 if bottom is not None:
782 self.bottom = bottom
783 if top is not None:
784 self.top = top
785 if wspace is not None:
786 self.wspace = wspace
787 if hspace is not None:
788 self.hspace = hspace