Coverage for /usr/lib/python3/dist-packages/matplotlib/gridspec.py: 22%

279 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2025-06-14 15:25 +0200

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 

11:doc:`/tutorials/intermediate/arranging_axes` for a guide. 

12""" 

13 

14import copy 

15import logging 

16from numbers import Integral 

17 

18import numpy as np 

19 

20import matplotlib as mpl 

21from matplotlib import _api, _pylab_helpers, _tight_layout 

22from matplotlib.transforms import Bbox 

23 

24_log = logging.getLogger(__name__) 

25 

26 

27class GridSpecBase: 

28 """ 

29 A base class of GridSpec that specifies the geometry of the grid 

30 that a subplot will be placed. 

31 """ 

32 

33 def __init__(self, nrows, ncols, height_ratios=None, width_ratios=None): 

34 """ 

35 Parameters 

36 ---------- 

37 nrows, ncols : int 

38 The number of rows and columns of the grid. 

39 width_ratios : array-like of length *ncols*, optional 

40 Defines the relative widths of the columns. Each column gets a 

41 relative width of ``width_ratios[i] / sum(width_ratios)``. 

42 If not given, all columns will have the same width. 

43 height_ratios : array-like of length *nrows*, optional 

44 Defines the relative heights of the rows. Each row gets a 

45 relative height of ``height_ratios[i] / sum(height_ratios)``. 

46 If not given, all rows will have the same height. 

47 """ 

48 if not isinstance(nrows, Integral) or nrows <= 0: 

49 raise ValueError( 

50 f"Number of rows must be a positive integer, not {nrows!r}") 

51 if not isinstance(ncols, Integral) or ncols <= 0: 

52 raise ValueError( 

53 f"Number of columns must be a positive integer, not {ncols!r}") 

54 self._nrows, self._ncols = nrows, ncols 

55 self.set_height_ratios(height_ratios) 

56 self.set_width_ratios(width_ratios) 

57 

58 def __repr__(self): 

59 height_arg = (', height_ratios=%r' % (self._row_height_ratios,) 

60 if len(set(self._row_height_ratios)) != 1 else '') 

61 width_arg = (', width_ratios=%r' % (self._col_width_ratios,) 

62 if len(set(self._col_width_ratios)) != 1 else '') 

63 return '{clsname}({nrows}, {ncols}{optionals})'.format( 

64 clsname=self.__class__.__name__, 

65 nrows=self._nrows, 

66 ncols=self._ncols, 

67 optionals=height_arg + width_arg, 

68 ) 

69 

70 nrows = property(lambda self: self._nrows, 

71 doc="The number of rows in the grid.") 

72 ncols = property(lambda self: self._ncols, 

73 doc="The number of columns in the grid.") 

74 

75 def get_geometry(self): 

76 """ 

77 Return a tuple containing the number of rows and columns in the grid. 

78 """ 

79 return self._nrows, self._ncols 

80 

81 def get_subplot_params(self, figure=None): 

82 # Must be implemented in subclasses 

83 pass 

84 

85 def new_subplotspec(self, loc, rowspan=1, colspan=1): 

86 """ 

87 Create and return a `.SubplotSpec` instance. 

88 

89 Parameters 

90 ---------- 

91 loc : (int, int) 

92 The position of the subplot in the grid as 

93 ``(row_index, column_index)``. 

94 rowspan, colspan : int, default: 1 

95 The number of rows and columns the subplot should span in the grid. 

96 """ 

97 loc1, loc2 = loc 

98 subplotspec = self[loc1:loc1+rowspan, loc2:loc2+colspan] 

99 return subplotspec 

100 

101 def set_width_ratios(self, width_ratios): 

102 """ 

103 Set the relative widths of the columns. 

104 

105 *width_ratios* must be of length *ncols*. Each column gets a relative 

106 width of ``width_ratios[i] / sum(width_ratios)``. 

107 """ 

108 if width_ratios is None: 

109 width_ratios = [1] * self._ncols 

110 elif len(width_ratios) != self._ncols: 

111 raise ValueError('Expected the given number of width ratios to ' 

112 'match the number of columns of the grid') 

113 self._col_width_ratios = width_ratios 

114 

115 def get_width_ratios(self): 

116 """ 

