plotnine 0.15.3__py3-none-any.whl → 0.16.0a2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. plotnine/_mpl/gridspec.py +50 -6
  2. plotnine/_mpl/layout_manager/__init__.py +2 -5
  3. plotnine/_mpl/layout_manager/_composition_layout_items.py +98 -0
  4. plotnine/_mpl/layout_manager/_composition_side_space.py +461 -0
  5. plotnine/_mpl/layout_manager/_engine.py +19 -58
  6. plotnine/_mpl/layout_manager/_grid.py +94 -0
  7. plotnine/_mpl/layout_manager/_layout_tree.py +402 -817
  8. plotnine/_mpl/layout_manager/{_layout_items.py → _plot_layout_items.py} +55 -278
  9. plotnine/_mpl/layout_manager/{_spaces.py → _plot_side_space.py} +111 -291
  10. plotnine/_mpl/layout_manager/_side_space.py +176 -0
  11. plotnine/_mpl/utils.py +259 -1
  12. plotnine/_utils/__init__.py +23 -3
  13. plotnine/_utils/context.py +9 -13
  14. plotnine/_utils/dataclasses.py +24 -0
  15. plotnine/_utils/ipython.py +4 -1
  16. plotnine/animation.py +13 -12
  17. plotnine/composition/__init__.py +6 -0
  18. plotnine/composition/_beside.py +13 -11
  19. plotnine/composition/_compose.py +263 -99
  20. plotnine/composition/_plot_annotation.py +75 -0
  21. plotnine/composition/_plot_layout.py +143 -0
  22. plotnine/composition/_plot_spacer.py +1 -1
  23. plotnine/composition/_stack.py +13 -11
  24. plotnine/composition/_types.py +28 -0
  25. plotnine/composition/_wrap.py +60 -0
  26. plotnine/facets/facet.py +9 -12
  27. plotnine/facets/facet_grid.py +2 -2
  28. plotnine/facets/facet_wrap.py +1 -1
  29. plotnine/geoms/geom.py +2 -2
  30. plotnine/geoms/geom_map.py +4 -5
  31. plotnine/geoms/geom_path.py +8 -7
  32. plotnine/geoms/geom_rug.py +6 -10
  33. plotnine/geoms/geom_text.py +5 -5
  34. plotnine/ggplot.py +63 -9
  35. plotnine/guides/guide.py +24 -6
  36. plotnine/guides/guide_colorbar.py +88 -46
  37. plotnine/guides/guide_legend.py +47 -20
  38. plotnine/guides/guides.py +2 -2
  39. plotnine/iapi.py +17 -1
  40. plotnine/scales/scale.py +1 -1
  41. plotnine/stats/binning.py +15 -43
  42. plotnine/stats/smoothers.py +7 -3
  43. plotnine/stats/stat.py +2 -2
  44. plotnine/stats/stat_density_2d.py +10 -6
  45. plotnine/stats/stat_pointdensity.py +8 -1
  46. plotnine/stats/stat_qq.py +5 -5
  47. plotnine/stats/stat_qq_line.py +6 -1
  48. plotnine/stats/stat_sina.py +19 -20
  49. plotnine/stats/stat_summary.py +4 -2
  50. plotnine/stats/stat_summary_bin.py +7 -1
  51. plotnine/themes/elements/element_line.py +2 -0
  52. plotnine/themes/elements/element_text.py +12 -1
  53. plotnine/themes/theme.py +18 -24
  54. plotnine/themes/themeable.py +17 -3
  55. plotnine/typing.py +9 -2
  56. {plotnine-0.15.3.dist-info → plotnine-0.16.0a2.dist-info}/METADATA +3 -3
  57. {plotnine-0.15.3.dist-info → plotnine-0.16.0a2.dist-info}/RECORD +60 -52
  58. {plotnine-0.15.3.dist-info → plotnine-0.16.0a2.dist-info}/WHEEL +1 -1
  59. plotnine/composition/_plotspec.py +0 -50
  60. {plotnine-0.15.3.dist-info → plotnine-0.16.0a2.dist-info}/licenses/LICENSE +0 -0
  61. {plotnine-0.15.3.dist-info → plotnine-0.16.0a2.dist-info}/top_level.txt +0 -0
