plotnine 0.14.5__py3-none-any.whl → 0.15.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 (92) hide show
  1. plotnine/__init__.py +31 -37
  2. plotnine/_mpl/gridspec.py +265 -0
  3. plotnine/_mpl/layout_manager/__init__.py +6 -0
  4. plotnine/_mpl/layout_manager/_engine.py +87 -0
  5. plotnine/_mpl/layout_manager/_layout_items.py +957 -0
  6. plotnine/_mpl/layout_manager/_layout_tree.py +905 -0
  7. plotnine/_mpl/layout_manager/_spaces.py +1154 -0
  8. plotnine/_mpl/patches.py +70 -34
  9. plotnine/_mpl/text.py +159 -37
  10. plotnine/_mpl/utils.py +78 -10
  11. plotnine/_utils/__init__.py +35 -9
  12. plotnine/_utils/dev.py +45 -27
  13. plotnine/_utils/yippie.py +115 -0
  14. plotnine/animation.py +1 -1
  15. plotnine/coords/coord.py +3 -3
  16. plotnine/coords/coord_trans.py +1 -1
  17. plotnine/data/__init__.py +43 -8
  18. plotnine/data/anscombe-quartet.csv +45 -0
  19. plotnine/doctools.py +2 -2
  20. plotnine/facets/facet.py +34 -43
  21. plotnine/facets/facet_grid.py +14 -6
  22. plotnine/facets/facet_wrap.py +3 -5
  23. plotnine/facets/strips.py +20 -33
  24. plotnine/geoms/annotate.py +3 -3
  25. plotnine/geoms/annotation_logticks.py +2 -0
  26. plotnine/geoms/annotation_stripes.py +2 -0
  27. plotnine/geoms/geom.py +3 -3
  28. plotnine/geoms/geom_bar.py +10 -2
  29. plotnine/geoms/geom_col.py +6 -0
  30. plotnine/geoms/geom_crossbar.py +2 -3
  31. plotnine/geoms/geom_path.py +2 -2
  32. plotnine/geoms/geom_violin.py +24 -7
  33. plotnine/ggplot.py +95 -66
  34. plotnine/guides/guide.py +19 -20
  35. plotnine/guides/guide_colorbar.py +6 -6
  36. plotnine/guides/guide_legend.py +15 -16
  37. plotnine/guides/guides.py +8 -8
  38. plotnine/helpers.py +49 -0
  39. plotnine/iapi.py +33 -7
  40. plotnine/labels.py +8 -3
  41. plotnine/layer.py +4 -4
  42. plotnine/mapping/_env.py +2 -2
  43. plotnine/mapping/_eval_environment.py +85 -0
  44. plotnine/mapping/aes.py +14 -30
  45. plotnine/mapping/evaluation.py +7 -65
  46. plotnine/options.py +14 -7
  47. plotnine/plot_composition/__init__.py +10 -0
  48. plotnine/plot_composition/_compose.py +462 -0
  49. plotnine/plot_composition/_plotspec.py +50 -0
  50. plotnine/plot_composition/_spacer.py +32 -0
  51. plotnine/positions/position_dodge.py +1 -1
  52. plotnine/positions/position_dodge2.py +1 -1
  53. plotnine/positions/position_stack.py +1 -2
  54. plotnine/qplot.py +1 -2
  55. plotnine/scales/__init__.py +0 -6
  56. plotnine/scales/limits.py +7 -7
  57. plotnine/scales/scale.py +4 -4
  58. plotnine/scales/scale_continuous.py +2 -1
  59. plotnine/scales/scale_identity.py +10 -2
  60. plotnine/scales/scale_manual.py +6 -2
  61. plotnine/stats/binning.py +5 -2
  62. plotnine/stats/smoothers.py +3 -5
  63. plotnine/stats/stat.py +3 -3
  64. plotnine/stats/stat_bindot.py +1 -3
  65. plotnine/stats/stat_density.py +2 -2
  66. plotnine/stats/stat_qq_line.py +1 -1
  67. plotnine/stats/stat_sina.py +34 -1
  68. plotnine/themes/elements/__init__.py +3 -0
  69. plotnine/themes/elements/element_text.py +35 -24
  70. plotnine/themes/elements/margin.py +137 -61
  71. plotnine/themes/targets.py +3 -1
  72. plotnine/themes/theme.py +21 -7
  73. plotnine/themes/theme_538.py +0 -1
  74. plotnine/themes/theme_bw.py +0 -1
  75. plotnine/themes/theme_dark.py +0 -1
  76. plotnine/themes/theme_gray.py +32 -34
  77. plotnine/themes/theme_light.py +1 -1
  78. plotnine/themes/theme_matplotlib.py +28 -31
  79. plotnine/themes/theme_seaborn.py +36 -36
  80. plotnine/themes/theme_void.py +25 -27
  81. plotnine/themes/theme_xkcd.py +0 -1
  82. plotnine/themes/themeable.py +369 -169
  83. plotnine/typing.py +3 -3
  84. plotnine/watermark.py +3 -3
  85. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/METADATA +8 -5
  86. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/RECORD +89 -78
  87. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/WHEEL +1 -1
  88. plotnine/_mpl/_plot_side_space.py +0 -888
  89. plotnine/_mpl/_plotnine_tight_layout.py +0 -293
  90. plotnine/_mpl/layout_engine.py +0 -110
  91. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info/licenses}/LICENSE +0 -0
  92. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.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
- "as_labeller",
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
- "save_as_pdf_pages",
346
+ "ggtitle",
346
347
  "guide_colorbar",
347
348
  "guide_colourbar",
348
349
  "guide_legend",
349
350
  "guides",
350
- "ggtitle",
351
+ "label_both",
352
+ "label_context",
353
+ "label_value",
354
+ "labeller",
351
355
  "labs",
352
- "xlab",
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
- "expand_limits",
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
- "xlim",
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,6 @@
1
+ from ._engine import PlotnineCompositionLayoutEngine, PlotnineLayoutEngine
2
+
3
+ __all__ = (
4
+ "PlotnineLayoutEngine",
5
+ "PlotnineCompositionLayoutEngine",
6
+ )
@@ -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)