plotnine 0.14.4__py3-none-any.whl → 0.15.0.dev1__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.
- plotnine/__init__.py +31 -37
- plotnine/_mpl/gridspec.py +265 -0
- plotnine/_mpl/layout_manager/__init__.py +6 -0
- plotnine/_mpl/layout_manager/_engine.py +87 -0
- plotnine/_mpl/layout_manager/_layout_items.py +775 -0
- plotnine/_mpl/layout_manager/_layout_tree.py +625 -0
- plotnine/_mpl/layout_manager/_spaces.py +1007 -0
- plotnine/_mpl/utils.py +78 -10
- plotnine/_utils/__init__.py +4 -4
- plotnine/_utils/dev.py +45 -27
- plotnine/animation.py +1 -1
- plotnine/coords/coord_trans.py +1 -1
- plotnine/data/__init__.py +12 -8
- plotnine/doctools.py +1 -1
- plotnine/facets/facet.py +30 -39
- plotnine/facets/facet_grid.py +14 -6
- plotnine/facets/facet_wrap.py +3 -5
- plotnine/facets/strips.py +2 -7
- plotnine/geoms/geom_crossbar.py +2 -3
- plotnine/geoms/geom_path.py +1 -1
- plotnine/geoms/geom_text.py +3 -1
- plotnine/ggplot.py +94 -65
- plotnine/guides/guide.py +10 -8
- plotnine/guides/guide_colorbar.py +3 -3
- plotnine/guides/guide_legend.py +5 -5
- plotnine/guides/guides.py +3 -3
- plotnine/iapi.py +1 -0
- plotnine/labels.py +5 -0
- plotnine/options.py +14 -7
- plotnine/plot_composition/__init__.py +10 -0
- plotnine/plot_composition/_compose.py +427 -0
- plotnine/plot_composition/_plotspec.py +50 -0
- plotnine/plot_composition/_spacer.py +32 -0
- plotnine/positions/position_dodge.py +1 -1
- plotnine/positions/position_dodge2.py +1 -1
- plotnine/positions/position_stack.py +1 -2
- plotnine/qplot.py +1 -2
- plotnine/scales/__init__.py +0 -6
- plotnine/scales/scale.py +1 -1
- plotnine/stats/binning.py +1 -1
- plotnine/stats/smoothers.py +3 -5
- plotnine/stats/stat_density.py +1 -1
- plotnine/stats/stat_qq_line.py +1 -1
- plotnine/stats/stat_sina.py +1 -1
- plotnine/themes/elements/__init__.py +2 -0
- plotnine/themes/elements/element_text.py +34 -24
- plotnine/themes/elements/margin.py +73 -60
- plotnine/themes/targets.py +2 -0
- plotnine/themes/theme.py +13 -7
- plotnine/themes/theme_gray.py +27 -31
- plotnine/themes/theme_matplotlib.py +25 -28
- plotnine/themes/theme_seaborn.py +31 -34
- plotnine/themes/theme_void.py +17 -26
- plotnine/themes/themeable.py +286 -153
- {plotnine-0.14.4.dist-info → plotnine-0.15.0.dev1.dist-info}/METADATA +4 -3
- {plotnine-0.14.4.dist-info → plotnine-0.15.0.dev1.dist-info}/RECORD +59 -52
- {plotnine-0.14.4.dist-info → plotnine-0.15.0.dev1.dist-info}/WHEEL +1 -1
- plotnine/_mpl/_plot_side_space.py +0 -888
- plotnine/_mpl/_plotnine_tight_layout.py +0 -293
- plotnine/_mpl/layout_engine.py +0 -110
- {plotnine-0.14.4.dist-info → plotnine-0.15.0.dev1.dist-info/licenses}/LICENSE +0 -0
- {plotnine-0.14.4.dist-info → plotnine-0.15.0.dev1.dist-info}/top_level.txt +0 -0
plotnine/__init__.py
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
# Do not edit this file by hand.
|
|
2
|
+
#
|
|
3
|
+
# Generate it using:
|
|
4
|
+
#
|
|
5
|
+
# $ python -c 'from plotnine._utils import dev; print(dev.get_init_py())'
|
|
6
|
+
|
|
1
7
|
from importlib.metadata import PackageNotFoundError, version
|
|
2
8
|
|
|
3
9
|
try:
|
|
@@ -9,12 +15,6 @@ finally:
|
|
|
9
15
|
del version
|
|
10
16
|
del PackageNotFoundError
|
|
11
17
|
|
|
12
|
-
# Imports below are generated with the following command:
|
|
13
|
-
#
|
|
14
|
-
# python -c "from plotnine._utils.dev import get_plotnine_all; get_plotnine_all()" # noqa: E501
|
|
15
|
-
#
|
|
16
|
-
# added to the clipboard. Run it and paste the results. Do not edit by hand.
|
|
17
|
-
|
|
18
18
|
from .coords import (
|
|
19
19
|
coord_cartesian,
|
|
20
20
|
coord_equal,
|
|
@@ -180,12 +180,10 @@ from .scales import (
|
|
|
180
180
|
scale_fill_manual,
|
|
181
181
|
scale_fill_ordinal,
|
|
182
182
|
scale_linetype,
|
|
183
|
-
scale_linetype_continuous,
|
|
184
183
|
scale_linetype_discrete,
|
|
185
184
|
scale_linetype_identity,
|
|
186
185
|
scale_linetype_manual,
|
|
187
186
|
scale_shape,
|
|
188
|
-
scale_shape_continuous,
|
|
189
187
|
scale_shape_discrete,
|
|
190
188
|
scale_shape_identity,
|
|
191
189
|
scale_shape_manual,
|
|
@@ -200,7 +198,6 @@ from .scales import (
|
|
|
200
198
|
scale_size_radius,
|
|
201
199
|
scale_stroke,
|
|
202
200
|
scale_stroke_continuous,
|
|
203
|
-
scale_stroke_discrete,
|
|
204
201
|
scale_x_continuous,
|
|
205
202
|
scale_x_date,
|
|
206
203
|
scale_x_datetime,
|
|
@@ -279,23 +276,27 @@ from .watermark import (
|
|
|
279
276
|
)
|
|
280
277
|
|
|
281
278
|
__all__ = (
|
|
279
|
+
"aes",
|
|
280
|
+
"after_scale",
|
|
281
|
+
"after_stat",
|
|
282
|
+
"annotate",
|
|
283
|
+
"annotation_logticks",
|
|
284
|
+
"annotation_stripes",
|
|
285
|
+
"arrow",
|
|
286
|
+
"as_labeller",
|
|
282
287
|
"coord_cartesian",
|
|
283
288
|
"coord_equal",
|
|
284
289
|
"coord_fixed",
|
|
285
290
|
"coord_flip",
|
|
286
291
|
"coord_trans",
|
|
287
|
-
"
|
|
292
|
+
"element_blank",
|
|
293
|
+
"element_line",
|
|
294
|
+
"element_rect",
|
|
295
|
+
"element_text",
|
|
296
|
+
"expand_limits",
|
|
288
297
|
"facet_grid",
|
|
289
298
|
"facet_null",
|
|
290
299
|
"facet_wrap",
|
|
291
|
-
"label_both",
|
|
292
|
-
"label_context",
|
|
293
|
-
"label_value",
|
|
294
|
-
"labeller",
|
|
295
|
-
"annotate",
|
|
296
|
-
"annotation_logticks",
|
|
297
|
-
"annotation_stripes",
|
|
298
|
-
"arrow",
|
|
299
300
|
"geom_abline",
|
|
300
301
|
"geom_area",
|
|
301
302
|
"geom_bar",
|
|
@@ -342,19 +343,17 @@ __all__ = (
|
|
|
342
343
|
"geom_vline",
|
|
343
344
|
"ggplot",
|
|
344
345
|
"ggsave",
|
|
345
|
-
"
|
|
346
|
+
"ggtitle",
|
|
346
347
|
"guide_colorbar",
|
|
347
348
|
"guide_colourbar",
|
|
348
349
|
"guide_legend",
|
|
349
350
|
"guides",
|
|
350
|
-
"
|
|
351
|
+
"label_both",
|
|
352
|
+
"label_context",
|
|
353
|
+
"label_value",
|
|
354
|
+
"labeller",
|
|
351
355
|
"labs",
|
|
352
|
-
"
|
|
353
|
-
"ylab",
|
|
354
|
-
"aes",
|
|
355
|
-
"after_scale",
|
|
356
|
-
"after_stat",
|
|
357
|
-
"stage",
|
|
356
|
+
"lims",
|
|
358
357
|
"position_dodge",
|
|
359
358
|
"position_dodge2",
|
|
360
359
|
"position_fill",
|
|
@@ -364,8 +363,7 @@ __all__ = (
|
|
|
364
363
|
"position_nudge",
|
|
365
364
|
"position_stack",
|
|
366
365
|
"qplot",
|
|
367
|
-
"
|
|
368
|
-
"lims",
|
|
366
|
+
"save_as_pdf_pages",
|
|
369
367
|
"scale_alpha",
|
|
370
368
|
"scale_alpha_continuous",
|
|
371
369
|
"scale_alpha_datetime",
|
|
@@ -425,12 +423,10 @@ __all__ = (
|
|
|
425
423
|
"scale_fill_manual",
|
|
426
424
|
"scale_fill_ordinal",
|
|
427
425
|
"scale_linetype",
|
|
428
|
-
"scale_linetype_continuous",
|
|
429
426
|
"scale_linetype_discrete",
|
|
430
427
|
"scale_linetype_identity",
|
|
431
428
|
"scale_linetype_manual",
|
|
432
429
|
"scale_shape",
|
|
433
|
-
"scale_shape_continuous",
|
|
434
430
|
"scale_shape_discrete",
|
|
435
431
|
"scale_shape_identity",
|
|
436
432
|
"scale_shape_manual",
|
|
@@ -445,7 +441,6 @@ __all__ = (
|
|
|
445
441
|
"scale_size_radius",
|
|
446
442
|
"scale_stroke",
|
|
447
443
|
"scale_stroke_continuous",
|
|
448
|
-
"scale_stroke_discrete",
|
|
449
444
|
"scale_x_continuous",
|
|
450
445
|
"scale_x_date",
|
|
451
446
|
"scale_x_datetime",
|
|
@@ -464,8 +459,7 @@ __all__ = (
|
|
|
464
459
|
"scale_y_sqrt",
|
|
465
460
|
"scale_y_symlog",
|
|
466
461
|
"scale_y_timedelta",
|
|
467
|
-
"
|
|
468
|
-
"ylim",
|
|
462
|
+
"stage",
|
|
469
463
|
"stat_bin",
|
|
470
464
|
"stat_bin2d",
|
|
471
465
|
"stat_bin_2d",
|
|
@@ -508,9 +502,9 @@ __all__ = (
|
|
|
508
502
|
"theme_update",
|
|
509
503
|
"theme_void",
|
|
510
504
|
"theme_xkcd",
|
|
511
|
-
"element_blank",
|
|
512
|
-
"element_line",
|
|
513
|
-
"element_rect",
|
|
514
|
-
"element_text",
|
|
515
505
|
"watermark",
|
|
506
|
+
"xlab",
|
|
507
|
+
"xlim",
|
|
508
|
+
"ylab",
|
|
509
|
+
"ylim",
|
|
516
510
|
)
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import asdict
|
|
4
|
+
from functools import cached_property
|
|
5
|
+
from typing import TYPE_CHECKING, cast
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from matplotlib.gridspec import GridSpecBase, SubplotParams
|
|
9
|
+
except ImportError:
|
|
10
|
+
# MPL 3.8
|
|
11
|
+
from matplotlib.figure import SubplotParams
|
|
12
|
+
from matplotlib.gridspec import GridSpecBase
|
|
13
|
+
|
|
14
|
+
from matplotlib.transforms import Bbox, BboxTransformTo, TransformedBbox
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from matplotlib.figure import Figure
|
|
18
|
+
from matplotlib.gridspec import SubplotSpec
|
|
19
|
+
from matplotlib.patches import Rectangle
|
|
20
|
+
from matplotlib.transforms import Transform
|
|
21
|
+
|
|
22
|
+
from plotnine._mpl.layout_manager._spaces import GridSpecParams
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class p9GridSpec(GridSpecBase):
|
|
26
|
+
"""
|
|
27
|
+
Gridspec for plotnine plots
|
|
28
|
+
|
|
29
|
+
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.
|
|
32
|
+
|
|
33
|
+
This gridspec can also be initialised while contained/nested in a given
|
|
34
|
+
subplot.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
nest_into :
|
|
39
|
+
If given, this gridspec will be contained in the subplot.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
_subplot_params: SubplotParams
|
|
43
|
+
"""
|
|
44
|
+
The subplot spacing parameters of this gridspec
|
|
45
|
+
|
|
46
|
+
These values are relative to where (figure or subplot) that the
|
|
47
|
+
gridspec is contained. Use .get_subplot_params to get the absolute
|
|
48
|
+
values (those in figure coordinates).
|
|
49
|
+
"""
|
|
50
|
+
_nested_gridspecs: list[p9GridSpec]
|
|
51
|
+
"""
|
|
52
|
+
All gridspecs that are nested into any of the subplots of this one
|
|
53
|
+
"""
|
|
54
|
+
_patch: Rectangle
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
nrows,
|
|
59
|
+
ncols,
|
|
60
|
+
figure: Figure,
|
|
61
|
+
*,
|
|
62
|
+
width_ratios=None,
|
|
63
|
+
height_ratios=None,
|
|
64
|
+
nest_into: SubplotSpec | None = None,
|
|
65
|
+
):
|
|
66
|
+
self.figure = figure
|
|
67
|
+
self._nested_gridspecs = []
|
|
68
|
+
|
|
69
|
+
super().__init__(
|
|
70
|
+
nrows,
|
|
71
|
+
ncols,
|
|
72
|
+
width_ratios=width_ratios,
|
|
73
|
+
height_ratios=height_ratios,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if nest_into:
|
|
77
|
+
self._parent_subplot_spec = nest_into
|
|
78
|
+
# MPL GridSpecBase expects only the subclasses that will be nested
|
|
79
|
+
# to have the .get_topmost_subplotspec method.
|
|
80
|
+
self.get_topmost_subplotspec = self._get_topmost_subplotspec
|
|
81
|
+
|
|
82
|
+
# Register this gridspec as nested
|
|
83
|
+
gs = cast("p9GridSpec", nest_into.get_gridspec())
|
|
84
|
+
gs._nested_gridspecs.append(self)
|
|
85
|
+
|
|
86
|
+
self.update(
|
|
87
|
+
left=0,
|
|
88
|
+
bottom=0,
|
|
89
|
+
top=1,
|
|
90
|
+
right=1,
|
|
91
|
+
wspace=0,
|
|
92
|
+
hspace=0,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def patch(self) -> Rectangle:
|
|
97
|
+
"""
|
|
98
|
+
Background patch for the whole gridspec
|
|
99
|
+
"""
|
|
100
|
+
return self._patch
|
|
101
|
+
|
|
102
|
+
@patch.setter
|
|
103
|
+
def patch(self, value: Rectangle):
|
|
104
|
+
"""
|
|
105
|
+
Set value and update position
|
|
106
|
+
"""
|
|
107
|
+
self._patch = value
|
|
108
|
+
self._update_patch_position()
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def nested(self):
|
|
112
|
+
"""
|
|
113
|
+
Return True if this gridspec is nested
|
|
114
|
+
"""
|
|
115
|
+
return hasattr(self, "_parent_subplot_spec")
|
|
116
|
+
|
|
117
|
+
def update(
|
|
118
|
+
self,
|
|
119
|
+
left=None,
|
|
120
|
+
bottom=None,
|
|
121
|
+
right=None,
|
|
122
|
+
top=None,
|
|
123
|
+
wspace=None,
|
|
124
|
+
hspace=None,
|
|
125
|
+
):
|
|
126
|
+
"""
|
|
127
|
+
Update the gridpec's suplot parameters and all that depend on them
|
|
128
|
+
"""
|
|
129
|
+
if not hasattr(self, "_subplot_params"):
|
|
130
|
+
self._subplot_params = SubplotParams()
|
|
131
|
+
|
|
132
|
+
self._subplot_params.update(
|
|
133
|
+
left=left,
|
|
134
|
+
bottom=bottom,
|
|
135
|
+
top=top,
|
|
136
|
+
right=right,
|
|
137
|
+
wspace=wspace,
|
|
138
|
+
hspace=hspace,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def _update_patch_position(self):
|
|
142
|
+
"""
|
|
143
|
+
Update the position and size of the patch
|
|
144
|
+
|
|
145
|
+
The patch position should be updated whenever the subplot
|
|
146
|
+
parameters change.
|
|
147
|
+
"""
|
|
148
|
+
if not hasattr(self, "_patch"):
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
ss_bbox = self[0].get_position(None) # pyright: ignore[reportArgumentType]
|
|
152
|
+
self.patch.set_xy((ss_bbox.x0, ss_bbox.y0))
|
|
153
|
+
self.patch.set_width(ss_bbox.width)
|
|
154
|
+
self.patch.set_height(ss_bbox.height)
|
|
155
|
+
|
|
156
|
+
def _update_axes_position(self):
|
|
157
|
+
"""
|
|
158
|
+
Update the position of the axes in this gridspec
|
|
159
|
+
"""
|
|
160
|
+
for ax in self.figure.axes:
|
|
161
|
+
if ss := ax.get_subplotspec():
|
|
162
|
+
ax._set_position(ss.get_position(self)) # pyright: ignore[reportAttributeAccessIssue, reportArgumentType]
|
|
163
|
+
|
|
164
|
+
def _update_artists(self):
|
|
165
|
+
"""
|
|
166
|
+
Update the artist positions that depend on this gridspec
|
|
167
|
+
"""
|
|
168
|
+
self._update_patch_position()
|
|
169
|
+
self._update_axes_position()
|
|
170
|
+
for gs in self._nested_gridspecs:
|
|
171
|
+
gs._update_artists()
|
|
172
|
+
|
|
173
|
+
def layout(self, gsparams: GridSpecParams):
|
|
174
|
+
"""
|
|
175
|
+
Update the layout of the gridspec
|
|
176
|
+
"""
|
|
177
|
+
self.update(**asdict(gsparams))
|
|
178
|
+
self._update_artists()
|
|
179
|
+
|
|
180
|
+
def get_subplot_params(self, figure=None) -> SubplotParams:
|
|
181
|
+
"""
|
|
182
|
+
Return the subplot parameters (in figure coordinates) for the gridspec
|
|
183
|
+
"""
|
|
184
|
+
params = self._subplot_params
|
|
185
|
+
|
|
186
|
+
if not self.nested:
|
|
187
|
+
return params
|
|
188
|
+
|
|
189
|
+
# When the gridspec is nested the subplot params of this gridspec
|
|
190
|
+
# are relative to the position of parent subplot. We want values that
|
|
191
|
+
# are relative to the figure, so we add these param values as offsets
|
|
192
|
+
# to the position of the parent subplot.
|
|
193
|
+
parent_bbox = self._parent_subplot_spec.get_position(figure) # pyright: ignore
|
|
194
|
+
_left, _bottom, _right, _top = parent_bbox.extents
|
|
195
|
+
|
|
196
|
+
left = _left + params.left
|
|
197
|
+
bottom = _bottom + params.bottom
|
|
198
|
+
right = _right - (1 - params.right)
|
|
199
|
+
top = _top - (1 - params.top)
|
|
200
|
+
|
|
201
|
+
return SubplotParams(
|
|
202
|
+
left=left,
|
|
203
|
+
bottom=bottom,
|
|
204
|
+
right=right,
|
|
205
|
+
top=top,
|
|
206
|
+
wspace=params.wspace,
|
|
207
|
+
hspace=params.hspace,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
def _get_topmost_subplotspec(self) -> SubplotSpec:
|
|
211
|
+
"""
|
|
212
|
+
Return the topmost `.SubplotSpec` instance associated with the subplot.
|
|
213
|
+
|
|
214
|
+
This method starts with an underscore so that mpl's GridSpecBase does
|
|
215
|
+
not think that any/all instances of this class are nested. It is then
|
|
216
|
+
only dynamically assigned (without the underscore) to an instance of
|
|
217
|
+
this class when it is nested into a subplot.
|
|
218
|
+
"""
|
|
219
|
+
return self._parent_subplot_spec.get_topmost_subplotspec()
|
|
220
|
+
|
|
221
|
+
@cached_property
|
|
222
|
+
def parent_gridspec(self) -> p9GridSpec | None:
|
|
223
|
+
if self.nested and (ss := self._parent_subplot_spec):
|
|
224
|
+
return ss.get_gridspec() # pyright: ignore[reportReturnType]
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def bbox_relative(self):
|
|
228
|
+
"""
|
|
229
|
+
Bounding box for the gridspec relative to the figure
|
|
230
|
+
|
|
231
|
+
This bbox is in figure coordinates.
|
|
232
|
+
"""
|
|
233
|
+
params = self.get_subplot_params()
|
|
234
|
+
return Bbox.from_extents(
|
|
235
|
+
params.left, params.bottom, params.right, params.top
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
@property
|
|
239
|
+
def bbox(self):
|
|
240
|
+
"""
|
|
241
|
+
Bounding box of the gridspec
|
|
242
|
+
|
|
243
|
+
This bbox is in display coordinates.
|
|
244
|
+
"""
|
|
245
|
+
return TransformedBbox(self.bbox_relative, self.figure.transSubfigure)
|
|
246
|
+
|
|
247
|
+
def to_transform(self) -> Transform:
|
|
248
|
+
"""
|
|
249
|
+
Return transform of this gridspec
|
|
250
|
+
|
|
251
|
+
Where:
|
|
252
|
+
- (0, 0) is the bottom left of the gridspec
|
|
253
|
+
- (1, 1) is the top right of the gridspec
|
|
254
|
+
|
|
255
|
+
The output of this transform is in the display units of the figure.
|
|
256
|
+
"""
|
|
257
|
+
return BboxTransformTo(self.bbox)
|
|
258
|
+
|
|
259
|
+
def set_height_ratios(self, height_ratios):
|
|
260
|
+
super().set_height_ratios(height_ratios)
|
|
261
|
+
self._update_artists()
|
|
262
|
+
|
|
263
|
+
def set_width_ratios(self, width_ratios):
|
|
264
|
+
super().set_width_ratios(width_ratios)
|
|
265
|
+
self._update_artists()
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
from warnings import warn
|
|
5
|
+
|
|
6
|
+
from matplotlib.layout_engine import LayoutEngine
|
|
7
|
+
|
|
8
|
+
from ...exceptions import PlotnineWarning
|
|
9
|
+
from ._layout_tree import LayoutTree
|
|
10
|
+
from ._spaces import LayoutSpaces
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from matplotlib.figure import Figure
|
|
14
|
+
|
|
15
|
+
from plotnine import ggplot
|
|
16
|
+
from plotnine.plot_composition import Compose
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PlotnineLayoutEngine(LayoutEngine):
|
|
20
|
+
"""
|
|
21
|
+
Implement geometry management for plotnine plots
|
|
22
|
+
|
|
23
|
+
This layout manager automatically adjusts the location of
|
|
24
|
+
objects placed around the plot panels and the subplot
|
|
25
|
+
spacing parameters so that the plot fits cleanly within
|
|
26
|
+
the figure area.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
_adjust_compatible = True
|
|
30
|
+
_colorbar_gridspec = False
|
|
31
|
+
|
|
32
|
+
def __init__(self, plot: ggplot):
|
|
33
|
+
self.plot = plot
|
|
34
|
+
self.theme = plot.theme
|
|
35
|
+
|
|
36
|
+
def execute(self, fig: Figure):
|
|
37
|
+
from contextlib import nullcontext
|
|
38
|
+
|
|
39
|
+
renderer = fig._get_renderer() # pyright: ignore[reportAttributeAccessIssue]
|
|
40
|
+
|
|
41
|
+
with getattr(renderer, "_draw_disabled", nullcontext)():
|
|
42
|
+
spaces = LayoutSpaces(self.plot)
|
|
43
|
+
|
|
44
|
+
gsparams = spaces.get_gridspec_params()
|
|
45
|
+
self.plot.facet._panels_gridspec.layout(gsparams)
|
|
46
|
+
spaces.items._adjust_positions(spaces)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class PlotnineCompositionLayoutEngine(LayoutEngine):
|
|
50
|
+
"""
|
|
51
|
+
Layout Manager for Plotnine Composition
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
_adjust_compatible = True
|
|
55
|
+
_colorbar_gridspec = False
|
|
56
|
+
|
|
57
|
+
def __init__(self, composition: Compose):
|
|
58
|
+
self.composition = composition
|
|
59
|
+
|
|
60
|
+
def execute(self, fig: Figure):
|
|
61
|
+
from contextlib import nullcontext
|
|
62
|
+
|
|
63
|
+
renderer = fig._get_renderer() # pyright: ignore[reportAttributeAccessIssue]
|
|
64
|
+
|
|
65
|
+
# Caculate the space taken up by all plot artists
|
|
66
|
+
lookup_spaces: dict[ggplot, LayoutSpaces] = {}
|
|
67
|
+
with getattr(renderer, "_draw_disabled", nullcontext)():
|
|
68
|
+
for ps in self.composition.plotspecs:
|
|
69
|
+
lookup_spaces[ps.plot] = LayoutSpaces(ps.plot)
|
|
70
|
+
|
|
71
|
+
# Adjust the size and placements of the plots
|
|
72
|
+
tree = LayoutTree.create(self.composition, lookup_spaces)
|
|
73
|
+
tree.harmonise()
|
|
74
|
+
|
|
75
|
+
# Set the final positions of the artists in each plot
|
|
76
|
+
for plot, spaces in lookup_spaces.items():
|
|
77
|
+
gsparams = spaces.get_gridspec_params()
|
|
78
|
+
if not gsparams.valid:
|
|
79
|
+
warn(
|
|
80
|
+
"The layout manager failed, the figure size is too small "
|
|
81
|
+
"to contain all the plots. Use theme() increase the "
|
|
82
|
+
"figure size and/or reduce the size of the texts.",
|
|
83
|
+
PlotnineWarning,
|
|
84
|
+
)
|
|
85
|
+
break
|
|
86
|
+
plot.facet._panels_gridspec.layout(gsparams)
|
|
87
|
+
spaces.items._adjust_positions(spaces)
|