plotnine/_mpl/gridspec.py CHANGED
@@ -11,15 +11,16 @@ except ImportError:
11
11
  from matplotlib.figure import SubplotParams
12
12
  from matplotlib.gridspec import GridSpecBase
13
13
 
14
+ from matplotlib.gridspec import SubplotSpec
14
15
  from matplotlib.transforms import Bbox, BboxTransformTo, TransformedBbox
15
16
 
16
17
  if TYPE_CHECKING:
17
18
  from matplotlib.figure import Figure
18
- from matplotlib.gridspec import SubplotSpec
19
19
  from matplotlib.patches import Rectangle
20
20
  from matplotlib.transforms import Transform
21
21
 
22
- from plotnine._mpl.layout_manager._spaces import GridSpecParams
22
+ from plotnine._mpl.layout_manager._side_space import GridSpecParams
23
+ from plotnine.composition._plot_layout import plot_layout
23
24
 
24
25
 
25
26
  class p9GridSpec(GridSpecBase):
@@ -27,8 +28,7 @@ class p9GridSpec(GridSpecBase):
27
28
  Gridspec for plotnine plots
28
29
 
29
30
  This gridspec does not read any subplot parameter values from matplotlib's
30
- rcparams and the default a grid that fills up all the available space;
31
- there is no space along the edges and between the subplots.
31
+ rcparams. And there is no space along the edges and between the subplots.
32
32
 
33
33
  This gridspec can also be initialised while contained/nested in a given
34
34
  subplot.
@@ -61,10 +61,12 @@ class p9GridSpec(GridSpecBase):
61
61
  *,
62
62
  width_ratios=None,
63
63
  height_ratios=None,
64
+ byrow: bool = True,
64
65
  nest_into: SubplotSpec | None = None,
65
66
  ):
66
67
  self.figure = figure
67
68
  self._nested_gridspecs = []
69
+ self.byrow = byrow
68
70
 
69
71
  super().__init__(
70
72
  nrows,
@@ -92,6 +94,34 @@ class p9GridSpec(GridSpecBase):
92
94
  hspace=0,
93
95
  )
94
96
 
97
+ @staticmethod
98
+ def from_layout(
99
+ layout: plot_layout,
100
+ figure: Figure,
101
+ *,
102
+ nest_into: SubplotSpec | None = None,
103
+ ) -> p9GridSpec:
104
+ """
105
+ Create gridspec from a plot_layout instance
106
+ """
107
+ return p9GridSpec(
108
+ layout.nrow,
109
+ layout.ncol,
110
+ figure,
111
+ byrow=True if layout.byrow is None else layout.byrow,
112
+ nest_into=nest_into,
113
+ )
114
+
115
+ def __iter__(self):
116
+ from itertools import product
117
+
118
+ if self.byrow:
119
+ for r, c in product(range(self.nrows), range(self.ncols)):
120
+ yield SubplotSpec(self, r * self.ncols + c)
121
+ else:
122
+ for c, r in product(range(self.ncols), range(self.nrows)):
123
+ yield SubplotSpec(self, r * self.ncols + c)
124
+
95
125
  @property
96
126
  def patch(self) -> Rectangle:
97
127
  """
@@ -170,9 +200,9 @@ class p9GridSpec(GridSpecBase):
170
200
  for gs in self._nested_gridspecs:
171
201
  gs._update_artists()
172
202
 
173
- def layout(self, gsparams: GridSpecParams):
203
+ def update_params_and_artists(self, gsparams: GridSpecParams):
174
204
  """
175
- Update the layout of the gridspec
205
+ Update gridpspec params and the artists
176
206
  """
177
207
  self.update(**asdict(gsparams))
178
208
  self._update_artists()
@@ -244,6 +274,20 @@ class p9GridSpec(GridSpecBase):
244
274
  """
