plotnine 0.15.0a6__py3-none-any.whl → 0.15.0a8__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/_mpl/layout_manager/_engine.py +2 -2
- plotnine/_mpl/layout_manager/_layout_items.py +6 -2
- plotnine/_mpl/layout_manager/_layout_tree.py +219 -58
- plotnine/_mpl/layout_manager/_spaces.py +98 -28
- plotnine/_mpl/text.py +1 -7
- plotnine/composition/__init__.py +2 -2
- plotnine/composition/_beside.py +12 -4
- plotnine/composition/{_arrange.py → _compose.py} +118 -38
- plotnine/composition/_plot_spacer.py +6 -0
- plotnine/composition/_stack.py +12 -4
- plotnine/geoms/geom_bar.py +1 -0
- plotnine/geoms/geom_bin_2d.py +4 -0
- plotnine/geoms/geom_boxplot.py +4 -0
- plotnine/geoms/geom_count.py +4 -0
- plotnine/geoms/geom_density_2d.py +4 -0
- plotnine/geoms/geom_dotplot.py +1 -1
- plotnine/geoms/geom_histogram.py +1 -1
- plotnine/geoms/geom_pointdensity.py +4 -0
- plotnine/geoms/geom_qq.py +4 -0
- plotnine/geoms/geom_qq_line.py +4 -0
- plotnine/geoms/geom_quantile.py +4 -0
- plotnine/geoms/geom_sina.py +1 -1
- plotnine/geoms/geom_smooth.py +4 -0
- plotnine/geoms/geom_violin.py +4 -0
- plotnine/ggplot.py +4 -4
- plotnine/stats/stat_bin.py +4 -0
- plotnine/stats/stat_bin_2d.py +4 -0
- plotnine/stats/stat_bindot.py +1 -0
- plotnine/stats/stat_boxplot.py +1 -1
- plotnine/stats/stat_count.py +1 -0
- plotnine/stats/stat_density.py +1 -1
- plotnine/stats/stat_density_2d.py +1 -0
- plotnine/stats/stat_ecdf.py +1 -1
- plotnine/stats/stat_ellipse.py +4 -0
- plotnine/stats/stat_function.py +4 -0
- plotnine/stats/stat_hull.py +4 -0
- plotnine/stats/stat_identity.py +4 -0
- plotnine/stats/stat_pointdensity.py +1 -0
- plotnine/stats/stat_qq.py +1 -0
- plotnine/stats/stat_qq_line.py +1 -0
- plotnine/stats/stat_quantile.py +1 -1
- plotnine/stats/stat_sina.py +1 -1
- plotnine/stats/stat_smooth.py +1 -0
- plotnine/stats/stat_sum.py +4 -0
- plotnine/stats/stat_summary.py +1 -1
- plotnine/stats/stat_summary_bin.py +1 -1
- plotnine/stats/stat_unique.py +4 -0
- plotnine/stats/stat_ydensity.py +1 -1
- plotnine/themes/theme.py +1 -1
- plotnine/themes/theme_void.py +1 -7
- {plotnine-0.15.0a6.dist-info → plotnine-0.15.0a8.dist-info}/METADATA +1 -1
- {plotnine-0.15.0a6.dist-info → plotnine-0.15.0a8.dist-info}/RECORD +55 -55
- {plotnine-0.15.0a6.dist-info → plotnine-0.15.0a8.dist-info}/WHEEL +0 -0
- {plotnine-0.15.0a6.dist-info → plotnine-0.15.0a8.dist-info}/licenses/LICENSE +0 -0
- {plotnine-0.15.0a6.dist-info → plotnine-0.15.0a8.dist-info}/top_level.txt +0 -0
|
@@ -16,6 +16,7 @@ from dataclasses import dataclass, field, fields
|
|
|
16
16
|
from functools import cached_property
|
|
17
17
|
from typing import TYPE_CHECKING, cast
|
|
18
18
|
|
|
19
|
+
from plotnine.exceptions import PlotnineError
|
|
19
20
|
from plotnine.facets import facet_grid, facet_null, facet_wrap
|
|
20
21
|
|
|
21
22
|
from ._layout_items import LayoutItems
|
|
@@ -233,7 +234,7 @@ class _side_spaces(ABC):
|
|
|
233
234
|
"""
|
|
234
235
|
The width of the tag including the margins
|
|
235
236
|
|
|
236
|
-
The value is zero
|
|
237
|
+
The value is zero except if all these are true:
|
|
237
238
|
- The tag is in the margin `theme(plot_tag_position = "margin")`
|
|
238
239
|
- The tag at one one of the the following locations;
|
|
239
240
|
left, right, topleft, topright, bottomleft or bottomright
|
|
@@ -245,13 +246,41 @@ class _side_spaces(ABC):
|
|
|
245
246
|
"""
|
|
246
247
|
The height of the tag including the margins
|
|
247
248
|
|
|
248
|
-
The value is zero
|
|
249
|
+
The value is zero except if all these are true:
|
|
249
250
|
- The tag is in the margin `theme(plot_tag_position = "margin")`
|
|
250
251
|
- The tag at one one of the the following locations;
|
|
251
252
|
top, bottom, topleft, topright, bottomleft or bottomright
|
|
252
253
|
"""
|
|
253
254
|
return 0
|
|
254
255
|
|
|
256
|
+
@property
|
|
257
|
+
def axis_title_clearance(self) -> float:
|
|
258
|
+
"""
|
|
259
|
+
The distance between the axis title and the panel
|
|
260
|
+
|
|
261
|
+
Figure
|
|
262
|
+
----------------------------
|
|
263
|
+
| Panel |
|
|
264
|
+
| ----------- |
|
|
265
|
+
| | | |
|
|
266
|
+
| | | |
|
|
267
|
+
| Y<--->| | |
|
|
268
|
+
| | | |
|
|
269
|
+
| | | |
|
|
270
|
+
| ----------- |
|
|
271
|
+
| |
|
|
272
|
+
----------------------------
|
|
273
|
+
|
|
274
|
+
We use this value to when aligning axis titles in a
|
|
275
|
+
plot composition.
|
|
276
|
+
"""
|
|
277
|
+
|
|
278
|
+
try:
|
|
279
|
+
return self.total - self.sum_upto("axis_title_alignment")
|
|
280
|
+
except AttributeError as err:
|
|
281
|
+
# There is probably an error in in the layout manager
|
|
282
|
+
raise PlotnineError("Side has no axis title") from err
|
|
283
|
+
|
|
255
284
|
|
|
256
285
|
@dataclass
|
|
257
286
|
class left_spaces(_side_spaces):
|
|
@@ -311,6 +340,14 @@ class left_spaces(_side_spaces):
|
|
|
311
340
|
axis_title_y_margin_left: float = 0
|
|
312
341
|
axis_title_y: float = 0
|
|
313
342
|
axis_title_y_margin_right: float = 0
|
|
343
|
+
axis_title_alignment: float = 0
|
|
344
|
+
"""
|
|
345
|
+
Space added to align the axis title with others in a composition
|
|
346
|
+
|
|
347
|
+
This value is calculated during the layout process. The amount is
|
|
348
|
+
the difference between the largest and smallest axis_title_clearance
|
|
349
|
+
among the items in the composition.
|
|
350
|
+
"""
|
|
314
351
|
axis_text_y_margin_left: float = 0
|
|
315
352
|
axis_text_y: float = 0
|
|
316
353
|
axis_text_y_margin_right: float = 0
|
|
@@ -385,18 +422,18 @@ class left_spaces(_side_spaces):
|
|
|
385
422
|
return self.to_figure_space(self.sum_incl(item))
|
|
386
423
|
|
|
387
424
|
@property
|
|
388
|
-
def
|
|
425
|
+
def panel_left_relative(self):
|
|
389
426
|
"""
|
|
390
427
|
Left (relative to the gridspec) of the panels in figure dimensions
|
|
391
428
|
"""
|
|
392
429
|
return self.total
|
|
393
430
|
|
|
394
431
|
@property
|
|
395
|
-
def
|
|
432
|
+
def panel_left(self):
|
|
396
433
|
"""
|
|
397
434
|
Left of the panels in figure space
|
|
398
435
|
"""
|
|
399
|
-
return self.to_figure_space(self.
|
|
436
|
+
return self.to_figure_space(self.panel_left_relative)
|
|
400
437
|
|
|
401
438
|
@property
|
|
402
439
|
def plot_left(self):
|
|
@@ -491,18 +528,18 @@ class right_spaces(_side_spaces):
|
|
|
491
528
|
return self.to_figure_space(1 - self.sum_upto(item))
|
|
492
529
|
|
|
493
530
|
@property
|
|
494
|
-
def
|
|
531
|
+
def panel_right_relative(self):
|
|
495
532
|
"""
|
|
496
533
|
Right (relative to the gridspec) of the panels in figure dimensions
|
|
497
534
|
"""
|
|
498
535
|
return 1 - self.total
|
|
499
536
|
|
|
500
537
|
@property
|
|
501
|
-
def
|
|
538
|
+
def panel_right(self):
|
|
502
539
|
"""
|
|
503
540
|
Right of the panels in figure space
|
|
504
541
|
"""
|
|
505
|
-
return self.to_figure_space(self.
|
|
542
|
+
return self.to_figure_space(self.panel_right_relative)
|
|
506
543
|
|
|
507
544
|
@property
|
|
508
545
|
def plot_right(self):
|
|
@@ -620,18 +657,18 @@ class top_spaces(_side_spaces):
|
|
|
620
657
|
return self.to_figure_space(1 - self.sum_upto(item))
|
|
621
658
|
|
|
622
659
|
@property
|
|
623
|
-
def
|
|
660
|
+
def panel_top_relative(self):
|
|
624
661
|
"""
|
|
625
662
|
Top (relative to the gridspec) of the panels in figure dimensions
|
|
626
663
|
"""
|
|
627
664
|
return 1 - self.total
|
|
628
665
|
|
|
629
666
|
@property
|
|
630
|
-
def
|
|
667
|
+
def panel_top(self):
|
|
631
668
|
"""
|
|
632
669
|
Top of the panels in figure space
|
|
633
670
|
"""
|
|
634
|
-
return self.to_figure_space(self.
|
|
671
|
+
return self.to_figure_space(self.panel_top_relative)
|
|
635
672
|
|
|
636
673
|
@property
|
|
637
674
|
def plot_top(self):
|
|
@@ -674,6 +711,15 @@ class bottom_spaces(_side_spaces):
|
|
|
674
711
|
axis_title_x_margin_bottom: float = 0
|
|
675
712
|
axis_title_x: float = 0
|
|
676
713
|
axis_title_x_margin_top: float = 0
|
|
714
|
+
axis_title_alignment: float = 0
|
|
715
|
+
"""
|
|
716
|
+
Space added to align the axis title with others in a composition
|
|
717
|
+
|
|
718
|
+
This value is calculated during the layout process in a tree structure
|
|
719
|
+
that has convenient access to the sides/edges of the panels in the
|
|
720
|
+
composition. It's amount is the difference in height between this axis
|
|
721
|
+
text (and it's margins) and the tallest axis text (and it's margin).
|
|
722
|
+
"""
|
|
677
723
|
axis_text_x_margin_bottom: float = 0
|
|
678
724
|
axis_text_x: float = 0
|
|
679
725
|
axis_text_x_margin_top: float = 0
|
|
@@ -758,18 +804,18 @@ class bottom_spaces(_side_spaces):
|
|
|
758
804
|
return self.to_figure_space(self.sum_incl(item))
|
|
759
805
|
|
|
760
806
|
@property
|
|
761
|
-
def
|
|
807
|
+
def panel_bottom_relative(self):
|
|
762
808
|
"""
|
|
763
809
|
Bottom (relative to the gridspec) of the panels in figure dimensions
|
|
764
810
|
"""
|
|
765
811
|
return self.total
|
|
766
812
|
|
|
767
813
|
@property
|
|
768
|
-
def
|
|
814
|
+
def panel_bottom(self):
|
|
769
815
|
"""
|
|
770
816
|
Bottom of the panels in figure space
|
|
771
817
|
"""
|
|
772
|
-
return self.to_figure_space(self.
|
|
818
|
+
return self.to_figure_space(self.panel_bottom_relative)
|
|
773
819
|
|
|
774
820
|
@property
|
|
775
821
|
def plot_bottom(self):
|
|
@@ -892,14 +938,14 @@ class LayoutSpaces:
|
|
|
892
938
|
"""
|
|
893
939
|
Width [figure dimensions] of panels
|
|
894
940
|
"""
|
|
895
|
-
return self.r.
|
|
941
|
+
return self.r.panel_right - self.l.panel_left
|
|
896
942
|
|
|
897
943
|
@property
|
|
898
944
|
def panel_height(self) -> float:
|
|
899
945
|
"""
|
|
900
946
|
Height [figure dimensions] of panels
|
|
901
947
|
"""
|
|
902
|
-
return self.t.
|
|
948
|
+
return self.t.panel_top - self.b.panel_bottom
|
|
903
949
|
|
|
904
950
|
@property
|
|
905
951
|
def tag_width(self) -> float:
|
|
@@ -945,6 +991,24 @@ class LayoutSpaces:
|
|
|
945
991
|
"""
|
|
946
992
|
return self.b.tag_height
|
|
947
993
|
|
|
994
|
+
@property
|
|
995
|
+
def left_axis_title_clearance(self) -> float:
|
|
996
|
+
"""
|
|
997
|
+
Distance between the left y-axis title and the panel
|
|
998
|
+
|
|
999
|
+
In figure dimensions.
|
|
1000
|
+
"""
|
|
1001
|
+
return self.l.axis_title_clearance
|
|
1002
|
+
|
|
1003
|
+
@property
|
|
1004
|
+
def bottom_axis_title_clearance(self) -> float:
|
|
1005
|
+
"""
|
|
1006
|
+
Distance between the bottom x-axis title and the panel
|
|
1007
|
+
|
|
1008
|
+
In figure dimensions.
|
|
1009
|
+
"""
|
|
1010
|
+
return self.b.axis_title_clearance
|
|
1011
|
+
|
|
948
1012
|
def increase_horizontal_plot_margin(self, dw: float):
|
|
949
1013
|
"""
|
|
950
1014
|
Increase the plot_margin to the right & left of the panels
|
|
@@ -981,8 +1045,8 @@ class LayoutSpaces:
|
|
|
981
1045
|
|
|
982
1046
|
This is the area in which the panels are drawn.
|
|
983
1047
|
"""
|
|
984
|
-
x1, x2 = self.l.
|
|
985
|
-
y1, y2 = self.b.
|
|
1048
|
+
x1, x2 = self.l.panel_left, self.r.panel_right
|
|
1049
|
+
y1, y2 = self.b.panel_bottom, self.t.panel_top
|
|
986
1050
|
return ((x1, y1), (x2, y2))
|
|
987
1051
|
|
|
988
1052
|
def _calculate_panel_spacing(self) -> GridSpecParams:
|
|
@@ -1003,10 +1067,10 @@ class LayoutSpaces:
|
|
|
1003
1067
|
raise TypeError(f"Unknown type of facet: {type(self.plot.facet)}")
|
|
1004
1068
|
|
|
1005
1069
|
return GridSpecParams(
|
|
1006
|
-
self.l.
|
|
1007
|
-
self.r.
|
|
1008
|
-
self.t.
|
|
1009
|
-
self.b.
|
|
1070
|
+
self.l.panel_left_relative,
|
|
1071
|
+
self.r.panel_right_relative,
|
|
1072
|
+
self.t.panel_top_relative,
|
|
1073
|
+
self.b.panel_bottom_relative,
|
|
1010
1074
|
wspace,
|
|
1011
1075
|
hspace,
|
|
1012
1076
|
)
|
|
@@ -1020,6 +1084,9 @@ class LayoutSpaces:
|
|
|
1020
1084
|
ncol = self.plot.facet.ncol
|
|
1021
1085
|
nrow = self.plot.facet.nrow
|
|
1022
1086
|
|
|
1087
|
+
left, right = self.l.panel_left, self.r.panel_right
|
|
1088
|
+
top, bottom = self.t.panel_top, self.b.panel_bottom
|
|
1089
|
+
|
|
1023
1090
|
# Both spacings are specified as fractions of the figure width
|
|
1024
1091
|
# Multiply the vertical by (W/H) so that the gullies along both
|
|
1025
1092
|
# directions are equally spaced.
|
|
@@ -1027,8 +1094,8 @@ class LayoutSpaces:
|
|
|
1027
1094
|
self.sh = theme.getp("panel_spacing_y") * self.W / self.H
|
|
1028
1095
|
|
|
1029
1096
|
# width and height of axes as fraction of figure width & height
|
|
1030
|
-
self.w = ((
|
|
1031
|
-
self.h = ((
|
|
1097
|
+
self.w = ((right - left) - self.sw * (ncol - 1)) / ncol
|
|
1098
|
+
self.h = ((top - bottom) - self.sh * (nrow - 1)) / nrow
|
|
1032
1099
|
|
|
1033
1100
|
# Spacing as fraction of axes width & height
|
|
1034
1101
|
wspace = self.sw / self.w
|
|
@@ -1045,6 +1112,9 @@ class LayoutSpaces:
|
|
|
1045
1112
|
ncol = facet.ncol
|
|
1046
1113
|
nrow = facet.nrow
|
|
1047
1114
|
|
|
1115
|
+
left, right = self.l.panel_left, self.r.panel_right
|
|
1116
|
+
top, bottom = self.t.panel_top, self.b.panel_bottom
|
|
1117
|
+
|
|
1048
1118
|
# Both spacings are specified as fractions of the figure width
|
|
1049
1119
|
self.sw = theme.getp("panel_spacing_x")
|
|
1050
1120
|
self.sh = theme.getp("panel_spacing_y") * self.W / self.H
|
|
@@ -1073,8 +1143,8 @@ class LayoutSpaces:
|
|
|
1073
1143
|
) + self.items.axis_ticks_y_max_width_at("all")
|
|
1074
1144
|
|
|
1075
1145
|
# width and height of axes as fraction of figure width & height
|
|
1076
|
-
self.w = ((
|
|
1077
|
-
self.h = ((
|
|
1146
|
+
self.w = ((right - left) - self.sw * (ncol - 1)) / ncol
|
|
1147
|
+
self.h = ((top - bottom) - self.sh * (nrow - 1)) / nrow
|
|
1078
1148
|
|
|
1079
1149
|
# Spacing as fraction of axes width & height
|
|
1080
1150
|
wspace = self.sw / self.w
|
|
@@ -1085,8 +1155,8 @@ class LayoutSpaces:
|
|
|
1085
1155
|
"""
|
|
1086
1156
|
Calculate spacing parts for facet_null
|
|
1087
1157
|
"""
|
|
1088
|
-
self.w = self.r.
|
|
1089
|
-
self.h = self.t.
|
|
1158
|
+
self.w = self.r.panel_right - self.l.panel_left
|
|
1159
|
+
self.h = self.t.panel_top - self.b.panel_bottom
|
|
1090
1160
|
self.sw = 0
|
|
1091
1161
|
self.sh = 0
|
|
1092
1162
|
return 0, 0
|
plotnine/_mpl/text.py
CHANGED
|
@@ -32,7 +32,7 @@ class StripText(Text):
|
|
|
32
32
|
"clip_on": False,
|
|
33
33
|
"zorder": 3.3,
|
|
34
34
|
# Since the text can be rotated, it is simpler to anchor it at
|
|
35
|
-
# the center, align it then do the rotation. Vertically,
|
|
35
|
+
# the center, align it, then do the rotation. Vertically,
|
|
36
36
|
# center_baseline places the text in the visual center, but
|
|
37
37
|
# only if it is one line. For multiline text, we are better
|
|
38
38
|
# off with plain center.
|
|
@@ -45,12 +45,6 @@ class StripText(Text):
|
|
|
45
45
|
self.draw_info = info
|
|
46
46
|
self.patch = StripTextPatch(self)
|
|
47
47
|
|
|
48
|
-
# self.set_horizontalalignment("center")
|
|
49
|
-
# self.set_verticalalignment(
|
|
50
|
-
# "center_baseline" if info.is_oneline else "center"
|
|
51
|
-
# )
|
|
52
|
-
# self.set_rotation_mode("anchor")
|
|
53
|
-
|
|
54
48
|
# TODO: This should really be part of the unit conversions in the
|
|
55
49
|
# margin class.
|
|
56
50
|
@lru_cache(2)
|
plotnine/composition/__init__.py
CHANGED
plotnine/composition/_beside.py
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from dataclasses import dataclass
|
|
3
4
|
from typing import TYPE_CHECKING
|
|
4
5
|
|
|
5
|
-
from .
|
|
6
|
+
from ._compose import Compose
|
|
6
7
|
|
|
7
8
|
if TYPE_CHECKING:
|
|
8
9
|
from plotnine.ggplot import ggplot
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
@dataclass
|
|
13
|
+
class Beside(Compose):
|
|
12
14
|
"""
|
|
13
15
|
Place plots or compositions side by side
|
|
14
16
|
|
|
@@ -20,6 +22,12 @@ class Beside(Arrange):
|
|
|
20
22
|
composition | composition
|
|
21
23
|
|
|
22
24
|
Typically, you will use this class through the `|` operator.
|
|
25
|
+
|
|
26
|
+
See Also
|
|
27
|
+
--------
|
|
28
|
+
plotnine.composition.Stack : To arrange plots vertically
|
|
29
|
+
plotnine.composition.plot_spacer : To add a blank space between plots
|
|
30
|
+
plotnine.composition.Compose : For more on composing plots
|
|
23
31
|
"""
|
|
24
32
|
|
|
25
33
|
@property
|
|
@@ -30,7 +38,7 @@ class Beside(Arrange):
|
|
|
30
38
|
def ncol(self) -> int:
|
|
31
39
|
return len(self)
|
|
32
40
|
|
|
33
|
-
def __or__(self, rhs: ggplot |
|
|
41
|
+
def __or__(self, rhs: ggplot | Compose) -> Compose:
|
|
34
42
|
"""
|
|
35
43
|
Add rhs as a column
|
|
36
44
|
"""
|
|
@@ -38,7 +46,7 @@ class Beside(Arrange):
|
|
|
38
46
|
# operands into a single operation
|
|
39
47
|
return Beside([*self, rhs])
|
|
40
48
|
|
|
41
|
-
def __truediv__(self, rhs: ggplot |
|
|
49
|
+
def __truediv__(self, rhs: ggplot | Compose) -> Compose:
|
|
42
50
|
"""
|
|
43
51
|
Add rhs as a row
|
|
44
52
|
"""
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import abc
|
|
4
|
-
from copy import deepcopy
|
|
4
|
+
from copy import copy, deepcopy
|
|
5
5
|
from dataclasses import dataclass, field
|
|
6
6
|
from io import BytesIO
|
|
7
|
-
from typing import TYPE_CHECKING
|
|
7
|
+
from typing import TYPE_CHECKING, overload
|
|
8
8
|
|
|
9
9
|
from .._utils.ipython import (
|
|
10
10
|
get_display_function,
|
|
@@ -25,41 +25,93 @@ if TYPE_CHECKING:
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
@dataclass
|
|
28
|
-
class
|
|
28
|
+
class Compose:
|
|
29
29
|
"""
|
|
30
30
|
Base class for those that create plot compositions
|
|
31
31
|
|
|
32
32
|
As a user, you will never directly work with this class, except
|
|
33
|
-
the operators
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
operands
|
|
40
|
-
|
|
33
|
+
through the operators that it makes possible.
|
|
34
|
+
The operators are of two kinds:
|
|
35
|
+
|
|
36
|
+
### 1. Composing Operators
|
|
37
|
+
|
|
38
|
+
The combine plots or compositions into a single composition.
|
|
39
|
+
Both operands are either a plot or a composition.
|
|
40
|
+
|
|
41
|
+
`/`
|
|
42
|
+
|
|
43
|
+
: Arrange operands side by side.
|
|
44
|
+
Powered by the subclass [](`~plotnine.composition.Beside`).
|
|
45
|
+
|
|
46
|
+
`|`
|
|
47
|
+
|
|
48
|
+
: Arrange operands vertically.
|
|
49
|
+
Powered by the subclass [](`~plotnine.composition.Stack`).
|
|
50
|
+
|
|
51
|
+
`-`
|
|
52
|
+
|
|
53
|
+
: Arrange operands side by side _and_ at the same nesting level.
|
|
54
|
+
Also powered by the subclass [](`~plotnine.composition.Beside`).
|
|
55
|
+
|
|
56
|
+
### 2. Plot Modifying Operators
|
|
57
|
+
|
|
58
|
+
The modify all or some of the plots in a composition.
|
|
59
|
+
The left operand is a composition and the right operand is a
|
|
60
|
+
_plotaddable_; any object that can be added to a `ggplot` object
|
|
61
|
+
e.g. _geoms_, _stats_, _themes_, _facets_, ... .
|
|
62
|
+
|
|
63
|
+
`&`
|
|
64
|
+
|
|
65
|
+
: Add right hand side to all plots in the composition.
|
|
66
|
+
|
|
67
|
+
`*`
|
|
68
|
+
|
|
69
|
+
: Add right hand side to all plots in the top-most nesting
|
|
70
|
+
level of the composition.
|
|
71
|
+
|
|
72
|
+
`+`
|
|
73
|
+
|
|
74
|
+
: Add right hand side to the last plot in the composition.
|
|
75
|
+
|
|
76
|
+
See Also
|
|
77
|
+
--------
|
|
78
|
+
plotnine.composition.Beside : To arrange plots side by side
|
|
79
|
+
plotnine.composition.Stack : To arrange plots vertically
|
|
80
|
+
plotnine.composition.plot_spacer : To add a blank space between plots
|
|
41
81
|
"""
|
|
42
82
|
|
|
43
|
-
|
|
83
|
+
items: list[ggplot | Compose]
|
|
84
|
+
"""
|
|
85
|
+
The objects to be arranged (composed).
|
|
86
|
+
"""
|
|
44
87
|
|
|
45
88
|
# These are created in the _create_figure method
|
|
46
89
|
figure: Figure = field(init=False, repr=False)
|
|
47
90
|
plotspecs: list[plotspec] = field(init=False, repr=False)
|
|
48
91
|
gridspec: p9GridSpec = field(init=False, repr=False)
|
|
49
92
|
|
|
93
|
+
def __post_init__(self):
|
|
94
|
+
# The way we handle the plots has consequences that would
|
|
95
|
+
# prevent having a duplicate plot in the composition.
|
|
96
|
+
# Using copies prevents this.
|
|
97
|
+
self.items = [
|
|
98
|
+
op if isinstance(op, Compose) else deepcopy(op)
|
|
99
|
+
for op in self.items
|
|
100
|
+
]
|
|
101
|
+
|
|
50
102
|
@abc.abstractmethod
|
|
51
|
-
def __or__(self, rhs: ggplot |
|
|
103
|
+
def __or__(self, rhs: ggplot | Compose) -> Compose:
|
|
52
104
|
"""
|
|
53
105
|
Add rhs as a column
|
|
54
106
|
"""
|
|
55
107
|
|
|
56
108
|
@abc.abstractmethod
|
|
57
|
-
def __truediv__(self, rhs: ggplot |
|
|
109
|
+
def __truediv__(self, rhs: ggplot | Compose) -> Compose:
|
|
58
110
|
"""
|
|
59
111
|
Add rhs as a row
|
|
60
112
|
"""
|
|
61
113
|
|
|
62
|
-
def __add__(self, rhs: ggplot |
|
|
114
|
+
def __add__(self, rhs: ggplot | Compose | PlotAddable) -> Compose:
|
|
63
115
|
"""
|
|
64
116
|
Add rhs to the composition
|
|
65
117
|
|
|
@@ -70,24 +122,36 @@ class Arrange:
|
|
|
70
122
|
"""
|
|
71
123
|
from plotnine import ggplot
|
|
72
124
|
|
|
73
|
-
if not isinstance(rhs, (ggplot,
|
|
125
|
+
if not isinstance(rhs, (ggplot, Compose)):
|
|
74
126
|
cmp = deepcopy(self)
|
|
75
127
|
cmp.last_plot = cmp.last_plot + rhs
|
|
76
128
|
return cmp
|
|
77
|
-
return self.__class__([*self, rhs])
|
|
78
129
|
|
|
79
|
-
|
|
130
|
+
t1, t2 = type(self).__name__, type(rhs).__name__
|
|
131
|
+
msg = f"unsupported operand type(s) for +: '{t1}' and '{t2}'"
|
|
132
|
+
raise TypeError(msg)
|
|
133
|
+
|
|
134
|
+
def __sub__(self, rhs: ggplot | Compose) -> Compose:
|
|
80
135
|
"""
|
|
81
|
-
Add the rhs
|
|
136
|
+
Add the rhs onto the composition
|
|
82
137
|
|
|
83
138
|
Parameters
|
|
84
139
|
----------
|
|
85
140
|
rhs:
|
|
86
141
|
What to place besides the composition
|
|
87
142
|
"""
|
|
88
|
-
|
|
143
|
+
from plotnine import ggplot
|
|
144
|
+
|
|
145
|
+
from . import Beside
|
|
146
|
+
|
|
147
|
+
if not isinstance(rhs, (ggplot, Compose)):
|
|
148
|
+
t1, t2 = type(self).__name__, type(rhs).__name__
|
|
149
|
+
msg = f"unsupported operand type(s) for -: '{t1}' and '{t2}'"
|
|
150
|
+
raise TypeError(msg)
|
|
89
151
|
|
|
90
|
-
|
|
152
|
+
return Beside([self, rhs])
|
|
153
|
+
|
|
154
|
+
def __and__(self, rhs: PlotAddable) -> Compose:
|
|
91
155
|
"""
|
|
92
156
|
Add rhs to all plots in the composition
|
|
93
157
|
|
|
@@ -98,17 +162,17 @@ class Arrange:
|
|
|
98
162
|
"""
|
|
99
163
|
self = deepcopy(self)
|
|
100
164
|
|
|
101
|
-
def add_other(
|
|
102
|
-
for item in
|
|
103
|
-
if isinstance(item,
|
|
165
|
+
def add_other(cmp: Compose):
|
|
166
|
+
for i, item in enumerate(cmp):
|
|
167
|
+
if isinstance(item, Compose):
|
|
104
168
|
add_other(item)
|
|
105
169
|
else:
|
|
106
|
-
item
|
|
170
|
+
cmp[i] = item + copy(rhs)
|
|
107
171
|
|
|
108
172
|
add_other(self)
|
|
109
173
|
return self
|
|
110
174
|
|
|
111
|
-
def __mul__(self, rhs: PlotAddable) ->
|
|
175
|
+
def __mul__(self, rhs: PlotAddable) -> Compose:
|
|
112
176
|
"""
|
|
113
177
|
Add rhs to the outermost nesting level of the composition
|
|
114
178
|
|
|
@@ -121,22 +185,38 @@ class Arrange:
|
|
|
121
185
|
|
|
122
186
|
self = deepcopy(self)
|
|
123
187
|
|
|
124
|
-
for item in self:
|
|
188
|
+
for i, item in enumerate(self):
|
|
125
189
|
if isinstance(item, ggplot):
|
|
126
|
-
item
|
|
190
|
+
self[i] = item + copy(rhs)
|
|
191
|
+
|
|
127
192
|
return self
|
|
128
193
|
|
|
129
194
|
def __len__(self) -> int:
|
|
130
195
|
"""
|
|
131
196
|
Number of operand
|
|
132
197
|
"""
|
|
133
|
-
return len(self.
|
|
198
|
+
return len(self.items)
|
|
134
199
|
|
|
135
|
-
def __iter__(self) -> Iterator[ggplot |
|
|
200
|
+
def __iter__(self) -> Iterator[ggplot | Compose]:
|
|
136
201
|
"""
|
|
137
|
-
Return an iterable of all the
|
|
202
|
+
Return an iterable of all the items
|
|
138
203
|
"""
|
|
139
|
-
return iter(self.
|
|
204
|
+
return iter(self.items)
|
|
205
|
+
|
|
206
|
+
@overload
|
|
207
|
+
def __getitem__(self, index: int) -> ggplot | Compose: ...
|
|
208
|
+
|
|
209
|
+
@overload
|
|
210
|
+
def __getitem__(self, index: slice) -> list[ggplot | Compose]: ...
|
|
211
|
+
|
|
212
|
+
def __getitem__(
|
|
213
|
+
self,
|
|
214
|
+
index: int | slice,
|
|
215
|
+
) -> ggplot | Compose | list[ggplot | Compose]:
|
|
216
|
+
return self.items[index]
|
|
217
|
+
|
|
218
|
+
def __setitem__(self, key, value):
|
|
219
|
+
self.items[key] = value
|
|
140
220
|
|
|
141
221
|
def _ipython_display_(self):
|
|
142
222
|
"""
|
|
@@ -165,7 +245,7 @@ class Arrange:
|
|
|
165
245
|
"""
|
|
166
246
|
from plotnine import ggplot
|
|
167
247
|
|
|
168
|
-
last_operand = self.
|
|
248
|
+
last_operand = self.items[-1]
|
|
169
249
|
if isinstance(last_operand, ggplot):
|
|
170
250
|
return last_operand
|
|
171
251
|
else:
|
|
@@ -178,9 +258,9 @@ class Arrange:
|
|
|
178
258
|
"""
|
|
179
259
|
from plotnine import ggplot
|
|
180
260
|
|
|
181
|
-
last_operand = self.
|
|
261
|
+
last_operand = self.items[-1]
|
|
182
262
|
if isinstance(last_operand, ggplot):
|
|
183
|
-
self.
|
|
263
|
+
self.items[-1] = plot
|
|
184
264
|
else:
|
|
185
265
|
last_operand.last_plot = plot
|
|
186
266
|
|
|
@@ -232,7 +312,7 @@ class Arrange:
|
|
|
232
312
|
from plotnine._mpl.gridspec import p9GridSpec
|
|
233
313
|
|
|
234
314
|
def _make_plotspecs(
|
|
235
|
-
cmp:
|
|
315
|
+
cmp: Compose, parent_gridspec: p9GridSpec | None
|
|
236
316
|
) -> Generator[plotspec]:
|
|
237
317
|
"""
|
|
238
318
|
Return the plot specification for each subplot in the composition
|
|
@@ -340,7 +420,7 @@ class Arrange:
|
|
|
340
420
|
dpi :
|
|
341
421
|
DPI to use for raster graphics. If None, defaults to using
|
|
342
422
|
the `dpi` of theme to the first plot.
|
|
343
|
-
kwargs :
|
|
423
|
+
**kwargs :
|
|
344
424
|
These are ignored. Here to "softly" match the API of
|
|
345
425
|
`ggplot.save()`.
|
|
346
426
|
"""
|
|
@@ -355,7 +435,7 @@ class Arrange:
|
|
|
355
435
|
|
|
356
436
|
@dataclass
|
|
357
437
|
class plot_composition_context:
|
|
358
|
-
cmp:
|
|
438
|
+
cmp: Compose
|
|
359
439
|
show: bool
|
|
360
440
|
|
|
361
441
|
def __post_init__(self):
|
|
@@ -17,6 +17,12 @@ class plot_spacer(ggplot):
|
|
|
17
17
|
|
|
18
18
|
The color can also be modified by adding a [](`~plotnine.theme`)
|
|
19
19
|
and setting the [](`~plotnine.themes.themeable.plot_background`).
|
|
20
|
+
|
|
21
|
+
See Also
|
|
22
|
+
--------
|
|
23
|
+
plotnine.composition.Beside : To arrange plots side by side
|
|
24
|
+
plotnine.composition.Stack : To arrange plots vertically
|
|
25
|
+
plotnine.composition.Compose : For more on composing plots
|
|
20
26
|
"""
|
|
21
27
|
|
|
22
28
|
def __init__(
|