|
11 | 11 |
|
12 | 12 | import copy
|
13 | 13 | import logging
|
| 14 | +from numbers import Integral |
14 | 15 |
|
15 | 16 | import numpy as np
|
16 | 17 |
|
@@ -234,6 +235,127 @@ def _normalize(key, size, axis): # Includes last index.
|
234 | 235 |
|
235 | 236 | return SubplotSpec(self, num1, num2)
|
236 | 237 |
|
| 238 | + def subplots(self, sharex=False, sharey=False, squeeze=True, |
| 239 | + subplot_kw=None): |
| 240 | + """ |
| 241 | + Add all subplots specified by this `GridSpec` to its parent figure. |
| 242 | +
|
| 243 | + This utility wrapper makes it convenient to create common layouts of |
| 244 | + subplots in a single call. |
| 245 | +
|
| 246 | + Parameters |
| 247 | + ---------- |
| 248 | + sharex, sharey : bool or {'none', 'all', 'row', 'col'}, default: False |
| 249 | + Controls sharing of properties among x (`sharex`) or y (`sharey`) |
| 250 | + axes: |
| 251 | +
|
| 252 | + - True or 'all': x- or y-axis will be shared among all |
| 253 | + subplots. |
| 254 | + - False or 'none': each subplot x- or y-axis will be |
| 255 | + independent. |
| 256 | + - 'row': each subplot row will share an x- or y-axis. |
| 257 | + - 'col': each subplot column will share an x- or y-axis. |
| 258 | +
|
| 259 | + When subplots have a shared x-axis along a column, only the x tick |
| 260 | + labels of the bottom subplot are created. Similarly, when subplots |
| 261 | + have a shared y-axis along a row, only the y tick labels of the |
| 262 | + first column subplot are created. To later turn other subplots' |
| 263 | + ticklabels on, use `~matplotlib.axes.Axes.tick_params`. |
| 264 | +
|
| 265 | + squeeze : bool, optional, default: True |
| 266 | + - If True, extra dimensions are squeezed out from the returned |
| 267 | + array of Axes: |
| 268 | +
|
| 269 | + - if only one subplot is constructed (nrows=ncols=1), the |
| 270 | + resulting single Axes object is returned as a scalar. |
| 271 | + - for Nx1 or 1xM subplots, the returned object is a 1D numpy |
| 272 | + object array of Axes objects. |
| 273 | + - for NxM, subplots with N>1 and M>1 are returned |
| 274 | + as a 2D array. |
| 275 | +
|
| 276 | + - If False, no squeezing at all is done: the returned Axes object |
| 277 | + is always a 2D array containing Axes instances, even if it ends |
| 278 | + up being 1x1. |
| 279 | +
|
| 280 | + subplot_kw : dict, optional |
| 281 | + Dict with keywords passed to the |
| 282 | + :meth:`~matplotlib.figure.Figure.add_subplot` call used to create |
| 283 | + each subplot. |
| 284 | +
|
| 285 | + Returns |
| 286 | + ------- |
| 287 | + ax : `~.axes.Axes` object or array of Axes objects. |
| 288 | + *ax* can be either a single `~matplotlib.axes.Axes` object or |
| 289 | + an array of Axes objects if more than one subplot was created. The |
| 290 | + dimensions of the resulting array can be controlled with the |
| 291 | + squeeze keyword, see above. |
| 292 | +
|
| 293 | + See Also |
| 294 | + -------- |
| 295 | + .pyplot.subplots |
| 296 | + .Figure.add_subplot |
| 297 | + .pyplot.subplot |
| 298 | + """ |
| 299 | + |
| 300 | + figure = self[0, 0].get_topmost_subplotspec().get_gridspec().figure |
| 301 | + |
| 302 | + if figure is None: |
| 303 | + raise ValueError("GridSpec.subplots() only works for GridSpecs " |
| 304 | + "created with a parent figure") |
| 305 | + |
| 306 | + if isinstance(sharex, bool): |
| 307 | + sharex = "all" if sharex else "none" |
| 308 | + if isinstance(sharey, bool): |
| 309 | + sharey = "all" if sharey else "none" |
| 310 | + # This check was added because it is very easy to type |
| 311 | + # `subplots(1, 2, 1)` when `subplot(1, 2, 1)` was intended. |
| 312 | + # In most cases, no error will ever occur, but mysterious behavior |
| 313 | + # will result because what was intended to be the subplot index is |
| 314 | + # instead treated as a bool for sharex. |
| 315 | + if isinstance(sharex, Integral): |
| 316 | + cbook._warn_external( |
| 317 | + "sharex argument to subplots() was an integer. Did you " |
| 318 | + "intend to use subplot() (without 's')?") |
| 319 | + cbook._check_in_list(["all", "row", "col", "none"], |
| 320 | + sharex=sharex, sharey=sharey) |
| 321 | + if subplot_kw is None: |
| 322 | + subplot_kw = {} |
| 323 | + # don't mutate kwargs passed by user... |
| 324 | + subplot_kw = subplot_kw.copy() |
| 325 | + |
| 326 | + # Create array to hold all axes. |
| 327 | + axarr = np.empty((self._nrows, self._ncols), dtype=object) |
| 328 | + for row in range(self._nrows): |
| 329 | + for col in range(self._ncols): |
| 330 | + shared_with = {"none": None, "all": axarr[0, 0], |
| 331 | + "row": axarr[row, 0], "col": axarr[0, col]} |
| 332 | + subplot_kw["sharex"] = shared_with[sharex] |
| 333 | + subplot_kw["sharey"] = shared_with[sharey] |
| 334 | + axarr[row, col] = figure.add_subplot( |
| 335 | + self[row, col], **subplot_kw) |
| 336 | + |
| 337 | + # turn off redundant tick labeling |
| 338 | + if sharex in ["col", "all"]: |
| 339 | + # turn off all but the bottom row |
| 340 | + for ax in axarr[:-1, :].flat: |
| 341 | + ax.xaxis.set_tick_params(which='both', |
| 342 | + labelbottom=False, labeltop=False) |
| 343 | + ax.xaxis.offsetText.set_visible(False) |
| 344 | + if sharey in ["row", "all"]: |
| 345 | + # turn off all but the first column |
| 346 | + for ax in axarr[:, 1:].flat: |
| 347 | + ax.yaxis.set_tick_params(which='both', |
| 348 | + labelleft=False, labelright=False) |
| 349 | + ax.yaxis.offsetText.set_visible(False) |
| 350 | + |
| 351 | + if squeeze: |
| 352 | + # Discarding unneeded dimensions that equal 1. If we only have one |
| 353 | + # subplot, just return it instead of a 1-element array. |
| 354 | + return axarr.item() if axarr.size == 1 else axarr.squeeze() |
| 355 | + else: |
| 356 | + # Returned axis array will be always 2-d, even if nrows=ncols=1. |
| 357 | + return axarr |
| 358 | + |
237 | 359 |
|
238 | 360 | class GridSpec(GridSpecBase):
|
239 | 361 | """
|
|
0 commit comments