245
275
  return TransformedBbox(self.bbox_relative, self.figure.transSubfigure)
246
276
 
277
+ @property
278
+ def width(self) -> float:
279
+ """
280
+ Width of bbox in figure space
281
+ """
282
+ return self.bbox_relative.width
283
+
284
+ @property
285
+ def height(self) -> float:
286
+ """
287
+ Height of bbox in figure space
288
+ """
289
+ return self.bbox_relative.height
290
+
247
291
  def to_transform(self) -> Transform:
248
292
  """
249
293
  Return transform of this gridspec
@@ -1,6 +1,3 @@
1
- from ._engine import PlotnineCompositionLayoutEngine, PlotnineLayoutEngine
1
+ from ._engine import PlotnineLayoutEngine
2
2
 
3
- __all__ = (
4
- "PlotnineLayoutEngine",
5
- "PlotnineCompositionLayoutEngine",
6
- )
3
+ __all__ = ("PlotnineLayoutEngine",)
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING
5
+
6
+ from matplotlib.text import Text
7
+
8
+ from plotnine._mpl.utils import (
9
+ ArtistGeometry,
10
+ JustifyBoundaries,
11
+ TextJustifier,
12
+ )
13
+
14
+ if TYPE_CHECKING:
15
+ from typing import Any
16
+
17
+ from plotnine.composition._compose import Compose
18
+
19
+ from ._composition_side_space import CompositionSideSpaces
20
+
21
+
22
+ @dataclass
23
+ class CompositionLayoutItems:
24
+ """
25
+ plot_annotation artists
26
+ """
27
+
28
+ def __init__(self, cmp: Compose):
29
+ def get(name: str) -> Any:
30
+ """
31
+ Return themeable target or None
32
+ """
33
+ if self._is_blank(name):
34
+ return None
35
+ else:
36
+ t = getattr(cmp.theme.targets, name)
37
+ if isinstance(t, Text) and t.get_text() == "":
38
+ return None
39
+ return t
40
+
41
+ self.cmp = cmp
42
+ self.geometry = ArtistGeometry(cmp.figure)
43
+
44
+ self.plot_title: Text | None = get("plot_title")
45
+ self.plot_subtitle: Text | None = get("plot_subtitle")
46
+ self.plot_caption: Text | None = get("plot_caption")
47
+
48
+ def _is_blank(self, name: str) -> bool:
49
+ return self.cmp.theme.T.is_blank(name)
50
+
51
+ def _move_artists(self, spaces: CompositionSideSpaces):
52
+ """
53
+ Move the annotations to their final positions
54
+ """
55
+ theme = self.cmp.theme
56
+ plot_title_position = theme.getp("plot_title_position", "panel")
57
+ plot_caption_position = theme.getp("plot_caption_position", "panel")
58
+ justify = CompositionTextJustifier(spaces)
59
+
60
+ if self.plot_title:
61
+ ha = theme.getp(("plot_title", "ha"))
62
+ self.plot_title.set_y(spaces.t.y2("plot_title"))
63
+ justify.horizontally_about(
64
+ self.plot_title, ha, plot_title_position
65
+ )
66
+
67
+ if self.plot_subtitle:
68
+ ha = theme.getp(("plot_subtitle", "ha"))
69
+ self.plot_subtitle.set_y(spaces.t.y2("plot_subtitle"))
70
+ justify.horizontally_about(
71
+ self.plot_subtitle, ha, plot_title_position
72
+ )
73
+
74
+ if self.plot_caption:
75
+ ha = theme.getp(("plot_caption", "ha"), "right")
76
+ self.plot_caption.set_y(spaces.b.y1("plot_caption"))
77
+ justify.horizontally_about(
78
+ self.plot_caption, ha, plot_caption_position
79
+ )
80
+
81
+
82
+ class CompositionTextJustifier(TextJustifier):
83
+ """
84
+ Justify Text about a composition or it's panels
85
+ """
86
+
87
+ def __init__(self, spaces: CompositionSideSpaces):
88
+ boundaries = JustifyBoundaries(
89
+ plot_left=spaces.plot_left,
90
+ plot_right=spaces.plot_right,
91
+ plot_bottom=spaces.plot_bottom,
92
+ plot_top=spaces.plot_top,
93
+ panel_left=spaces.panel_left,
94
+ panel_right=spaces.panel_right,
95
+ panel_bottom=spaces.panel_bottom,
96
+ panel_top=spaces.panel_top,
97
+ )
98
+ super().__init__(spaces.cmp.figure, boundaries)
@@ -0,0 +1,461 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from plotnine._mpl.layout_manager._layout_tree import LayoutTree
6
+ from plotnine._mpl.layout_manager._plot_side_space import PlotSideSpaces
7
+
8
+ from ._composition_layout_items import CompositionLayoutItems
9
+ from ._side_space import GridSpecParams, _side_space
10
+
11
+ if TYPE_CHECKING:
12
+ from plotnine.composition._compose import Compose
13
+
14
+
15
+ class _composition_side_space(_side_space):
16
+ """
17
+ Base class for the side space around a composition
18
+ """
19
+
20
+ def __init__(self, items: CompositionLayoutItems):
21
+ self.items = items
22
+ self.gridspec = items.cmp._gridspec
23
+ self._calculate()
24
+
25
+
26
+ class composition_left_space(_composition_side_space):
27
+ plot_margin: float = 0
28
+
29
+ def _calculate(self):
30
+ theme = self.items.cmp.theme
31
+ self.plot_margin = theme.getp("plot_margin_left")
32
+
33
+ @property
34
+ def offset(self) -> float:
35
+ """
36
+ Distance from left of the figure to the left of the plot gridspec
37
+
38
+ ----------------(1, 1)
39
+ | ---- |
40
+ | dx | | |
41
+ |<--->| | |
42
+ | | | |
43
+ | ---- |
44
+ (0, 0)----------------
45
+
46
+ """
47
+ return self.gridspec.bbox_relative.x0
48
+
49
+ def x1(self, item: str) -> float:
50
+ """
51
+ Lower x-coordinate in figure space of the item
52
+ """
53
+ return self.to_figure_space(self.sum_upto(item))
54
+
55
+ def x2(self, item: str) -> float:
56
+ """
57
+ Higher x-coordinate in figure space of the item
58
+ """
59
+ return self.to_figure_space(self.sum_incl(item))
60
+
61
+ @property
62
+ def items_left_relative(self):
63
+ """
64
+ Left (relative to the gridspec) of the cmp items in figure dimensions
65
+ """
66
+ return self.total
67
+
68
+ @property
69
+ def items_left(self):
70
+ """
71
+ Left of the composition items in figure space
72
+ """
73
+ return self.to_figure_space(self.items_left_relative)
74
+
75
+
76
+ class composition_right_space(_composition_side_space):
77
+ """
78
+ Space for annotations to the right of the actual composition
79
+
80
+ Ordered from the edge of the figure and going inwards
81
+ """
82
+
83
+ plot_margin: float = 0
84
+
85
+ def _calculate(self):
86
+ theme = self.items.cmp.theme
87
+ self.plot_margin = theme.getp("plot_margin_right")
88
+
89
+ @property
90
+ def offset(self):
91
+ """
92
+ Distance from right of the figure to the right of the plot gridspec
93
+
94
+ ---------------(1, 1)
95
+ | ---- |
96
+ | | | -dx |
97
+ | | |<--->|
98
+ | | | |
99
+ | ---- |
100
+ (0, 0)---------------
101
+
102
+ """
103
+ return self.gridspec.bbox_relative.x1 - 1
104
+
105
+ def x1(self, item: str) -> float:
106
+ """
107
+ Lower x-coordinate in figure space of the item
108
+ """
109
+ return self.to_figure_space(1 - self.sum_incl(item))
110
+
111
+ def x2(self, item: str) -> float:
112
+ """
113
+ Higher x-coordinate in figure space of the item
114
+ """
115
+ return self.to_figure_space(1 - self.sum_upto(item))
116
+
117
+ @property
118
+ def items_right_relative(self):
119
+ """
120
+ Right (relative to the gridspec) of the panels in figure dimensions
121
+ """
122
+ return 1 - self.total
123
+
124
+ @property
125
+ def items_right(self):
126
+ """
127
+ Right of the panels in figure space
128
+ """
129
+ return self.to_figure_space(self.items_right_relative)
130
+
131
+
132
+ class composition_top_space(_composition_side_space):
133
+ """
134
+ Space for annotations above the actual composition
135
+
136
+ Ordered from the edge of the figure and going inwards
137
+ """
138
+
139
+ plot_margin: float = 0
140
+ plot_title_margin_top: float = 0
141
+ plot_title: float = 0
142
+ plot_title_margin_bottom: float = 0
143
+ plot_subtitle_margin_top: float = 0
144
+ plot_subtitle: float = 0
145
+ plot_subtitle_margin_bottom: float = 0
146
+
147
+ def _calculate(self):
148
+ items = self.items
149
+ theme = self.items.cmp.theme
150
+ geometry = self.items.geometry
151
+ W, H = theme.getp("figure_size")
152
+ F = W / H
153
+
154
+ self.plot_margin = theme.getp("plot_margin_top") * F
155
+
156
+ if items.plot_title:
157
+ m = theme.get_margin("plot_title").fig
158
+ self.plot_title_margin_top = m.t * F
159
+ self.plot_title = geometry.height(items.plot_title)
160
+ self.plot_title_margin_bottom = m.b * F
161
+
162
+ if items.plot_subtitle:
163
+ m = theme.get_margin("plot_subtitle").fig
164
+ self.plot_subtitle_margin_top = m.t * F
165
+ self.plot_subtitle = geometry.height(items.plot_subtitle)
166
+ self.plot_subtitle_margin_bottom = m.b * F
167
+
168
+ @property
169
+ def offset(self) -> float:
170
+ """
171
+ Distance from top of the figure to the top of the composition gridspec
172
+
173
+ ----------------(1, 1)
174
+ | ^ |
175
+ | |-dy |
176
+ | v |
177
+ | ---- |
178
+ | | | |
179
+ | | | |
180
+ | | | |
181
+ | ---- |
182
+ | |
183
+ (0, 0)----------------
184
+ """
185
+ return self.gridspec.bbox_relative.y1 - 1
186
+
187
+ def y1(self, item: str) -> float:
188
+ """
189
+ Lower y-coordinate in figure space of the item
190
+ """
191
+ return self.to_figure_space(1 - self.sum_incl(item))
192
+
193
+ def y2(self, item: str) -> float:
194
+ """
195
+ Higher y-coordinate in figure space of the item
196
+ """
197
+ return self.to_figure_space(1 - self.sum_upto(item))
198
+
199
+ @property
200
+ def items_top_relative(self):
201
+ """
202
+ Top (relative to the gridspec) of the panels in figure dimensions
203
+ """
204
+ return 1 - self.total
205
+
206
+ @property
207
+ def items_top(self):
208
+ """
209
+ Top of the composition items in figure space
210
+ """
211
+ return self.to_figure_space(self.items_top_relative)
212
+
213
+
214
+ class composition_bottom_space(_composition_side_space):
215
+ """
216
+ Space in the figure for artists below the panel area
217
+
218
+ Ordered from the edge of the figure and going inwards
219
+ """
220
+
221
+ plot_margin: float = 0
222
+ plot_caption_margin_bottom: float = 0
223
+ plot_caption: float = 0
224
+ plot_caption_margin_top: float = 0
225
+
226
+ def _calculate(self):
227
+ items = self.items
228
+ theme = self.items.cmp.theme
229
+ geometry = self.items.geometry
230
+ W, H = theme.getp("figure_size")
231
+ F = W / H
232
+
233
+ self.plot_margin = theme.getp("plot_margin_bottom") * F
234
+
235
+ if items.plot_caption:
236
+ m = theme.get_margin("plot_caption").fig
237
+ self.plot_caption_margin_bottom = m.b * F
238
+ self.plot_caption = geometry.height(items.plot_caption)
239
+ self.plot_caption_margin_top = m.t * F
240
+
241
+ @property
242
+ def offset(self) -> float:
243
+ """
244
+ Distance from bottom of the figure to the composition gridspec
245
+
246
+ ----------------(1, 1)
247
+ | |
248
+ | ---- |
249
+ | | | |
250
+ | | | |
251
+ | | | |
252
+ | ---- |
253
+ | ^ |
254
+ | |dy |
255
+ | v |
256
+ (0, 0)----------------
257
+ """
258
+ return self.gridspec.bbox_relative.y0
259
+
260
+ def y1(self, item: str) -> float:
261
+ """
262
+ Lower y-coordinate in figure space of the item
263
+ """
264
+ return self.to_figure_space(self.sum_upto(item))
265
+
266
+ def y2(self, item: str) -> float:
267
+ """
268
+ Higher y-coordinate in figure space of the item
269
+ """
270
+ return self.to_figure_space(self.sum_incl(item))
271
+
272
+ @property
273
+ def items_bottom_relative(self):
274
+ """
275
+ Bottom (relative to the gridspec) of the panels in figure dimensions
276
+ """
277
+ return self.total
278
+
279
+ @property
280
+ def items_bottom(self):
281
+ """
282
+ Bottom of the panels in figure space
283
+ """
284
+ return self.to_figure_space(self.items_bottom_relative)
285
+
286
+
287
+ class CompositionSideSpaces:
288
+ """
289
+ Compute the spaces required to layout the composition
290
+
291
+ This is meant for the top-most composition
292
+ """
293
+
294
+ def __init__(self, cmp: Compose):
295
+ self.cmp = cmp
296
+ self.gridspec = cmp._gridspec
297
+ self.sub_gridspec = cmp._sub_gridspec
298
+ self.items = CompositionLayoutItems(cmp)
299
+
300
+ self.l = composition_left_space(self.items)
301
+ """All subspaces to the left of the panels"""
302
+
303
+ self.r = composition_right_space(self.items)
304
+ """All subspaces to the right of the panels"""
305
+
306
+ self.t = composition_top_space(self.items)
307
+ """All subspaces above the top of the panels"""
308
+
309
+ self.b = composition_bottom_space(self.items)
310
+ """All subspaces below the bottom of the panels"""
311
+
312
+ self._create_plot_sidespaces()
313
+ self.tree = LayoutTree.create(cmp)
314
+
315
+ def arrange(self):
316
+ """
317
+ Resize composition and place artists in final positions
318
+ """
319
+ # We first resize the compositions gridspec so that the tree
320
+ # algorithms can work with the final position and total area.
321
+ self.resize_gridspec()
322
+ self.tree.arrange_layout()
323
+ self.items._move_artists(self)
324
+ self._arrange_plots()
325
+
326
+ def _arrange_plots(self):
327
+ """
328
+ Arrange all the plots in the composition
329
+ """
330
+ for plot in self.cmp.iter_plots_all():
331
+ plot._sidespaces.arrange()
332
+
333
+ def _create_plot_sidespaces(self):
334
+ """
335
+ Create sidespaces for all the plots in the composition
336
+ """
337
+ for plot in self.cmp.iter_plots_all():
338
+ plot._sidespaces = PlotSideSpaces(plot)
339
+
340
+ def resize_gridspec(self):
341
+ """
342
+ Apply the space calculations to the sub_gridspec
343
+
344
+ After calling this method, the sub_gridspec will be appropriately
345
+ sized to accomodate the content of the annotations.
346
+ """
347
+ gsparams = self.calculate_gridspec_params()
348
+ gsparams.validate()
349
+ self.sub_gridspec.update_params_and_artists(gsparams)
350
+
351
+ def calculate_gridspec_params(self) -> GridSpecParams:
352
+ """
353
+ Grid spacing between compositions w.r.t figure
354
+ """
355
+ return GridSpecParams(
356
+ self.l.items_left_relative,
357
+ self.r.items_right_relative,
358
+ self.t.items_top_relative,
359
+ self.b.items_bottom_relative,
360
+ 0,
361
+ 0,
362
+ )
363
+
364
+ @property
365
+ def horizontal_space(self) -> float:
366
+ """
367
+ Horizontal non-panel space [figure dimensions]
368
+ """
369
+ return self.l.total + self.r.total
370
+
371
+ @property
372
+ def vertical_space(self) -> float:
373
+ """
374
+ Vertical non-panel space [figure dimensions]
375
+ """
376
+ return self.t.total + self.b.total
377
+
378
+ @property
379
+ def plot_left(self) -> float:
380
+ """
381
+ Distance up to left most artist in the composition
382
+ """
383
+ try:
384
+ return min([l.plot_left for l in self.tree.left_most_spaces])
385
+ except ValueError:
386
+ return self.sub_gridspec.bbox_relative.x0
387
+
388
+ @property
389
+ def plot_right(self) -> float:
390
+ """
391
+ Distance up to right most artist in the composition
392
+ """
393
+ try:
394
+ return max([r.plot_right for r in self.tree.right_most_spaces])
395
+ except ValueError:
396
+ # When the user asks for more columns than there are
397
+ # plots/compositions to fill the columns, we get one or
398
+ # more empty columns on the right. i.e. max([])
399
+ # In that case, act as if there is an invisible plot
400
+ # whose right edge is along that of the gridspec.
401
+ return self.sub_gridspec.bbox_relative.x1
402
+
403
+ @property
404
+ def plot_bottom(self) -> float:
405
+ """
406
+ Distance up to bottom most artist in the composition
407
+ """
408
+ try:
409
+ return min([b.plot_bottom for b in self.tree.bottom_most_spaces])
410
+ except ValueError:
411
+ return self.sub_gridspec.bbox_relative.y0
412
+
413
+ @property
414
+ def plot_top(self) -> float:
415
+ """
416
+ Distance upto top most artist in the composition
417
+ """
418
+ try:
419
+ return max([t.plot_top for t in self.tree.top_most_spaces])
420
+ except ValueError:
421
+ return self.sub_gridspec.bbox_relative.y1
422
+
423
+ @property
424
+ def panel_left(self) -> float:
425
+ """
426
+ Distance up to left most artist in the composition
427
+ """
428
+ try:
429
+ return min([l.panel_left for l in self.tree.left_most_spaces])
430
+ except ValueError:
431
+ return self.sub_gridspec.bbox_relative.x0
432
+
433
+ @property
434
+ def panel_right(self) -> float:
435
+ """
436
+ Distance up to right most artist in the composition
437
+ """
438
+ try:
439
+ return max([r.panel_right for r in self.tree.right_most_spaces])
440
+ except ValueError:
441
+ return self.sub_gridspec.bbox_relative.x1
442
+
443
+ @property
444
+ def panel_bottom(self) -> float:
445
+ """
446
+ Distance up to bottom most artist in the composition
447
+ """
448
+ try:
449
+ return min([b.panel_bottom for b in self.tree.bottom_most_spaces])
450
+ except ValueError:
451
+ return self.sub_gridspec.bbox_relative.y0
452
+
453
+ @property
454
+ def panel_top(self) -> float:
455
+ """
456
+ Distance upto top most artist in the composition
457
+ """
458
+ try:
459
+ return max([t.panel_top for t in self.tree.top_most_spaces])
460
+ except ValueError:
461
+ return self.sub_gridspec.bbox_relative.y1