1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

""" 

:mod:`~matplotlib.gridspec` is a module which specifies the location 

of the subplot in the figure. 

 

`GridSpec` 

specifies the geometry of the grid that a subplot will be 

placed. The number of rows and number of columns of the grid 

need to be set. Optionally, the subplot layout parameters 

(e.g., left, right, etc.) can be tuned. 

 

`SubplotSpec` 

specifies the location of the subplot in the given `GridSpec`. 

 

""" 

 

import copy 

import logging 

import warnings 

 

import numpy as np 

 

import matplotlib as mpl 

from matplotlib import _pylab_helpers, cbook, tight_layout, rcParams 

from matplotlib.transforms import Bbox 

import matplotlib._layoutbox as layoutbox 

 

_log = logging.getLogger(__name__) 

 

 

class GridSpecBase(object): 

""" 

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

that a subplot will be placed. 

""" 

 

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

""" 

The number of rows and number of columns of the grid need to 

be set. Optionally, the ratio of heights and widths of rows and 

columns can be specified. 

""" 

self._nrows, self._ncols = nrows, ncols 

self.set_height_ratios(height_ratios) 

self.set_width_ratios(width_ratios) 

 

def __repr__(self): 

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

if self._row_height_ratios is not None else '') 

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

if self._col_width_ratios is not None else '') 

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

clsname=self.__class__.__name__, 

nrows=self._nrows, 

ncols=self._ncols, 

optionals=height_arg + width_arg, 

) 

 

def get_geometry(self): 

'get the geometry of the grid, e.g., 2,3' 

return self._nrows, self._ncols 

 

def get_subplot_params(self, figure=None, fig=None): 

pass 

 

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

""" 

create and return a SuplotSpec instance. 

""" 

loc1, loc2 = loc 

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

return subplotspec 

 

def set_width_ratios(self, width_ratios): 

if width_ratios is not None and len(width_ratios) != self._ncols: 

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

'match the number of columns of the grid') 

self._col_width_ratios = width_ratios 

 

def get_width_ratios(self): 

return self._col_width_ratios 

 

def set_height_ratios(self, height_ratios): 

if height_ratios is not None and len(height_ratios) != self._nrows: 

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

'match the number of rows of the grid') 

self._row_height_ratios = height_ratios 

 

def get_height_ratios(self): 

return self._row_height_ratios 

 

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

""" 

return lists of bottom and top position of rows, left and 

right positions of columns. 

 

If raw=True, then these are all in units relative to the container 

with no margins. (used for constrained_layout). 

""" 

nrows, ncols = self.get_geometry() 

 

if raw: 

left = 0. 

right = 1. 

bottom = 0. 

top = 1. 

wspace = 0. 

hspace = 0. 

else: 

subplot_params = self.get_subplot_params(fig) 

left = subplot_params.left 

right = subplot_params.right 

bottom = subplot_params.bottom 

top = subplot_params.top 

wspace = subplot_params.wspace 

hspace = subplot_params.hspace 

tot_width = right - left 

tot_height = top - bottom 

 

# calculate accumulated heights of columns 

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

sep_h = hspace * cell_h 

if self._row_height_ratios is not None: 

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

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

else: 

cell_heights = [cell_h] * nrows 

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

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

 

# calculate accumulated widths of rows 

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

sep_w = wspace * cell_w 

if self._col_width_ratios is not None: 

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

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

else: 

cell_widths = [cell_w] * ncols 

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

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

 

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

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

return fig_bottoms, fig_tops, fig_lefts, fig_rights 

 

def __getitem__(self, key): 

"""Create and return a SuplotSpec instance. 

""" 

nrows, ncols = self.get_geometry() 

 

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

if isinstance(key, slice): 

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

if stop > start: 

return start, stop - 1 

else: 

if key < 0: 

key += size 

if 0 <= key < size: 

return key, key 

raise IndexError("invalid index") 

 

if isinstance(key, tuple): 

try: 

k1, k2 = key 

except ValueError: 

raise ValueError("unrecognized subplot spec") 

num1, num2 = np.ravel_multi_index( 

[_normalize(k1, nrows), _normalize(k2, ncols)], (nrows, ncols)) 

else: # Single key 

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

 

return SubplotSpec(self, num1, num2) 

 

 

class GridSpec(GridSpecBase): 

""" 

A class that specifies the geometry of the grid that a subplot 

will be placed. The location of grid is determined by similar way 

as the SubplotParams. 

""" 

 

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

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

wspace=None, hspace=None, 

width_ratios=None, height_ratios=None): 

""" 

The number of rows and number of columns of the grid need to be set. 

Optionally, the subplot layout parameters (e.g., left, right, etc.) 

can be tuned. 

 

Parameters 

---------- 

nrows : int 

Number of rows in grid. 

 

ncols : int 

Number or columns in grid. 

 

figure : ~.figure.Figure, optional 

 

left, right, top, bottom : float 

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

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

top. 

 

wspace : float 

The amount of width reserved for space between subplots, 

expressed as a fraction of the average axis width. 

 

hspace : float 

The amount of height reserved for space between subplots, 

expressed as a fraction of the average axis height. 

 

Notes 

----- 

See `~.figure.SubplotParams` for descriptions of the layout parameters. 

""" 