117 Return the width ratios. 

118 

119 This is *None* if no width ratios have been set explicitly. 

120 """ 

121 return self._col_width_ratios 

122 

123 def set_height_ratios(self, height_ratios): 

124 """ 

125 Set the relative heights of the rows. 

126 

127 *height_ratios* must be of length *nrows*. Each row gets a relative 

128 height of ``height_ratios[i] / sum(height_ratios)``. 

129 """ 

130 if height_ratios is None: 

131 height_ratios = [1] * self._nrows 

132 elif len(height_ratios) != self._nrows: 

133 raise ValueError('Expected the given number of height ratios to ' 

134 'match the number of rows of the grid') 

135 self._row_height_ratios = height_ratios 

136 

137 def get_height_ratios(self): 

138 """ 

139 Return the height ratios. 

140 

141 This is *None* if no height ratios have been set explicitly. 

142 """ 

143 return self._row_height_ratios 

144 

145 @_api.delete_parameter("3.7", "raw") 

146 def get_grid_positions(self, fig, raw=False): 

147 """ 

148 Return the positions of the grid cells in figure coordinates. 

149 

150 Parameters 

151 ---------- 

152 fig : `~matplotlib.figure.Figure` 

153 The figure the grid should be applied to. The subplot parameters 

154 (margins and spacing between subplots) are taken from *fig*. 

155 raw : bool, default: False 

156 If *True*, the subplot parameters of the figure are not taken 

157 into account. The grid spans the range [0, 1] in both directions 

158 without margins and there is no space between grid cells. This is 

159 used for constrained_layout. 

160 

161 Returns 

162 ------- 

163 bottoms, tops, lefts, rights : array 

164 The bottom, top, left, right positions of the grid cells in 

165 figure coordinates. 

166 """ 

167 nrows, ncols = self.get_geometry() 

168 

169 if raw: 

170 left = 0. 

171 right = 1. 

172 bottom = 0. 

173 top = 1. 

174 wspace = 0. 

175 hspace = 0. 

176 else: 

177 subplot_params = self.get_subplot_params(fig) 

178 left = subplot_params.left 

179 right = subplot_params.right 

180 bottom = subplot_params.bottom 

181 top = subplot_params.top 

182 wspace = subplot_params.wspace 

183 hspace = subplot_params.hspace 

184 tot_width = right - left 

185 tot_height = top - bottom 

186 

187 # calculate accumulated heights of columns 

188 cell_h = tot_height / (nrows + hspace*(nrows-1)) 

189 sep_h = hspace * cell_h 

190 norm = cell_h * nrows / sum(self._row_height_ratios) 

191 cell_heights = [r * norm for r in self._row_height_ratios] 

192 sep_heights = [0] + ([sep_h] * (nrows-1)) 

193 cell_hs = np.cumsum(np.column_stack([sep_heights, cell_heights]).flat) 

194 

195 # calculate accumulated widths of rows 

196 cell_w = tot_width / (ncols + wspace*(ncols-1)) 

197 sep_w = wspace * cell_w 

198 norm = cell_w * ncols / sum(self._col_width_ratios) 

199 cell_widths = [r * norm for r in self._col_width_ratios] 

200 sep_widths = [0] + ([sep_w] * (ncols-1)) 

201 cell_ws = np.cumsum(np.column_stack([sep_widths, cell_widths]).flat) 

202 

203 fig_tops, fig_bottoms = (top - cell_hs).reshape((-1, 2)).T 

204 fig_lefts, fig_rights = (left + cell_ws).reshape((-1, 2)).T 

205 return fig_bottoms, fig_tops, fig_lefts, fig_rights 

206 

207 @staticmethod 

208 def _check_gridspec_exists(figure, nrows, ncols): 

209 """ 

210 Check if the figure already has a gridspec with these dimensions, 

211 or create a new one 

