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.
- 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 +957 -0
- plotnine/_mpl/layout_manager/_layout_tree.py +905 -0
- plotnine/_mpl/layout_manager/_spaces.py +1154 -0
- plotnine/_mpl/patches.py +70 -34
- plotnine/_mpl/text.py +159 -37
- plotnine/_mpl/utils.py +78 -10
- plotnine/_utils/__init__.py +35 -9
- plotnine/_utils/dev.py +45 -27
- plotnine/_utils/yippie.py +115 -0
- plotnine/animation.py +1 -1
- plotnine/coords/coord.py +3 -3
- plotnine/coords/coord_trans.py +1 -1
- plotnine/data/__init__.py +43 -8
- plotnine/data/anscombe-quartet.csv +45 -0
- plotnine/doctools.py +2 -2
- plotnine/facets/facet.py +34 -43
- plotnine/facets/facet_grid.py +14 -6
- plotnine/facets/facet_wrap.py +3 -5
- plotnine/facets/strips.py +20 -33
- plotnine/geoms/annotate.py +3 -3
- plotnine/geoms/annotation_logticks.py +2 -0
- plotnine/geoms/annotation_stripes.py +2 -0
- plotnine/geoms/geom.py +3 -3
- plotnine/geoms/geom_bar.py +10 -2
- plotnine/geoms/geom_col.py +6 -0
- plotnine/geoms/geom_crossbar.py +2 -3
- plotnine/geoms/geom_path.py +2 -2
- plotnine/geoms/geom_violin.py +24 -7
- plotnine/ggplot.py +95 -66
- plotnine/guides/guide.py +19 -20
- plotnine/guides/guide_colorbar.py +6 -6
- plotnine/guides/guide_legend.py +15 -16
- plotnine/guides/guides.py +8 -8
- plotnine/helpers.py +49 -0
- plotnine/iapi.py +33 -7
- plotnine/labels.py +8 -3
- plotnine/layer.py +4 -4
- plotnine/mapping/_env.py +2 -2
- plotnine/mapping/_eval_environment.py +85 -0
- plotnine/mapping/aes.py +14 -30
- plotnine/mapping/evaluation.py +7 -65
- plotnine/options.py +14 -7
- plotnine/plot_composition/__init__.py +10 -0
- plotnine/plot_composition/_compose.py +462 -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/limits.py +7 -7
- plotnine/scales/scale.py +4 -4
- plotnine/scales/scale_continuous.py +2 -1
- plotnine/scales/scale_identity.py +10 -2
- plotnine/scales/scale_manual.py +6 -2
- plotnine/stats/binning.py +5 -2
- plotnine/stats/smoothers.py +3 -5
- plotnine/stats/stat.py +3 -3
- plotnine/stats/stat_bindot.py +1 -3
- plotnine/stats/stat_density.py +2 -2
- plotnine/stats/stat_qq_line.py +1 -1
- plotnine/stats/stat_sina.py +34 -1
- plotnine/themes/elements/__init__.py +3 -0
- plotnine/themes/elements/element_text.py +35 -24
- plotnine/themes/elements/margin.py +137 -61
- plotnine/themes/targets.py +3 -1
- plotnine/themes/theme.py +21 -7
- plotnine/themes/theme_538.py +0 -1
- plotnine/themes/theme_bw.py +0 -1
- plotnine/themes/theme_dark.py +0 -1
- plotnine/themes/theme_gray.py +32 -34
- plotnine/themes/theme_light.py +1 -1
- plotnine/themes/theme_matplotlib.py +28 -31
- plotnine/themes/theme_seaborn.py +36 -36
- plotnine/themes/theme_void.py +25 -27
- plotnine/themes/theme_xkcd.py +0 -1
- plotnine/themes/themeable.py +369 -169
- plotnine/typing.py +3 -3
- plotnine/watermark.py +3 -3
- {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/METADATA +8 -5
- {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/RECORD +89 -78
- {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.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.5.dist-info → plotnine-0.15.0a2.dist-info/licenses}/LICENSE +0 -0
- {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Functions/class to quickly create plots for development and testing
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from plotnine import (
|
|
8
|
+
aes,
|
|
9
|
+
element_blank,
|
|
10
|
+
element_rect,
|
|
11
|
+
element_text,
|
|
12
|
+
geom_col,
|
|
13
|
+
geom_point,
|
|
14
|
+
ggplot,
|
|
15
|
+
labs,
|
|
16
|
+
theme,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = ("geom", "legend", "plot", "rotate", "tag")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class _Plot:
|
|
23
|
+
"""
|
|
24
|
+
Create a plot
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __getattr__(self, color: str):
|
|
28
|
+
"""
|
|
29
|
+
Create a blank plot with given background color
|
|
30
|
+
"""
|
|
31
|
+
return (
|
|
32
|
+
ggplot()
|
|
33
|
+
+ labs(
|
|
34
|
+
x="x-axis",
|
|
35
|
+
y="y-axis",
|
|
36
|
+
title=color.title(),
|
|
37
|
+
)
|
|
38
|
+
+ theme(
|
|
39
|
+
figure_size=(8, 6),
|
|
40
|
+
text=element_text(color="black", size=11),
|
|
41
|
+
panel_background=element_rect(fill=color, size=1),
|
|
42
|
+
plot_background=element_rect(fill=color, alpha=0.2),
|
|
43
|
+
panel_border=element_rect(color="black"),
|
|
44
|
+
strip_background=element_rect(color="black"),
|
|
45
|
+
panel_grid=element_blank(),
|
|
46
|
+
legend_key_size=12,
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class _Geom:
|
|
52
|
+
"""
|
|
53
|
+
Create some simple geoms
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
data = pd.DataFrame(
|
|
57
|
+
{
|
|
58
|
+
"cat": ["a", "b", "c", "d"],
|
|
59
|
+
"cat2": ["r", "r", "s", "s"],
|
|
60
|
+
"value": [1, 2, 3, 4],
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def points(self):
|
|
66
|
+
return geom_point(aes("cat", "value", color="cat"), self.data, size=2)
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def cols(self):
|
|
70
|
+
return geom_col(aes("cat", "value", fill="cat"), self.data)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class _Legend:
|
|
74
|
+
"""
|
|
75
|
+
Position Legends
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def left(self):
|
|
80
|
+
return theme(legend_position="left")
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def bottom(self):
|
|
84
|
+
return theme(legend_position="bottom")
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def right(self):
|
|
88
|
+
return theme(legend_position="right")
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def top(self):
|
|
92
|
+
return theme(legend_position="top")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class _Rotate:
|
|
96
|
+
"""
|
|
97
|
+
Rotate a text themeable
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
def __getattr__(self, name):
|
|
101
|
+
angle = 0 if name[-2:] == "_y" else 90
|
|
102
|
+
return theme(**{name: element_text(angle=angle)}) # pyright: ignore[reportArgumentType]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def tag(s, position="topleft"):
|
|
106
|
+
"""
|
|
107
|
+
Create a tag at a position
|
|
108
|
+
"""
|
|
109
|
+
return [labs(tag=s), theme(plot_tag_position=position)]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
plot = _Plot()
|
|
113
|
+
geom = _Geom()
|
|
114
|
+
legend = _Legend()
|
|
115
|
+
rotate = _Rotate()
|
plotnine/animation.py
CHANGED
plotnine/coords/coord.py
CHANGED
|
@@ -35,12 +35,12 @@ class coord:
|
|
|
35
35
|
# if the coordinate system needs them
|
|
36
36
|
params: dict[str, Any]
|
|
37
37
|
|
|
38
|
-
def __radd__(self,
|
|
38
|
+
def __radd__(self, other: ggplot) -> ggplot:
|
|
39
39
|
"""
|
|
40
40
|
Add coordinates to ggplot object
|
|
41
41
|
"""
|
|
42
|
-
|
|
43
|
-
return
|
|
42
|
+
other.coordinates = copy(self)
|
|
43
|
+
return other
|
|
44
44
|
|
|
45
45
|
def setup_data(self, data: list[pd.DataFrame]) -> list[pd.DataFrame]:
|
|
46
46
|
"""
|
plotnine/coords/coord_trans.py
CHANGED
|
@@ -117,7 +117,7 @@ class coord_trans(coord):
|
|
|
117
117
|
range=ranges.range,
|
|
118
118
|
)
|
|
119
119
|
sv.range = tuple(sorted(ranges.range_coord)) # type: ignore
|
|
120
|
-
breaks = cast(tuple[float, float], sv.breaks)
|
|
120
|
+
breaks = cast("tuple[float, float]", sv.breaks)
|
|
121
121
|
sv.breaks = transform_value(trans, breaks)
|
|
122
122
|
sv.minor_breaks = transform_value(trans, sv.minor_breaks)
|
|
123
123
|
return sv
|
plotnine/data/__init__.py
CHANGED
|
@@ -8,6 +8,7 @@ import pandas as pd
|
|
|
8
8
|
from pandas.api.types import CategoricalDtype
|
|
9
9
|
|
|
10
10
|
__all__ = (
|
|
11
|
+
"anscombe_quartet",
|
|
11
12
|
"diamonds",
|
|
12
13
|
"economics",
|
|
13
14
|
"economics_long",
|
|
@@ -42,6 +43,7 @@ penguins = pd.read_csv(DATA_DIR / "penguins.csv")
|
|
|
42
43
|
luv_colours = pd.read_csv(DATA_DIR / "luv_colours.csv")
|
|
43
44
|
faithfuld = pd.read_csv(DATA_DIR / "faithfuld.csv")
|
|
44
45
|
faithful = pd.read_csv(DATA_DIR / "faithful.csv")
|
|
46
|
+
anscombe_quartet = pd.read_csv(DATA_DIR / "anscombe-quartet.csv")
|
|
45
47
|
|
|
46
48
|
# For convenience to the user, we set some columns in these
|
|
47
49
|
# dataframes to categoricals.
|
|
@@ -81,20 +83,24 @@ def _process_categories():
|
|
|
81
83
|
"""
|
|
82
84
|
Set columns in some of the dataframes to categoricals
|
|
83
85
|
"""
|
|
84
|
-
global diamonds,
|
|
86
|
+
global diamonds, penguins
|
|
85
87
|
diamonds = _ordered_categories(
|
|
86
88
|
diamonds,
|
|
87
89
|
{
|
|
88
|
-
"cut": "Fair, Good, Very Good, Premium, Ideal"
|
|
89
|
-
"clarity":
|
|
90
|
+
"cut": ["Fair", "Good", "Very Good", "Premium", "Ideal"],
|
|
91
|
+
"clarity": [
|
|
92
|
+
"I1",
|
|
93
|
+
"SI2",
|
|
94
|
+
"SI1",
|
|
95
|
+
"VS2",
|
|
96
|
+
"VS1",
|
|
97
|
+
"VVS2",
|
|
98
|
+
"VVS1",
|
|
99
|
+
"IF",
|
|
100
|
+
],
|
|
90
101
|
"color": list("DEFGHIJ"),
|
|
91
102
|
},
|
|
92
103
|
)
|
|
93
|
-
mpg = _unordered_categories(
|
|
94
|
-
mpg, "manufacturer model trans fl drv class".split()
|
|
95
|
-
)
|
|
96
|
-
midwest = _unordered_categories(midwest, ["category"])
|
|
97
|
-
msleep = _unordered_categories(msleep, ["vore", "conservation"])
|
|
98
104
|
penguins = _unordered_categories(penguins, ["species", "island", "sex"])
|
|
99
105
|
|
|
100
106
|
|
|
@@ -614,3 +620,32 @@ A data frame with 83 rows and 11 variables
|
|
|
614
620
|
Additional variables order, conservation status and
|
|
615
621
|
vore were added from wikipedia.
|
|
616
622
|
"""
|
|
623
|
+
|
|
624
|
+
anscombe_quartet.__doc__ = """
|
|
625
|
+
Anscombe's Quartet
|
|
626
|
+
|
|
627
|
+
## Description
|
|
628
|
+
|
|
629
|
+
A dataset by Statistician Francis Anscombe that challenged the commonly held
|
|
630
|
+
belief that "numerical calculations are exact, but graphs are rough"
|
|
631
|
+
(Anscombe, 1973).
|
|
632
|
+
|
|
633
|
+
It comprises of 4 (the quartet!) small sub-datasets, each with 11 points that
|
|
634
|
+
have different distributions but nearly identical descriptive statistics.
|
|
635
|
+
It is perhaps the best argument for visualising data.
|
|
636
|
+
|
|
637
|
+
## Format
|
|
638
|
+
|
|
639
|
+
A dataframe with 44 rows and 3 variables
|
|
640
|
+
|
|
641
|
+
| Column | Description |
|
|
642
|
+
|--------------|---------------------------------------|
|
|
643
|
+
| dataset | The Dataset |
|
|
644
|
+
| x | x |
|
|
645
|
+
| y | y |
|
|
646
|
+
|
|
647
|
+
## References
|
|
648
|
+
|
|
649
|
+
Anscombe, F. J. (1973). "Graphs in Statistical Analysis".
|
|
650
|
+
American Statistician. 27 (1): 17–21.
|
|
651
|
+
"""
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
dataset,x,y
|
|
2
|
+
I,10,8.04
|
|
3
|
+
I,8,6.95
|
|
4
|
+
I,13,7.58
|
|
5
|
+
I,9,8.81
|
|
6
|
+
I,11,8.33
|
|
7
|
+
I,14,9.96
|
|
8
|
+
I,6,7.24
|
|
9
|
+
I,4,4.26
|
|
10
|
+
I,12,10.84
|
|
11
|
+
I,7,4.82
|
|
12
|
+
I,5,5.68
|
|
13
|
+
II,10,9.14
|
|
14
|
+
II,8,8.14
|
|
15
|
+
II,13,8.74
|
|
16
|
+
II,9,8.77
|
|
17
|
+
II,11,9.26
|
|
18
|
+
II,14,8.1
|
|
19
|
+
II,6,6.13
|
|
20
|
+
II,4,3.1
|
|
21
|
+
II,12,9.13
|
|
22
|
+
II,7,7.26
|
|
23
|
+
II,5,4.74
|
|
24
|
+
III,10,7.46
|
|
25
|
+
III,8,6.77
|
|
26
|
+
III,13,12.74
|
|
27
|
+
III,9,7.11
|
|
28
|
+
III,11,7.81
|
|
29
|
+
III,14,8.84
|
|
30
|
+
III,6,6.08
|
|
31
|
+
III,4,5.39
|
|
32
|
+
III,12,8.15
|
|
33
|
+
III,7,6.42
|
|
34
|
+
III,5,5.73
|
|
35
|
+
IV,8,6.58
|
|
36
|
+
IV,8,5.76
|
|
37
|
+
IV,8,7.71
|
|
38
|
+
IV,8,8.84
|
|
39
|
+
IV,8,8.47
|
|
40
|
+
IV,8,7.04
|
|
41
|
+
IV,8,5.25
|
|
42
|
+
IV,19,12.5
|
|
43
|
+
IV,8,5.56
|
|
44
|
+
IV,8,7.91
|
|
45
|
+
IV,8,6.89
|
plotnine/doctools.py
CHANGED
|
@@ -188,7 +188,7 @@ def dict_to_table(header: tuple[str, str], contents: dict[str, str]) -> str:
|
|
|
188
188
|
fill `None`
|
|
189
189
|
"""
|
|
190
190
|
rows = [
|
|
191
|
-
(name, value if value == "" else f"`{value!r}`
|
|
191
|
+
(name, value if value == "" else f"`{value!r}`{{.py}}")
|
|
192
192
|
for name, value in contents.items()
|
|
193
193
|
]
|
|
194
194
|
return table_function(rows, headers=header, tablefmt="grid")
|
|
@@ -451,7 +451,7 @@ def document_geom(geom: type[geom]) -> type[geom]:
|
|
|
451
451
|
table = dict_to_table(("Aesthetic", "Default value"), contents)
|
|
452
452
|
aesthetics_table = AESTHETICS_TABLE_TPL.format(table=table)
|
|
453
453
|
tpl = dedent(geom._aesthetics_doc).strip()
|
|
454
|
-
aesthetics_doc = tpl.
|
|
454
|
+
aesthetics_doc = tpl.replace("{aesthetics_table}", aesthetics_table)
|
|
455
455
|
aesthetics_doc = indent(aesthetics_doc, " " * 4)
|
|
456
456
|
|
|
457
457
|
# common_parameters
|
plotnine/facets/facet.py
CHANGED
|
@@ -20,9 +20,9 @@ if typing.TYPE_CHECKING:
|
|
|
20
20
|
import numpy.typing as npt
|
|
21
21
|
from matplotlib.axes import Axes
|
|
22
22
|
from matplotlib.figure import Figure
|
|
23
|
-
from matplotlib.gridspec import GridSpec
|
|
24
23
|
|
|
25
24
|
from plotnine import ggplot, theme
|
|
25
|
+
from plotnine._mpl.gridspec import p9GridSpec
|
|
26
26
|
from plotnine.coords.coord import coord
|
|
27
27
|
from plotnine.facets.labelling import CanBeStripLabellingFunc
|
|
28
28
|
from plotnine.facets.layout import Layout
|
|
@@ -93,6 +93,7 @@ class facet:
|
|
|
93
93
|
|
|
94
94
|
# Axes
|
|
95
95
|
axs: list[Axes]
|
|
96
|
+
_panels_gridspec: p9GridSpec
|
|
96
97
|
|
|
97
98
|
# ggplot object that the facet belongs to
|
|
98
99
|
plot: ggplot
|
|
@@ -100,8 +101,6 @@ class facet:
|
|
|
100
101
|
# Facet strips
|
|
101
102
|
strips: Strips
|
|
102
103
|
|
|
103
|
-
grid_spec: GridSpec
|
|
104
|
-
|
|
105
104
|
# The plot environment
|
|
106
105
|
environment: Environment
|
|
107
106
|
|
|
@@ -121,33 +120,39 @@ class facet:
|
|
|
121
120
|
self.as_table = as_table
|
|
122
121
|
self.drop = drop
|
|
123
122
|
self.dir = dir
|
|
123
|
+
allowed_scales = ["fixed", "free", "free_x", "free_y"]
|
|
124
|
+
if scales not in allowed_scales:
|
|
125
|
+
raise ValueError(
|
|
126
|
+
"Argument `scales` must be one of {allowed_scales}."
|
|
127
|
+
)
|
|
124
128
|
self.free = {
|
|
125
129
|
"x": scales in ("free_x", "free"),
|
|
126
130
|
"y": scales in ("free_y", "free"),
|
|
127
131
|
}
|
|
128
132
|
|
|
129
|
-
def __radd__(self,
|
|
133
|
+
def __radd__(self, other: ggplot) -> ggplot:
|
|
130
134
|
"""
|
|
131
135
|
Add facet to ggplot object
|
|
132
136
|
"""
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return
|
|
137
|
+
other.facet = copy(self)
|
|
138
|
+
other.facet.environment = other.environment
|
|
139
|
+
return other
|
|
136
140
|
|
|
137
141
|
def setup(self, plot: ggplot):
|
|
138
142
|
self.plot = plot
|
|
139
143
|
self.layout = plot.layout
|
|
144
|
+
self.figure = plot.figure
|
|
140
145
|
|
|
141
|
-
if hasattr(plot, "
|
|
142
|
-
self.
|
|
146
|
+
if hasattr(plot, "axs"):
|
|
147
|
+
self.axs = plot.axs
|
|
143
148
|
else:
|
|
144
|
-
self.
|
|
149
|
+
self.axs = self._make_axes()
|
|
145
150
|
|
|
146
151
|
self.coordinates = plot.coordinates
|
|
147
152
|
self.theme = plot.theme
|
|
148
153
|
self.layout.axs = self.axs
|
|
149
154
|
self.strips = Strips.from_facet(self)
|
|
150
|
-
return self.
|
|
155
|
+
return self.axs
|
|
151
156
|
|
|
152
157
|
def setup_data(self, data: list[pd.DataFrame]) -> list[pd.DataFrame]:
|
|
153
158
|
"""
|
|
@@ -346,17 +351,8 @@ class facet:
|
|
|
346
351
|
ax.xaxis.set_major_formatter(MyFixedFormatter(panel_params.x.labels))
|
|
347
352
|
ax.yaxis.set_major_formatter(MyFixedFormatter(panel_params.y.labels))
|
|
348
353
|
|
|
349
|
-
pad_x = (
|
|
350
|
-
|
|
351
|
-
if (margin := theme.getp(("axis_text_x", "margin")))
|
|
352
|
-
else 0
|
|
353
|
-
)
|
|
354
|
-
|
|
355
|
-
pad_y = (
|
|
356
|
-
margin.get_as("r", "pt")
|
|
357
|
-
if (margin := theme.getp(("axis_text_y", "margin")))
|
|
358
|
-
else 0
|
|
359
|
-
)
|
|
354
|
+
pad_x = theme.get_margin("axis_text_x").pt.t
|
|
355
|
+
pad_y = theme.get_margin("axis_text_y").pt.r
|
|
360
356
|
|
|
361
357
|
ax.tick_params(axis="x", which="major", pad=pad_x)
|
|
362
358
|
ax.tick_params(axis="y", which="major", pad=pad_y)
|
|
@@ -372,7 +368,7 @@ class facet:
|
|
|
372
368
|
new = result.__dict__
|
|
373
369
|
|
|
374
370
|
# don't make a deepcopy of the figure & the axes
|
|
375
|
-
shallow = {"
|
|
371
|
+
shallow = {"axs", "first_ax", "last_ax"}
|
|
376
372
|
for key, item in old.items():
|
|
377
373
|
if key in shallow:
|
|
378
374
|
new[key] = item
|
|
@@ -382,35 +378,31 @@ class facet:
|
|
|
382
378
|
|
|
383
379
|
return result
|
|
384
380
|
|
|
385
|
-
def
|
|
381
|
+
def _get_panels_gridspec(self) -> p9GridSpec:
|
|
386
382
|
"""
|
|
387
|
-
Create
|
|
383
|
+
Create gridspec for the panels
|
|
388
384
|
"""
|
|
389
|
-
|
|
390
|
-
from matplotlib.gridspec import GridSpec
|
|
385
|
+
from plotnine._mpl.gridspec import p9GridSpec
|
|
391
386
|
|
|
392
|
-
return
|
|
387
|
+
return p9GridSpec(
|
|
388
|
+
self.nrow, self.ncol, self.figure, nest_into=self.plot._gridspec[0]
|
|
389
|
+
)
|
|
393
390
|
|
|
394
|
-
def
|
|
391
|
+
def _make_axes(self) -> list[Axes]:
|
|
395
392
|
"""
|
|
396
|
-
Create and return
|
|
393
|
+
Create and return subplot axes
|
|
397
394
|
"""
|
|
398
395
|
num_panels = len(self.layout.layout)
|
|
399
396
|
axsarr = np.empty((self.nrow, self.ncol), dtype=object)
|
|
400
397
|
|
|
401
|
-
|
|
402
|
-
figure, gs = self._make_figure()
|
|
403
|
-
self.grid_spec = gs
|
|
398
|
+
self._panels_gridspec = self._get_panels_gridspec()
|
|
404
399
|
|
|
405
400
|
# Create axes
|
|
406
401
|
it = itertools.product(range(self.nrow), range(self.ncol))
|
|
407
402
|
for i, (row, col) in enumerate(it):
|
|
408
|
-
axsarr[row, col] = figure.add_subplot(
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
# figure.add_subplot(gs[i])
|
|
412
|
-
# for i in range(self.nrow * self.ncol)
|
|
413
|
-
# ]).reshape((self.nrow, self.ncol))
|
|
403
|
+
axsarr[row, col] = self.figure.add_subplot(
|
|
404
|
+
self._panels_gridspec[i]
|
|
405
|
+
)
|
|
414
406
|
|
|
415
407
|
# Rearrange axes
|
|
416
408
|
# They are ordered to match the positions in the layout table
|
|
@@ -429,9 +421,9 @@ class facet:
|
|
|
429
421
|
|
|
430
422
|
# Delete unused axes
|
|
431
423
|
for ax in axs[num_panels:]:
|
|
432
|
-
figure.delaxes(ax)
|
|
424
|
+
self.figure.delaxes(ax)
|
|
433
425
|
axs = axs[:num_panels]
|
|
434
|
-
return
|
|
426
|
+
return list(axs)
|
|
435
427
|
|
|
436
428
|
def _aspect_ratio(self) -> Optional[float]:
|
|
437
429
|
"""
|
|
@@ -477,8 +469,7 @@ def combine_vars(
|
|
|
477
469
|
has_all = [x.shape[1] == len(vars) for x in values]
|
|
478
470
|
if not any(has_all):
|
|
479
471
|
raise PlotnineError(
|
|
480
|
-
"At least one layer must contain all variables "
|
|
481
|
-
"used for facetting"
|
|
472
|
+
"At least one layer must contain all variables used for facetting"
|
|
482
473
|
)
|
|
483
474
|
base = pd.concat([x for i, x in enumerate(values) if has_all[i]], axis=0)
|
|
484
475
|
base = base.drop_duplicates()
|
plotnine/facets/facet_grid.py
CHANGED
|
@@ -107,9 +107,11 @@ class facet_grid(facet):
|
|
|
107
107
|
self.space = space
|
|
108
108
|
self.margins = margins
|
|
109
109
|
|
|
110
|
-
def
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
def _get_panels_gridspec(self):
|
|
111
|
+
"""
|
|
112
|
+
Create gridspec for the panels
|
|
113
|
+
"""
|
|
114
|
+
from plotnine._mpl.gridspec import p9GridSpec
|
|
113
115
|
|
|
114
116
|
layout = self.layout
|
|
115
117
|
space = self.space
|
|
@@ -155,7 +157,13 @@ class facet_grid(facet):
|
|
|
155
157
|
ratios["width_ratios"] = self.space.get("x")
|
|
156
158
|
ratios["height_ratios"] = self.space.get("y")
|
|
157
159
|
|
|
158
|
-
return
|
|
160
|
+
return p9GridSpec(
|
|
161
|
+
self.nrow,
|
|
162
|
+
self.ncol,
|
|
163
|
+
self.figure,
|
|
164
|
+
nest_into=self.plot._gridspec[0],
|
|
165
|
+
**ratios,
|
|
166
|
+
)
|
|
159
167
|
|
|
160
168
|
def compute_layout(self, data: list[pd.DataFrame]) -> pd.DataFrame:
|
|
161
169
|
if not self.rows and not self.cols:
|
|
@@ -302,7 +310,7 @@ def parse_grid_facets_old(
|
|
|
302
310
|
"((var1, var2), (var3, var4))",
|
|
303
311
|
]
|
|
304
312
|
error_msg_s = (
|
|
305
|
-
"Valid sequences for specifying 'facets' look like
|
|
313
|
+
f"Valid sequences for specifying 'facets' look like {valid_seqs}"
|
|
306
314
|
)
|
|
307
315
|
|
|
308
316
|
valid_forms = [
|
|
@@ -314,7 +322,7 @@ def parse_grid_facets_old(
|
|
|
314
322
|
". ~ func(var1) + func(var2)",
|
|
315
323
|
". ~ func(var1+var3) + func(var2)",
|
|
316
324
|
] + valid_seqs
|
|
317
|
-
error_msg_f = "Valid formula for 'facet_grid' look like
|
|
325
|
+
error_msg_f = f"Valid formula for 'facet_grid' look like {valid_forms}"
|
|
318
326
|
|
|
319
327
|
if not isinstance(facets, str):
|
|
320
328
|
if len(facets) == 1:
|
plotnine/facets/facet_wrap.py
CHANGED
|
@@ -198,8 +198,7 @@ def check_dimensions(
|
|
|
198
198
|
if nrow is not None:
|
|
199
199
|
if nrow < 1:
|
|
200
200
|
warn(
|
|
201
|
-
"'nrow' must be greater than 0. "
|
|
202
|
-
"Your value has been ignored.",
|
|
201
|
+
"'nrow' must be greater than 0. Your value has been ignored.",
|
|
203
202
|
PlotnineWarning,
|
|
204
203
|
)
|
|
205
204
|
nrow = None
|
|
@@ -209,8 +208,7 @@ def check_dimensions(
|
|
|
209
208
|
if ncol is not None:
|
|
210
209
|
if ncol < 1:
|
|
211
210
|
warn(
|
|
212
|
-
"'ncol' must be greater than 0. "
|
|
213
|
-
"Your value has been ignored.",
|
|
211
|
+
"'ncol' must be greater than 0. Your value has been ignored.",
|
|
214
212
|
PlotnineWarning,
|
|
215
213
|
)
|
|
216
214
|
ncol = None
|
|
@@ -241,7 +239,7 @@ def parse_wrap_facets_old(facets: str | Sequence[str]) -> Sequence[str]:
|
|
|
241
239
|
This handles the old & silently deprecated r-style formulas
|
|
242
240
|
"""
|
|
243
241
|
valid_forms = ["~ var1", "~ var1 + var2"]
|
|
244
|
-
error_msg = "Valid formula for 'facet_wrap' look like
|
|
242
|
+
error_msg = f"Valid formula for 'facet_wrap' look like {valid_forms}"
|
|
245
243
|
|
|
246
244
|
if isinstance(facets, (list, tuple)):
|
|
247
245
|
return facets
|
plotnine/facets/strips.py
CHANGED
|
@@ -63,62 +63,49 @@ class strip:
|
|
|
63
63
|
"""
|
|
64
64
|
theme = self.theme
|
|
65
65
|
position = self.position
|
|
66
|
+
|
|
66
67
|
if position == "top":
|
|
67
68
|
# The x & y values are just starting locations
|
|
68
69
|
# The final location is determined by the layout manager.
|
|
69
|
-
|
|
70
|
-
ha
|
|
70
|
+
bg_y = 1
|
|
71
|
+
ha = theme.getp(("strip_text_x", "ha"), "center")
|
|
72
|
+
va = theme.getp(("strip_text_x", "va"), "center")
|
|
71
73
|
rotation = theme.getp(("strip_text_x", "rotation"))
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# TODO: Allow two unique paddings for either side.
|
|
75
|
-
# Requires implementing an mpl.patches.boxstyle that recognises
|
|
76
|
-
# two padding values.
|
|
77
|
-
|
|
78
|
-
strip_text_margin = (
|
|
79
|
-
margin.get_as("b", "lines")
|
|
80
|
-
if (margin := theme.getp(("strip_text_x", "margin")))
|
|
81
|
-
else 0
|
|
82
|
-
)
|
|
74
|
+
bg_height = 0 # Determined by the text size
|
|
75
|
+
margin = theme.getp(("strip_text_x", "margin")).to("lines")
|
|
83
76
|
strip_align = theme.getp("strip_align_x")
|
|
84
77
|
|
|
85
78
|
# x & width properties of the background slide and
|
|
86
79
|
# shrink the strip horizontally.
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
bg_x = theme.getp(("strip_text_x", "x"), 0)
|
|
81
|
+
bg_width = theme.getp(("strip_background_x", "width"), 1)
|
|
89
82
|
|
|
90
83
|
elif position == "right":
|
|
91
84
|
# The x & y values are just starting locations
|
|
92
85
|
# The final location is determined by the layout manager.
|
|
93
|
-
|
|
94
|
-
ha
|
|
86
|
+
bg_x = 1
|
|
87
|
+
ha = theme.getp(("strip_text_y", "ha"), "center")
|
|
88
|
+
va = theme.getp(("strip_text_y", "va"), "center")
|
|
95
89
|
rotation = theme.getp(("strip_text_y", "rotation"))
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
# Requires implementing an mpl.patches.boxstyle that recognises
|
|
99
|
-
# two padding values.
|
|
100
|
-
strip_text_margin = (
|
|
101
|
-
margin.get_as("r", "lines")
|
|
102
|
-
if (margin := theme.getp(("strip_text_y", "margin")))
|
|
103
|
-
else 0
|
|
104
|
-
)
|
|
90
|
+
bg_width = 0 # Determine by the text height
|
|
91
|
+
margin = theme.getp(("strip_text_y", "margin")).to("lines")
|
|
105
92
|
strip_align = theme.getp("strip_align_y")
|
|
106
93
|
|
|
107
94
|
# y & height properties of the background slide and
|
|
108
95
|
# shrink the strip vertically.
|
|
109
|
-
|
|
110
|
-
|
|
96
|
+
bg_y = theme.getp(("strip_text_y", "y"), 0)
|
|
97
|
+
bg_height = theme.getp(("strip_background_y", "height"), 1)
|
|
111
98
|
else:
|
|
112
99
|
raise ValueError(f"Unknown position for strip text: {position!r}")
|
|
113
100
|
|
|
114
101
|
return strip_draw_info(
|
|
115
|
-
|
|
116
|
-
|
|
102
|
+
bg_x=bg_x,
|
|
103
|
+
bg_y=bg_y,
|
|
117
104
|
ha=ha,
|
|
118
105
|
va=va,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
106
|
+
bg_width=bg_width,
|
|
107
|
+
bg_height=bg_height,
|
|
108
|
+
margin=margin,
|
|
122
109
|
strip_align=strip_align,
|
|
123
110
|
position=position,
|
|
124
111
|
label=self.label_info.text(),
|
plotnine/geoms/annotate.py
CHANGED
|
@@ -131,12 +131,12 @@ class annotate:
|
|
|
131
131
|
**kwargs,
|
|
132
132
|
)
|
|
133
133
|
|
|
134
|
-
def __radd__(self,
|
|
134
|
+
def __radd__(self, other: ggplot) -> ggplot:
|
|
135
135
|
"""
|
|
136
136
|
Add to ggplot
|
|
137
137
|
"""
|
|
138
|
-
|
|
139
|
-
return
|
|
138
|
+
other += self.to_layer() # Add layer
|
|
139
|
+
return other
|
|
140
140
|
|
|
141
141
|
def to_layer(self) -> layer:
|
|
142
142
|
"""
|