self.left = left 

self.bottom = bottom 

self.right = right 

self.top = top 

self.wspace = wspace 

self.hspace = hspace 

self.figure = figure 

 

GridSpecBase.__init__(self, nrows, ncols, 

width_ratios=width_ratios, 

height_ratios=height_ratios) 

 

if self.figure is None or not self.figure.get_constrained_layout(): 

self._layoutbox = None 

else: 

self.figure.init_layoutbox() 

self._layoutbox = layoutbox.LayoutBox( 

parent=self.figure._layoutbox, 

name='gridspec' + layoutbox.seq_id(), 

artist=self) 

# by default the layoutbox for a gridsepc will fill a figure. 

# but this can change below if the gridspec is created from a 

# subplotspec. (GridSpecFromSubplotSpec) 

 

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

 

def __getstate__(self): 

state = self.__dict__ 

try: 

state.pop('_layoutbox') 

except KeyError: 

pass 

return state 

 

def __setstate__(self, state): 

self.__dict__ = state 

# layoutboxes don't survive pickling... 

self._layoutbox = None 

 

def update(self, **kwargs): 

""" 

Update the current values. If any kwarg is None, default to 

the current value, if set, otherwise to rc. 

""" 

 

for k, v in kwargs.items(): 

if k in self._AllowedKeys: 

setattr(self, k, v) 

else: 

raise AttributeError("%s is unknown keyword" % (k,)) 

 

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

for ax in figmanager.canvas.figure.axes: 

# copied from Figure.subplots_adjust 

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

# Check if sharing a subplots axis 

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

if ax._sharex.get_subplotspec().get_gridspec() == self: 

ax._sharex.update_params() 

ax._set_position(ax._sharex.figbox) 

elif isinstance(ax._sharey, mpl.axes.SubplotBase): 

if ax._sharey.get_subplotspec().get_gridspec() == self: 

ax._sharey.update_params() 

ax._set_position(ax._sharey.figbox) 

else: 

ss = ax.get_subplotspec().get_topmost_subplotspec() 

if ss.get_gridspec() == self: 

ax.update_params() 

ax._set_position(ax.figbox) 

 

def get_subplot_params(self, figure=None, fig=None): 

""" 

Return a dictionary of subplot layout parameters. The default 

parameters are from rcParams unless a figure attribute is set. 

""" 

if fig is not None: 

cbook.warn_deprecated("2.2", "fig", obj_type="keyword argument", 

alternative="figure") 

if figure is None: 

figure = fig 

 

if figure is None: 

kw = {k: rcParams["figure.subplot."+k] for k in self._AllowedKeys} 

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

else: 

subplotpars = copy.copy(figure.subplotpars) 

 

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

 

return subplotpars 

 

def locally_modified_subplot_params(self): 

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

 

def tight_layout(self, figure, renderer=None, 

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

""" 

Adjust subplot parameters to give specified padding. 

 

Parameters 

---------- 

 

pad : float 

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

fraction of the font-size. 

h_pad, w_pad : float, optional 

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

Defaults to ``pad_inches``. 

rect : tuple of 4 floats, optional 

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

coordinates that the whole subplots area (including labels) will 

fit into. Default is (0, 0, 1, 1). 

""" 

 

subplotspec_list = tight_layout.get_subplotspec_list( 

figure.axes, grid_spec=self) 

if None in subplotspec_list: 

warnings.warn("This figure includes Axes that are not compatible " 

"with tight_layout, so results might be incorrect.") 

 

if renderer is None: 

renderer = tight_layout.get_renderer(figure) 

 

kwargs = tight_layout.get_tight_layout_figure( 

figure, figure.axes, subplotspec_list, renderer, 

pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) 

if kwargs: 

self.update(**kwargs) 

 

 

class GridSpecFromSubplotSpec(GridSpecBase): 

""" 

GridSpec whose subplot layout parameters are inherited from the 

location specified by a given SubplotSpec. 

""" 

def __init__(self, nrows, ncols, 

subplot_spec, 

wspace=None, hspace=None, 

height_ratios=None, width_ratios=None): 

""" 

The number of rows and number of columns of the grid need to 

be set. An instance of SubplotSpec is also needed to be set 

from which the layout parameters will be inherited. The wspace 

and hspace of the layout can be optionally specified or the 

default values (from the figure or rcParams) will be used. 

""" 

self._wspace = wspace 

self._hspace = hspace 

self._subplot_spec = subplot_spec 

GridSpecBase.__init__(self, nrows, ncols, 

width_ratios=width_ratios, 

height_ratios=height_ratios) 

# do the layoutboxes 

subspeclb = subplot_spec._layoutbox 

if subspeclb is None: 

self._layoutbox = None 

else: 

# OK, this is needed to divide the figure. 

self._layoutbox = subspeclb.layout_from_subplotspec( 

subplot_spec, 

name=subspeclb.name + '.gridspec' + layoutbox.seq_id(), 

artist=self) 

 

def get_subplot_params(self, figure=None, fig=None): 

"""Return a dictionary of subplot layout parameters. 

""" 

if fig is not None: 

cbook.warn_deprecated("2.2", "fig", obj_type="keyword argument", 

alternative="figure") 

if figure is None: 

figure = fig 

 

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

else figure.subplotpars.hspace if figure is not None 

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

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

else figure.subplotpars.wspace if figure is not None 

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

 

figbox = self._subplot_spec.get_position(figure) 

left, bottom, right, top = figbox.extents 

 

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

bottom=bottom, top=top, 

wspace=wspace, hspace=hspace) 

 