212 """ 

213 for ax in figure.get_axes(): 

214 if hasattr(ax, 'get_subplotspec'): 

215 gs = ax.get_subplotspec().get_gridspec() 

216 if hasattr(gs, 'get_topmost_subplotspec'): 

217 # This is needed for colorbar gridspec layouts. 

218 # This is probably OK because this whole logic tree 

219 # is for when the user is doing simple things with the 

220 # add_subplot command. For complicated layouts 

221 # like subgridspecs the proper gridspec is passed in... 

222 gs = gs.get_topmost_subplotspec().get_gridspec() 

223 if gs.get_geometry() == (nrows, ncols): 

224 return gs 

225 # else gridspec not found: 

226 return GridSpec(nrows, ncols, figure=figure) 

227 

228 def __getitem__(self, key): 

229 """Create and return a `.SubplotSpec` instance.""" 

230 nrows, ncols = self.get_geometry() 

231 

232 def _normalize(key, size, axis): # Includes last index. 

233 orig_key = key 

234 if isinstance(key, slice): 

235 start, stop, _ = key.indices(size) 

236 if stop > start: 

237 return start, stop - 1 

238 raise IndexError("GridSpec slice would result in no space " 

239 "allocated for subplot") 

240 else: 

241 if key < 0: 

242 key = key + size 

243 if 0 <= key < size: 

244 return key, key 

245 elif axis is not None: 

246 raise IndexError(f"index {orig_key} is out of bounds for " 

247 f"axis {axis} with size {size}") 

248 else: # flat index 

249 raise IndexError(f"index {orig_key} is out of bounds for " 

250 f"GridSpec with size {size}") 

251 

252 if isinstance(key, tuple): 

253 try: 

254 k1, k2 = key 

255 except ValueError as err: 

256 raise ValueError("Unrecognized subplot spec") from err 

257 num1, num2 = np.ravel_multi_index( 

258 [_normalize(k1, nrows, 0), _normalize(k2, ncols, 1)], 

259 (nrows, ncols)) 

260 else: # Single key 

261 num1, num2 = _normalize(key, nrows * ncols, None) 

262 

263 return SubplotSpec(self, num1, num2) 

264 

265 def subplots(self, *, sharex=False, sharey=False, squeeze=True, 

266 subplot_kw=None): 

267 """ 

268 Add all subplots specified by this `GridSpec` to its parent figure. 

269 

270 See `.Figure.subplots` for detailed documentation. 

271 """ 

272 

273 figure = self.figure 

274 

275 if figure is None: 

276 raise ValueError("GridSpec.subplots() only works for GridSpecs " 

277 "created with a parent figure") 

278 

279 if isinstance(sharex, bool): 

280 sharex = "all" if sharex else "none" 

281 if isinstance(sharey, bool): 

282 sharey = "all" if sharey else "none" 

283 # This check was added because it is very easy to type 

284 # `subplots(1, 2, 1)` when `subplot(1, 2, 1)` was intended. 

285 # In most cases, no error will ever occur, but mysterious behavior 

286 # will result because what was intended to be the subplot index is 

287 # instead treated as a bool for sharex. This check should go away 

288 # once sharex becomes kwonly. 

289 if isinstance(sharex, Integral): 

290 _api.warn_external( 

291 "sharex argument to subplots() was an integer. Did you " 

292 "intend to use subplot() (without 's')?") 

293 _api.check_in_list(["all", "row", "col", "none"], 

294 sharex=sharex, sharey=sharey) 

295 if subplot_kw is None: 

296 subplot_kw = {} 

297 # don't mutate kwargs passed by user... 

298 subplot_kw = subplot_kw.copy() 

299 

300 # Create array to hold all axes. 

301 axarr = np.empty((self._nrows, self._ncols), dtype=object) 

302 for row in range(self._nrows): 

303 for col in range(self._ncols): 

304 shared_with = {"none": None, "all": axarr[0, 0], 

305 "row": axarr[row, 0], "col": axarr[0, col]} 

306 subplot_kw["sharex"] = shared_with[sharex] 

307 subplot_kw["sharey"] = shared_with[sharey] 

308 axarr[row, col] = figure.add_subplot( 

309 self[row, col], **subplot_kw) 

310 

311 # turn off redundant tick labeling 

312 if sharex in ["col", "all"]: 

313 for ax in axarr.flat: 

314 ax._label_outer_xaxis(check_patch=True) 

315 if sharey in ["row", "all"]: 

316 for ax in axarr.flat: 

317 ax._label_outer_yaxis(check_patch=True) 

318 

319 if squeeze: 

320 # Discarding unneeded dimensions that equal 1. If we only have one 

321 # subplot, just return it instead of a 1-element array. 

322 return axarr.item() if axarr.size == 1 else axarr.squeeze() 

323 else: 

324 # Returned axis array will be always 2-d, even if nrows=ncols=1. 

325 return axarr 

326 

327 

328class GridSpec(GridSpecBase): 

329 """ 

330 A grid layout to place subplots within a figure. 

331 

332 The location of the grid cells is determined in a similar way to 

333 `~.figure.SubplotParams` using *left*, *right*, *top*, *bottom*, *wspace* 

334 and *hspace*. 

335 

336 Indexing a GridSpec instance returns a `.SubplotSpec`. 

337 """ 

338 def __init__(self, nrows, ncols, figure=None, 

339 left=None, bottom=None, right=None, top=None, 

340 wspace=None, hspace=None, 

341 width_ratios=None, height_ratios=None): 

342 """ 

343 Parameters 

344 ---------- 

345 nrows, ncols : int 

346 The number of rows and columns of the grid. 

347 

348 figure : `.Figure`, optional 

349 Only used for constrained layout to create a proper layoutgrid. 

350 

351 left, right, top, bottom : float, optional 

352 Extent of the subplots as a fraction of figure width or height. 

353 Left cannot be larger than right, and bottom cannot be larger than 

354 top. If not given, the values will be inferred from a figure or 

355 rcParams at draw time. See also `GridSpec.get_subplot_params`. 

356 

357 wspace : float, optional 

358 The amount of width reserved for space between subplots, 

359 expressed as a fraction of the average axis width. 

360 If not given, the values will be inferred from a figure or 

361 rcParams when necessary. See also `GridSpec.get_subplot_params`. 

362 

363 hspace : float, optional 

364 The amount of height reserved for space between subplots, 

365 expressed as a fraction of the average axis height. 

366 If not given, the values will be inferred from a figure or 

367 rcParams when necessary. See also `GridSpec.get_subplot_params`. 

368 

369 width_ratios : array-like of length *ncols*, optional 

370 Defines the relative widths of the columns. Each column gets a 

371 relative width of ``width_ratios[i] / sum(width_ratios)``. 

372 If not given, all columns will have the same width. 

373 

374 height_ratios : array-like of length *nrows*, optional 

375 Defines the relative heights of the rows. Each row gets a 

376 relative height of ``height_ratios[i] / sum(height_ratios)``. 

377 If not given, all rows will have the same height. 

378 

379 """ 

380 self.left = left 

381 self.bottom = bottom 

382 self.right = right 

383 self.top = top 

384 self.wspace = wspace 

385 self.hspace = hspace 

386 self.figure = figure 

387 

388 super().__init__(nrows, ncols, 

389 width_ratios=width_ratios, 

390 height_ratios=height_ratios) 

391 

392 _AllowedKeys = ["left", "bottom", "right", "top", "wspace", "hspace"] 

393 

394 def update(self, **kwargs): 

395 """ 

396 Update the subplot parameters of the grid. 

397 

398 Parameters that are not explicitly given are not changed. Setting a 

399 parameter to *None* resets it to :rc:`figure.subplot.*`. 

400 

401 Parameters 

402 ---------- 

403 left, right, top, bottom : float or None, optional 

404 Extent of the subplots as a fraction of figure width or height. 

405 wspace, hspace : float, optional 

406 Spacing between the subplots as a fraction of the average subplot 

407 width / height. 

408 """ 

409 for k, v in kwargs.items(): 

410 if k in self._AllowedKeys: 

411 setattr(self, k, v) 

412 else: 

413 raise AttributeError(f"{k} is an unknown keyword") 

414 for figmanager in _pylab_helpers.Gcf.figs.values(): 

415 for ax in figmanager.canvas.figure.axes: 

416 if isinstance(ax, mpl.axes.SubplotBase): 

417 ss = ax.get_subplotspec().get_topmost_subplotspec() 

418 if ss.get_gridspec() == self: 

419 ax._set_position( 

420 ax.get_subplotspec().get_position(ax.figure)) 

421 

422 def get_subplot_params(self, figure=None): 

423 """ 