def get_topmost_subplotspec(self): 

"""Get the topmost SubplotSpec instance associated with the subplot.""" 

return self._subplot_spec.get_topmost_subplotspec() 

 

 

class SubplotSpec(object): 

"""Specifies the location of the subplot in the given `GridSpec`. 

""" 

 

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

""" 

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

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

num1-th cell and num2-th cell. 

 

The index starts from 0. 

""" 

self._gridspec = gridspec 

self.num1 = num1 

self.num2 = num2 

if gridspec._layoutbox is not None: 

glb = gridspec._layoutbox 

# So note that here we don't assign any layout yet, 

# just make the layoutbox that will conatin all items 

# associated w/ this axis. This can include other axes like 

# a colorbar or a legend. 

self._layoutbox = layoutbox.LayoutBox( 

parent=glb, 

name=glb.name + '.ss' + layoutbox.seq_id(), 

artist=self) 

else: 

self._layoutbox = None 

 

def __getstate__(self): 

state = self.__dict__ 

try: 

state.pop('_layoutbox') 

except KeyError: 

pass 

return state 

 

def __setstate__(self, state): 

self.__dict__ = state 

# layoutboxes don't survive pickling... 

self._layoutbox = None 

 

def get_gridspec(self): 

return self._gridspec 

 

def get_geometry(self): 

""" 

Get the subplot geometry (``n_rows, n_cols, start, stop``). 

 

start and stop are the index of the start and stop of the 

subplot. 

""" 

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

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

 

def get_rows_columns(self): 

""" 

Get the subplot row and column numbers: 

(``n_rows, n_cols, row_start, row_stop, col_start, col_stop``) 

""" 

gridspec = self.get_gridspec() 

nrows, ncols = gridspec.get_geometry() 

row_start, col_start = divmod(self.num1, ncols) 

if self.num2 is not None: 

row_stop, col_stop = divmod(self.num2, ncols) 

else: 

row_stop = row_start 

col_stop = col_start 

return nrows, ncols, row_start, row_stop, col_start, col_stop 

 

def get_position(self, figure, return_all=False): 

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

""" 

gridspec = self.get_gridspec() 

nrows, ncols = gridspec.get_geometry() 

rows, cols = np.unravel_index( 

[self.num1] if self.num2 is None else [self.num1, self.num2], 

(nrows, ncols)) 

fig_bottoms, fig_tops, fig_lefts, fig_rights = \ 

gridspec.get_grid_positions(figure) 

 

fig_bottom = fig_bottoms[rows].min() 

fig_top = fig_tops[rows].max() 

fig_left = fig_lefts[cols].min() 

fig_right = fig_rights[cols].max() 

figbox = Bbox.from_extents(fig_left, fig_bottom, fig_right, fig_top) 

 

if return_all: 

return figbox, rows[0], cols[0], nrows, ncols 

else: 

return figbox 

 

def get_topmost_subplotspec(self): 

'get the topmost SubplotSpec instance associated with the subplot' 

gridspec = self.get_gridspec() 

if hasattr(gridspec, "get_topmost_subplotspec"): 

return gridspec.get_topmost_subplotspec() 

else: 

return self 

 

def __eq__(self, other): 

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

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

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

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

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

 

def __hash__(self): 

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

 

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

""" 

Return a `.GridSpecFromSubplotSpec` that has this subplotspec as 

a parent. 

 

Parameters 

---------- 

nrows : int 

Number of rows in grid. 

 

ncols : int 

Number or columns in grid. 

 

Returns 

------- 

gridspec : `.GridSpec` 

 

Other Parameters 

---------------- 

**kwargs 

All other parameters are passed to `.GridSpec`. 

 

See Also 

-------- 

matplotlib.pyplot.subplots 

 

Examples 

-------- 

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

 

fig = plt.figure() 

gs0 = fig.add_gridspec(3, 1) 

ax1 = fig.add_subplot(gs0[0]) 

ax2 = fig.add_subplot(gs0[1]) 

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

for i in range(3): 

fig.add_subplot(gssub[0, i]) 

""" 

 

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