424 Return the `.SubplotParams` for the GridSpec. 

425 

426 In order of precedence the values are taken from 

427 

428 - non-*None* attributes of the GridSpec 

429 - the provided *figure* 

430 - :rc:`figure.subplot.*` 

431 """ 

432 if figure is None: 

433 kw = {k: mpl.rcParams["figure.subplot."+k] 

434 for k in self._AllowedKeys} 

435 subplotpars = mpl.figure.SubplotParams(**kw) 

436 else: 

437 subplotpars = copy.copy(figure.subplotpars) 

438 

439 subplotpars.update(**{k: getattr(self, k) for k in self._AllowedKeys}) 

440 

441 return subplotpars 

442 

443 def locally_modified_subplot_params(self): 

444 """ 

445 Return a list of the names of the subplot parameters explicitly set 

446 in the GridSpec. 

447 

448 This is a subset of the attributes of `.SubplotParams`. 

449 """ 

450 return [k for k in self._AllowedKeys if getattr(self, k)] 

451 

452 def tight_layout(self, figure, renderer=None, 

453 pad=1.08, h_pad=None, w_pad=None, rect=None): 

454 """ 

455 Adjust subplot parameters to give specified padding. 

456 

457 Parameters 

458 ---------- 

459 pad : float 

460 Padding between the figure edge and the edges of subplots, as a 

461 fraction of the font-size. 

462 h_pad, w_pad : float, optional 

463 Padding (height/width) between edges of adjacent subplots. 

464 Defaults to *pad*. 

465 rect : tuple (left, bottom, right, top), default: None 

466 (left, bottom, right, top) rectangle in normalized figure 

467 coordinates that the whole subplots area (including labels) will 

468 fit into. Default (None) is the whole figure. 

469 """ 

470 if renderer is None: 

471 renderer = figure._get_renderer() 

472 kwargs = _tight_layout.get_tight_layout_figure( 

473 figure, figure.axes, 

474 _tight_layout.get_subplotspec_list(figure.axes, grid_spec=self), 

475 renderer, pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) 

476 if kwargs: 

477 self.update(**kwargs) 

478 

479 

480class GridSpecFromSubplotSpec(GridSpecBase): 

481 """ 

482 GridSpec whose subplot layout parameters are inherited from the 

483 location specified by a given SubplotSpec. 

484 """ 

485 def __init__(self, nrows, ncols, 

486 subplot_spec, 

487 wspace=None, hspace=None, 

488 height_ratios=None, width_ratios=None): 

489 """ 

490 Parameters 

491 ---------- 

492 nrows, ncols : int 

493 Number of rows and number of columns of the grid. 

494 subplot_spec : SubplotSpec 

495 Spec from which the layout parameters are inherited. 

496 wspace, hspace : float, optional 

497 See `GridSpec` for more details. If not specified default values 

498 (from the figure or rcParams) are used. 

499 height_ratios : array-like of length *nrows*, optional 

500 See `GridSpecBase` for details. 

501 width_ratios : array-like of length *ncols*, optional 

502 See `GridSpecBase` for details. 

503 """ 

504 self._wspace = wspace 

505 self._hspace = hspace 

506 self._subplot_spec = subplot_spec 

507 self.figure = self._subplot_spec.get_gridspec().figure 

508 super().__init__(nrows, ncols, 

509 width_ratios=width_ratios, 

510 height_ratios=height_ratios) 

511 

512 def get_subplot_params(self, figure=None): 

513 """Return a dictionary of subplot layout parameters.""" 

514 hspace = (self._hspace if self._hspace is not None 

515 else figure.subplotpars.hspace if figure is not None 

516 else mpl.rcParams["figure.subplot.hspace"]) 

517 wspace = (self._wspace if self._wspace is not None 

518 else figure.subplotpars.wspace if figure is not None 

519 else mpl.rcParams["figure.subplot.wspace"]) 

520 

521 figbox = self._subplot_spec.get_position(figure) 

522 left, bottom, right, top = figbox.extents 

523 

524 return mpl.figure.SubplotParams(left=left, right=right, 

525 bottom=bottom, top=top, 

526 wspace=wspace, hspace=hspace) 

527 

528 def get_topmost_subplotspec(self): 

529 """ 

530 Return the topmost `.SubplotSpec` instance associated with the subplot. 

531 """ 

532 return self._subplot_spec.get_topmost_subplotspec() 

533 

534 

535class SubplotSpec: 

536 """ 

537 The location of a subplot in a `GridSpec`. 

538 

539 .. note:: 

540 

541 Likely, you will never instantiate a `SubplotSpec` yourself. Instead, 

542 you will typically obtain one from a `GridSpec` using item-access. 

543 

544 Parameters 

545 ---------- 

546 gridspec : `~matplotlib.gridspec.GridSpec` 

547 The GridSpec, which the subplot is referencing. 

548 num1, num2 : int 

549 The subplot will occupy the *num1*-th cell of the given 

550 *gridspec*. If *num2* is provided, the subplot will span between 

551 *num1*-th cell and *num2*-th cell **inclusive**. 

552 

553 The index starts from 0. 

554 """ 

555 def __init__(self, gridspec, num1, num2=None): 

556 self._gridspec = gridspec 

557 self.num1 = num1 

558 self.num2 = num2 

559 

560 def __repr__(self): 

561 return (f"{self.get_gridspec()}[" 

562 f"{self.rowspan.start}:{self.rowspan.stop}, " 

563 f"{self.colspan.start}:{self.colspan.stop}]") 

564 

565 @staticmethod 

566 def _from_subplot_args(figure, args): 

567 """ 

568 Construct a `.SubplotSpec` from a parent `.Figure` and either 

569 

570 - a `.SubplotSpec` -- returned as is; 

571 - one or three numbers -- a MATLAB-style subplot specifier. 

572 """ 

573 if len(args) == 1: 

574 arg, = args 

575 if isinstance(arg, SubplotSpec): 

576 return arg 

577 elif not isinstance(arg, Integral): 

578 raise ValueError( 

579 f"Single argument to subplot must be a three-digit " 

580 f"integer, not {arg!r}") 

581 try: 

582 rows, cols, num = map(int, str(arg)) 

583 except ValueError: 

584 raise ValueError( 

585 f"Single argument to subplot must be a three-digit " 

586 f"integer, not {arg!r}") from None 

587 elif len(args) == 3: 

588 rows, cols, num = args 

589 else: 

590 raise TypeError(f"subplot() takes 1 or 3 positional arguments but " 

591 f"{len(args)} were given") 

592 

593 gs = GridSpec._check_gridspec_exists(figure, rows, cols) 

594 if gs is None: 

595 gs = GridSpec(rows, cols, figure=figure) 

596 if isinstance(num, tuple) and len(num) == 2: 

597 if not all(isinstance(n, Integral) for n in num): 

598 raise ValueError( 

599 f"Subplot specifier tuple must contain integers, not {num}" 

600 ) 

601 i, j = num 

602 else: 

603 if not isinstance(num, Integral) or num < 1 or num > rows*cols: 

604 raise ValueError( 

605 f"num must be 1 <= num <= {rows*cols}, not {num!r}") 

606 i = j = num 

607 return gs[i-1:j] 

608 

609 # num2 is a property only to handle the case where it is None and someone 

610 # mutates num1. 

611 

612 @property 

613 def num2(self): 

614 return self.num1 if self._num2 is None else self._num2 

615 

616 @num2.setter 

617 def num2(self, value): 

618 self._num2 = value 

619 

620 def get_gridspec(self): 

621 return self._gridspec 

622 

623 def get_geometry(self): 

624 """ 

625 Return the subplot geometry as tuple ``(n_rows, n_cols, start, stop)``. 

626 

627 The indices *start* and *stop* define the range of the subplot within 

628 the `GridSpec`. *stop* is inclusive (i.e. for a single cell 

629 ``start == stop``). 

630 """ 

631 rows, cols = self.get_gridspec().get_geometry() 

632 return rows, cols, self.num1, self.num2 

633 

634 @property 

635 def rowspan(self): 

636 """The rows spanned by this subplot, as a `range` object.""" 

637 ncols = self.get_gridspec().ncols 

638 return range(self.num1 // ncols, self.num2 // ncols + 1) 

639 

640 @property 

641 def colspan(self): 

642 """The columns spanned by this subplot, as a `range` object.""" 

643 ncols = self.get_gridspec().ncols 

644 # We explicitly support num2 referring to a column on num1's *left*, so 

645 # we must sort the column indices here so that the range makes sense. 

646 c1, c2 = sorted([self.num1 % ncols, self.num2 % ncols]) 

647 return range(c1, c2 + 1) 

648 

649 def is_first_row(self): 

650 return self.rowspan.start == 0 

651 

652 def is_last_row(self): 

653 return self.rowspan.stop == self.get_gridspec().nrows 

654 

655 def is_first_col(self): 

656 return self.colspan.start == 0 

657 

658 def is_last_col(self): 

659 return self.colspan.stop == self.get_gridspec().ncols 

660 

661 def get_position(self, figure): 

662 """ 

663 Update the subplot position from ``figure.subplotpars``. 

664 """ 

665 gridspec = self.get_gridspec() 

666 nrows, ncols = gridspec.get_geometry() 

667 rows, cols = np.unravel_index([self.num1, self.num2], (nrows, ncols)) 

668 fig_bottoms, fig_tops, fig_lefts, fig_rights = \ 

669 gridspec.get_grid_positions(figure) 

670 

671 fig_bottom = fig_bottoms[rows].min() 

672 fig_top = fig_tops[rows].max() 

673 fig_left = fig_lefts[cols].min() 

674 fig_right = fig_rights[cols].max() 

675 return Bbox.from_extents(fig_left, fig_bottom, fig_right, fig_top) 

676 

677 def get_topmost_subplotspec(self): 

678 """ 

679 Return the topmost `SubplotSpec` instance associated with the subplot. 

680 """ 

681 gridspec = self.get_gridspec() 

682 if hasattr(gridspec, "get_topmost_subplotspec"): 

683 return gridspec.get_topmost_subplotspec() 

684 else: 

685 return self 

686 

687 def __eq__(self, other): 

688 """ 

689 Two SubplotSpecs are considered equal if they refer to the same 

690 position(s) in the same `GridSpec`. 

691 """ 

692 # other may not even have the attributes we are checking. 

693 return ((self._gridspec, self.num1, self.num2) 

694 == (getattr(other, "_gridspec", object()), 

695 getattr(other, "num1", object()), 

696 getattr(other, "num2", object()))) 

697 

698 def __hash__(self): 

699 return hash((self._gridspec, self.num1, self.num2)) 

700 

701 def subgridspec(self, nrows, ncols, **kwargs): 

702 """ 

703 Create a GridSpec within this subplot. 

704 

705 The created `.GridSpecFromSubplotSpec` will have this `SubplotSpec` as 

706 a parent. 

707 

708 Parameters 

709 ---------- 

710 nrows : int 

711 Number of rows in grid. 

712 

713 ncols : int 

714 Number of columns in grid. 

715 

716 Returns 

717 ------- 

718 `.GridSpecFromSubplotSpec` 

719 

720 Other Parameters 

721 ---------------- 

722 **kwargs 

723 All other parameters are passed to `.GridSpecFromSubplotSpec`. 

724 

725 See Also 

726 -------- 

727 matplotlib.pyplot.subplots 

728 

729 Examples 

730 -------- 

731 Adding three subplots in the space occupied by a single subplot:: 

732 

733 fig = plt.figure() 

734 gs0 = fig.add_gridspec(3, 1) 

735 ax1 = fig.add_subplot(gs0[0]) 

736 ax2 = fig.add_subplot(gs0[1]) 

737 gssub = gs0[2].subgridspec(1, 3) 

738 for i in range(3): 

739 fig.add_subplot(gssub[0, i]) 

740 """ 

741 return GridSpecFromSubplotSpec(nrows, ncols, self, **kwargs)