plotnine 0.15.0.dev3__py3-none-any.whl → 0.15.2__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 +2 -0
- plotnine/_mpl/layout_manager/_engine.py +1 -1
- plotnine/_mpl/layout_manager/_layout_items.py +126 -41
- plotnine/_mpl/layout_manager/_layout_tree.py +712 -314
- plotnine/_mpl/layout_manager/_spaces.py +305 -101
- plotnine/_mpl/patches.py +70 -34
- plotnine/_mpl/text.py +144 -63
- plotnine/_mpl/utils.py +1 -1
- plotnine/_utils/__init__.py +50 -107
- plotnine/_utils/context.py +78 -2
- plotnine/_utils/ipython.py +35 -51
- plotnine/_utils/quarto.py +26 -0
- plotnine/_utils/yippie.py +115 -0
- plotnine/composition/__init__.py +11 -0
- plotnine/composition/_beside.py +55 -0
- plotnine/composition/_compose.py +471 -0
- plotnine/composition/_plot_spacer.py +60 -0
- plotnine/composition/_stack.py +55 -0
- plotnine/coords/coord.py +3 -3
- plotnine/data/__init__.py +31 -0
- plotnine/data/anscombe-quartet.csv +45 -0
- plotnine/doctools.py +4 -4
- plotnine/facets/facet.py +4 -4
- plotnine/facets/strips.py +17 -28
- plotnine/geoms/annotate.py +13 -13
- plotnine/geoms/annotation_logticks.py +7 -8
- plotnine/geoms/annotation_stripes.py +6 -6
- plotnine/geoms/geom.py +60 -27
- plotnine/geoms/geom_abline.py +3 -2
- plotnine/geoms/geom_area.py +2 -2
- plotnine/geoms/geom_bar.py +1 -0
- plotnine/geoms/geom_bin_2d.py +6 -2
- plotnine/geoms/geom_blank.py +0 -3
- plotnine/geoms/geom_boxplot.py +8 -4
- plotnine/geoms/geom_col.py +2 -2
- plotnine/geoms/geom_count.py +6 -2
- plotnine/geoms/geom_crossbar.py +3 -3
- plotnine/geoms/geom_density_2d.py +6 -2
- plotnine/geoms/geom_dotplot.py +2 -2
- plotnine/geoms/geom_errorbar.py +2 -2
- plotnine/geoms/geom_errorbarh.py +2 -2
- plotnine/geoms/geom_histogram.py +1 -1
- plotnine/geoms/geom_hline.py +3 -2
- plotnine/geoms/geom_linerange.py +2 -2
- plotnine/geoms/geom_map.py +5 -5
- plotnine/geoms/geom_path.py +11 -12
- plotnine/geoms/geom_point.py +4 -5
- plotnine/geoms/geom_pointdensity.py +4 -0
- plotnine/geoms/geom_pointrange.py +3 -5
- plotnine/geoms/geom_polygon.py +2 -3
- 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_raster.py +4 -5
- plotnine/geoms/geom_rect.py +3 -4
- plotnine/geoms/geom_ribbon.py +7 -7
- plotnine/geoms/geom_rug.py +1 -1
- plotnine/geoms/geom_segment.py +2 -2
- plotnine/geoms/geom_sina.py +3 -3
- plotnine/geoms/geom_smooth.py +7 -3
- plotnine/geoms/geom_step.py +2 -2
- plotnine/geoms/geom_text.py +2 -3
- plotnine/geoms/geom_violin.py +8 -5
- plotnine/geoms/geom_vline.py +3 -2
- plotnine/ggplot.py +64 -85
- plotnine/guides/guide.py +7 -10
- plotnine/guides/guide_colorbar.py +3 -3
- plotnine/guides/guide_legend.py +3 -3
- plotnine/guides/guides.py +6 -6
- plotnine/helpers.py +49 -0
- plotnine/iapi.py +28 -5
- plotnine/labels.py +3 -3
- plotnine/layer.py +36 -19
- plotnine/mapping/_atomic.py +178 -0
- plotnine/mapping/_env.py +13 -2
- plotnine/mapping/_eval_environment.py +1 -1
- plotnine/mapping/aes.py +85 -49
- plotnine/scales/__init__.py +2 -0
- plotnine/scales/limits.py +7 -7
- plotnine/scales/scale.py +3 -3
- plotnine/scales/scale_color.py +82 -18
- plotnine/scales/scale_continuous.py +6 -4
- plotnine/scales/scale_datetime.py +28 -14
- plotnine/scales/scale_discrete.py +1 -1
- plotnine/scales/scale_identity.py +21 -2
- plotnine/scales/scale_manual.py +8 -2
- plotnine/scales/scale_xy.py +2 -2
- plotnine/stats/binning.py +4 -1
- plotnine/stats/smoothers.py +23 -36
- plotnine/stats/stat.py +20 -32
- plotnine/stats/stat_bin.py +6 -5
- plotnine/stats/stat_bin_2d.py +11 -9
- plotnine/stats/stat_bindot.py +13 -16
- plotnine/stats/stat_boxplot.py +6 -6
- plotnine/stats/stat_count.py +6 -9
- plotnine/stats/stat_density.py +7 -10
- plotnine/stats/stat_density_2d.py +12 -8
- plotnine/stats/stat_ecdf.py +7 -6
- plotnine/stats/stat_ellipse.py +9 -6
- plotnine/stats/stat_function.py +10 -8
- plotnine/stats/stat_hull.py +6 -3
- plotnine/stats/stat_identity.py +5 -2
- plotnine/stats/stat_pointdensity.py +5 -7
- plotnine/stats/stat_qq.py +46 -20
- plotnine/stats/stat_qq_line.py +16 -11
- plotnine/stats/stat_quantile.py +15 -9
- plotnine/stats/stat_sina.py +13 -15
- plotnine/stats/stat_smooth.py +8 -10
- plotnine/stats/stat_sum.py +5 -2
- plotnine/stats/stat_summary.py +7 -10
- plotnine/stats/stat_summary_bin.py +11 -14
- plotnine/stats/stat_unique.py +5 -2
- plotnine/stats/stat_ydensity.py +8 -11
- plotnine/themes/elements/__init__.py +2 -1
- plotnine/themes/elements/element_line.py +17 -9
- plotnine/themes/elements/margin.py +64 -1
- plotnine/themes/theme.py +9 -1
- 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 +6 -5
- plotnine/themes/theme_light.py +1 -1
- plotnine/themes/theme_matplotlib.py +5 -5
- plotnine/themes/theme_seaborn.py +7 -4
- plotnine/themes/theme_void.py +9 -8
- plotnine/themes/theme_xkcd.py +0 -1
- plotnine/themes/themeable.py +109 -31
- plotnine/typing.py +17 -6
- plotnine/watermark.py +3 -3
- {plotnine-0.15.0.dev3.dist-info → plotnine-0.15.2.dist-info}/METADATA +13 -6
- plotnine-0.15.2.dist-info/RECORD +221 -0
- {plotnine-0.15.0.dev3.dist-info → plotnine-0.15.2.dist-info}/WHEEL +1 -1
- plotnine/plot_composition/__init__.py +0 -10
- plotnine/plot_composition/_compose.py +0 -436
- plotnine/plot_composition/_spacer.py +0 -32
- plotnine-0.15.0.dev3.dist-info/RECORD +0 -215
- /plotnine/{plot_composition → composition}/_plotspec.py +0 -0
- {plotnine-0.15.0.dev3.dist-info → plotnine-0.15.2.dist-info}/licenses/LICENSE +0 -0
- {plotnine-0.15.0.dev3.dist-info → plotnine-0.15.2.dist-info}/top_level.txt +0 -0
plotnine/scales/scale_color.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from dataclasses import KW_ONLY, InitVar, dataclass
|
|
3
|
+
from dataclasses import KW_ONLY, InitVar, dataclass, field
|
|
4
4
|
from typing import Literal, Sequence
|
|
5
5
|
from warnings import warn
|
|
6
6
|
|
|
@@ -48,36 +48,72 @@ class _scale_color_continuous(
|
|
|
48
48
|
class scale_color_hue(_scale_color_discrete):
|
|
49
49
|
"""
|
|
50
50
|
Qualitative color scale with evenly spaced hues
|
|
51
|
+
|
|
52
|
+
See Also
|
|
53
|
+
--------
|
|
54
|
+
mizani.palettes.hue_pal : The palette class that generates colours
|
|
55
|
+
in HCL space.
|
|
51
56
|
"""
|
|
52
57
|
|
|
53
|
-
h: InitVar[float] =
|
|
58
|
+
h: InitVar[float | tuple[float, float]] = 15
|
|
54
59
|
"""
|
|
55
|
-
Hue.
|
|
60
|
+
Hue. If a float, it is the first hue value, in the range `[0, 360]`.
|
|
61
|
+
The range of the palette will be `[first, first + 360)`.
|
|
62
|
+
|
|
63
|
+
If a tuple, it is the range `[first, last)` of the hues.
|
|
56
64
|
"""
|
|
57
65
|
|
|
58
|
-
|
|
66
|
+
c: InitVar[float] = 100
|
|
59
67
|
"""
|
|
60
|
-
|
|
68
|
+
Chroma. Must be in the range `[0, 100]`
|
|
61
69
|
"""
|
|
62
70
|
|
|
63
|
-
|
|
71
|
+
l: InitVar[float] = 65
|
|
64
72
|
"""
|
|
65
|
-
|
|
73
|
+
Lightness. Must be in the range [0, 100]
|
|
66
74
|
"""
|
|
67
75
|
|
|
68
|
-
|
|
76
|
+
direction: InitVar[Literal[1, -1]] = 1
|
|
69
77
|
"""
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
or [hsluv](https://www.hsluv.org/).
|
|
73
|
-
https://www.hsluv.org/
|
|
78
|
+
The order of colours in the scale. If -1 the order
|
|
79
|
+
of colours is reversed. The default is 1.
|
|
74
80
|
"""
|
|
75
81
|
|
|
76
|
-
|
|
82
|
+
_: KW_ONLY
|
|
83
|
+
|
|
84
|
+
s: None = field(default=None, repr=False)
|
|
85
|
+
"""
|
|
86
|
+
Not being used and will be removed in a future version
|
|
87
|
+
"""
|
|
88
|
+
color_space: None = field(default=None, repr=False)
|
|
89
|
+
"""
|
|
90
|
+
Not being used and will be removed in a future version
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
def __post_init__(self, h, c, l, direction):
|
|
77
94
|
from mizani.palettes import hue_pal
|
|
78
95
|
|
|
96
|
+
if (s := self.s) is not None:
|
|
97
|
+
warn(
|
|
98
|
+
f"You used {s=} for the saturation which has been ignored. "
|
|
99
|
+
f"{self.__class__.__name__} now works in HCL colorspace. "
|
|
100
|
+
f"Using `s` in future versions will throw an exception.",
|
|
101
|
+
FutureWarning,
|
|
102
|
+
)
|
|
103
|
+
del self.s
|
|
104
|
+
|
|
105
|
+
if (color_space := self.color_space) is not None:
|
|
106
|
+
warn(
|
|
107
|
+
f"You used {color_space=} to select a color_space and it "
|
|
108
|
+
f"has been ignored. {self.__class__.__name__} now only works "
|
|
109
|
+
f"in HCL colorspace. Using `color_space` in future versions "
|
|
110
|
+
"will throw an exception.",
|
|
111
|
+
FutureWarning,
|
|
112
|
+
)
|
|
113
|
+
del self.color_space
|
|
114
|
+
|
|
79
115
|
super().__post_init__()
|
|
80
|
-
self.palette = hue_pal(h,
|
|
116
|
+
self.palette = hue_pal(h, c, l, direction)
|
|
81
117
|
|
|
82
118
|
|
|
83
119
|
@dataclass
|
|
@@ -95,6 +131,11 @@ class scale_color_brewer(_scale_color_discrete):
|
|
|
95
131
|
Sequential, diverging and qualitative discrete color scales
|
|
96
132
|
|
|
97
133
|
See `colorbrewer.org <http://colorbrewer2.org/>`_
|
|
134
|
+
|
|
135
|
+
See Also
|
|
136
|
+
--------
|
|
137
|
+
mizani.palette.brewer_pal : The palette class that generates colours
|
|
138
|
+
that generates the brewer colors.
|
|
98
139
|
"""
|
|
99
140
|
|
|
100
141
|
type: InitVar[
|
|
@@ -146,6 +187,11 @@ class scale_fill_brewer(scale_color_brewer):
|
|
|
146
187
|
class scale_color_grey(_scale_color_discrete):
|
|
147
188
|
"""
|
|
148
189
|
Sequential grey color scale.
|
|
190
|
+
|
|
191
|
+
See Also
|
|
192
|
+
--------
|
|
193
|
+
mizani.palettes.grey_pal : The palette class that generates colours
|
|
194
|
+
gray scale color.
|
|
149
195
|
"""
|
|
150
196
|
|
|
151
197
|
start: InitVar[float] = 0.2
|
|
@@ -188,6 +234,8 @@ class scale_color_gradient(_scale_color_continuous):
|
|
|
188
234
|
--------
|
|
189
235
|
plotnine.scale_color_gradient2
|
|
190
236
|
plotnine.scale_color_gradientn
|
|
237
|
+
mizani.palettes.gradient_n_pal : The palette class that generates
|
|
238
|
+
the colour gradient.
|
|
191
239
|
"""
|
|
192
240
|
|
|
193
241
|
low: InitVar[str] = "#132B43"
|
|
@@ -220,6 +268,11 @@ class scale_fill_gradient(scale_color_gradient):
|
|
|
220
268
|
class scale_color_desaturate(_scale_color_continuous):
|
|
221
269
|
"""
|
|
222
270
|
Create a desaturated color gradient
|
|
271
|
+
|
|
272
|
+
See Also
|
|
273
|
+
--------
|
|
274
|
+
mizani.palettes.desaturate_pal : The palette class that generates
|
|
275
|
+
the desaturated colours.
|
|
223
276
|
"""
|
|
224
277
|
|
|
225
278
|
color: InitVar[str] = "red"
|
|
@@ -263,6 +316,8 @@ class scale_color_gradient2(_scale_color_continuous):
|
|
|
263
316
|
--------
|
|
264
317
|
plotnine.scale_color_gradient
|
|
265
318
|
plotnine.scale_color_gradientn
|
|
319
|
+
mizani.palettes.gradient_n_pal : The palette class that generates
|
|
320
|
+
the colour gradient.
|
|
266
321
|
"""
|
|
267
322
|
|
|
268
323
|
low: InitVar[str] = "#832424"
|
|
@@ -316,9 +371,11 @@ class scale_color_gradientn(_scale_color_continuous):
|
|
|
316
371
|
--------
|
|
317
372
|
plotnine.scale_color_gradient
|
|
318
373
|
plotnine.scale_color_gradientn
|
|
374
|
+
mizani.palettes.gradient_n_pal : The palette class that generates
|
|
375
|
+
the colour gradient.
|
|
319
376
|
"""
|
|
320
377
|
|
|
321
|
-
colors: InitVar[Sequence[str]]
|
|
378
|
+
colors: InitVar[Sequence[str]]
|
|
322
379
|
"""
|
|
323
380
|
List of colors
|
|
324
381
|
"""
|
|
@@ -332,8 +389,8 @@ class scale_color_gradientn(_scale_color_continuous):
|
|
|
332
389
|
def __post_init__(self, colors, values):
|
|
333
390
|
from mizani.palettes import gradient_n_pal
|
|
334
391
|
|
|
335
|
-
self.palette = gradient_n_pal(colors, values)
|
|
336
392
|
super().__post_init__()
|
|
393
|
+
self.palette = gradient_n_pal(colors, values)
|
|
337
394
|
|
|
338
395
|
|
|
339
396
|
@dataclass
|
|
@@ -430,6 +487,8 @@ class scale_color_cmap(_scale_color_continuous):
|
|
|
430
487
|
--------
|
|
431
488
|
[](`matplotlib.cm`)
|
|
432
489
|
[](`matplotlib.colors`)
|
|
490
|
+
mizani.palettes.cmap_pal : The palette class that generates
|
|
491
|
+
the colour gradients of this scale.
|
|
433
492
|
"""
|
|
434
493
|
|
|
435
494
|
cmap_name: InitVar[str] = "viridis"
|
|
@@ -457,7 +516,7 @@ class scale_fill_cmap(scale_color_cmap):
|
|
|
457
516
|
|
|
458
517
|
|
|
459
518
|
@dataclass
|
|
460
|
-
class scale_color_cmap_d(
|
|
519
|
+
class scale_color_cmap_d(_scale_color_discrete):
|
|
461
520
|
"""
|
|
462
521
|
A discrete color scales using Matplotlib colormaps
|
|
463
522
|
|
|
@@ -465,6 +524,8 @@ class scale_color_cmap_d(scale_discrete):
|
|
|
465
524
|
--------
|
|
466
525
|
[](`matplotlib.cm`)
|
|
467
526
|
[](`matplotlib.colors`)
|
|
527
|
+
mizani.palettes.cmap_pal : The palette class that generates
|
|
528
|
+
the colours of this scale.
|
|
468
529
|
"""
|
|
469
530
|
|
|
470
531
|
cmap_name: InitVar[str] = "viridis"
|
|
@@ -474,7 +535,6 @@ class scale_color_cmap_d(scale_discrete):
|
|
|
474
535
|
`matplotlib.cm.cmap_d.keys()` or see the
|
|
475
536
|
`documentation <http://matplotlib.org/users/colormaps.html>`_.
|
|
476
537
|
"""
|
|
477
|
-
_aesthetics = ["color"]
|
|
478
538
|
|
|
479
539
|
def __post_init__(self, cmap_name):
|
|
480
540
|
from mizani.palettes import cmap_d_pal
|
|
@@ -496,6 +556,10 @@ class scale_fill_cmap_d(scale_color_cmap_d):
|
|
|
496
556
|
class scale_color_datetime(scale_datetime, scale_color_cmap): # pyright: ignore[reportIncompatibleVariableOverride]
|
|
497
557
|
"""
|
|
498
558
|
Datetime color scale
|
|
559
|
+
|
|
560
|
+
See Also
|
|
561
|
+
--------
|
|
562
|
+
plotnine.scale_color_cmap : The parent class.
|
|
499
563
|
"""
|
|
500
564
|
|
|
501
565
|
_: KW_ONLY
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from contextlib import suppress
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
-
from typing import TYPE_CHECKING, Sequence
|
|
5
|
+
from typing import TYPE_CHECKING, Sequence, cast
|
|
6
6
|
from warnings import warn
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
@@ -387,14 +387,15 @@ class scale_continuous(
|
|
|
387
387
|
limits = self.final_limits
|
|
388
388
|
|
|
389
389
|
x = self.oob(self.rescaler(x, _from=limits))
|
|
390
|
+
na_value = cast("float", self.na_value)
|
|
390
391
|
|
|
391
392
|
uniq = np.unique(x)
|
|
392
393
|
pal = np.asarray(self.palette(uniq))
|
|
393
394
|
scaled = pal[match(x, uniq)]
|
|
394
395
|
if scaled.dtype.kind == "U":
|
|
395
|
-
scaled = [
|
|
396
|
+
scaled = [na_value if x == "nan" else x for x in scaled]
|
|
396
397
|
else:
|
|
397
|
-
scaled[pd.isna(scaled)] =
|
|
398
|
+
scaled[pd.isna(scaled)] = na_value
|
|
398
399
|
return scaled
|
|
399
400
|
|
|
400
401
|
def get_breaks(
|
|
@@ -520,11 +521,12 @@ class scale_continuous(
|
|
|
520
521
|
# When user sets breaks and labels of equal size,
|
|
521
522
|
# but the limits exclude some of the breaks.
|
|
522
523
|
# We remove the corresponding labels
|
|
523
|
-
from collections.abc import Sized
|
|
524
|
+
from collections.abc import Iterable, Sized
|
|
524
525
|
|
|
525
526
|
labels = self.labels
|
|
526
527
|
if (
|
|
527
528
|
len(labels) != len(breaks)
|
|
529
|
+
and isinstance(self.breaks, Iterable)
|
|
528
530
|
and isinstance(self.breaks, Sized)
|
|
529
531
|
and len(labels) == len(self.breaks)
|
|
530
532
|
):
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from dataclasses import KW_ONLY, InitVar, dataclass
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
5
|
+
from warnings import warn
|
|
5
6
|
|
|
6
7
|
from ._runtime_typing import TransUser # noqa: TCH001
|
|
7
8
|
from .scale_continuous import scale_continuous
|
|
@@ -20,24 +21,21 @@ class scale_datetime(scale_continuous):
|
|
|
20
21
|
"""
|
|
21
22
|
A string giving the distance between major breaks.
|
|
22
23
|
For example `'2 weeks'`, `'5 years'`. If specified,
|
|
23
|
-
`date_breaks` takes precedence over
|
|
24
|
-
`breaks`.
|
|
24
|
+
`date_breaks` takes precedence over `breaks`.
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
27
|
date_labels: InitVar[str | None] = None
|
|
28
28
|
"""
|
|
29
29
|
Format string for the labels.
|
|
30
30
|
See [strftime](:ref:`strftime-strptime-behavior`).
|
|
31
|
-
If specified, `date_labels` takes precedence over
|
|
32
|
-
`labels`.
|
|
31
|
+
If specified, `date_labels` takes precedence over `labels`.
|
|
33
32
|
"""
|
|
34
33
|
|
|
35
34
|
date_minor_breaks: InitVar[str | None] = None
|
|
36
35
|
"""
|
|
37
36
|
A string giving the distance between minor breaks.
|
|
38
37
|
For example `'2 weeks'`, `'5 years'`. If specified,
|
|
39
|
-
`date_minor_breaks` takes precedence over
|
|
40
|
-
`minor_breaks`.
|
|
38
|
+
`date_minor_breaks` takes precedence over `minor_breaks`.
|
|
41
39
|
"""
|
|
42
40
|
|
|
43
41
|
_: KW_ONLY
|
|
@@ -80,22 +78,38 @@ class scale_datetime(scale_continuous):
|
|
|
80
78
|
date_labels: str | None,
|
|
81
79
|
date_minor_breaks: str | None,
|
|
82
80
|
):
|
|
83
|
-
from mizani.breaks import
|
|
84
|
-
from mizani.labels import label_date
|
|
81
|
+
from mizani.breaks import breaks_date_width
|
|
82
|
+
from mizani.labels import label_date
|
|
85
83
|
|
|
86
84
|
if date_breaks is not None:
|
|
87
|
-
self.breaks =
|
|
85
|
+
self.breaks = breaks_date_width(date_breaks) # pyright: ignore[reportAttributeAccessIssue]
|
|
88
86
|
elif isinstance(self.breaks, str):
|
|
89
|
-
|
|
87
|
+
warn(
|
|
88
|
+
"Passing a string to `breaks` will not work in "
|
|
89
|
+
f"future versions. Use `date_breaks={self.breaks!r}`",
|
|
90
|
+
FutureWarning,
|
|
91
|
+
)
|
|
92
|
+
self.breaks = breaks_date_width(width=self.breaks) # pyright: ignore[reportAttributeAccessIssue]
|
|
90
93
|
|
|
91
94
|
if date_labels is not None:
|
|
92
|
-
self.labels =
|
|
95
|
+
self.labels = label_date(fmt=date_labels) # pyright: ignore[reportAttributeAccessIssue]
|
|
93
96
|
elif isinstance(self.labels, str):
|
|
94
|
-
|
|
97
|
+
warn(
|
|
98
|
+
"Passing a string to `labels` will not work in "
|
|
99
|
+
f"future versions. Use `date_labels={self.labels!r}`",
|
|
100
|
+
FutureWarning,
|
|
101
|
+
)
|
|
102
|
+
self.labels = label_date(fmt=self.labels) # pyright: ignore[reportAttributeAccessIssue]
|
|
95
103
|
|
|
96
104
|
if date_minor_breaks is not None:
|
|
97
|
-
self.minor_breaks =
|
|
105
|
+
self.minor_breaks = breaks_date_width(date_minor_breaks) # pyright: ignore[reportAttributeAccessIssue]
|
|
98
106
|
elif isinstance(self.minor_breaks, str):
|
|
99
|
-
|
|
107
|
+
warn(
|
|
108
|
+
"Passing a string to `minor_breaks` will not work in "
|
|
109
|
+
"future versions. "
|
|
110
|
+
f"Use `date_minor_breaks={self.minor_breaks!r}`",
|
|
111
|
+
FutureWarning,
|
|
112
|
+
)
|
|
113
|
+
self.minor_breaks = breaks_date_width(width=self.minor_breaks) # pyright: ignore[reportAttributeAccessIssue]
|
|
100
114
|
|
|
101
115
|
scale_continuous.__post_init__(self)
|
|
@@ -156,7 +156,7 @@ class scale_discrete(
|
|
|
156
156
|
range = self.dimension(limits=limits)
|
|
157
157
|
|
|
158
158
|
breaks_d = self.get_breaks(limits)
|
|
159
|
-
breaks = self.map(pd.Categorical(breaks_d))
|
|
159
|
+
breaks = self.map(pd.Categorical(breaks_d)) # pyright: ignore[reportArgumentType]
|
|
160
160
|
minor_breaks = []
|
|
161
161
|
labels = self.get_labels(breaks_d)
|
|
162
162
|
|
|
@@ -43,6 +43,8 @@ class scale_color_identity(MapTrainMixin, scale_discrete):
|
|
|
43
43
|
"""
|
|
44
44
|
|
|
45
45
|
_aesthetics = ["color"]
|
|
46
|
+
_: KW_ONLY
|
|
47
|
+
guide: Literal["legend"] | None = None
|
|
46
48
|
|
|
47
49
|
|
|
48
50
|
@dataclass
|
|
@@ -52,6 +54,8 @@ class scale_fill_identity(scale_color_identity):
|
|
|
52
54
|
"""
|
|
53
55
|
|
|
54
56
|
_aesthetics = ["fill"]
|
|
57
|
+
_: KW_ONLY
|
|
58
|
+
guide: Literal["legend"] | None = None
|
|
55
59
|
|
|
56
60
|
|
|
57
61
|
@dataclass
|
|
@@ -61,6 +65,8 @@ class scale_shape_identity(MapTrainMixin, scale_discrete):
|
|
|
61
65
|
"""
|
|
62
66
|
|
|
63
67
|
_aesthetics = ["shape"]
|
|
68
|
+
_: KW_ONLY
|
|
69
|
+
guide: Literal["legend"] | None = None
|
|
64
70
|
|
|
65
71
|
|
|
66
72
|
@dataclass
|
|
@@ -70,6 +76,8 @@ class scale_linetype_identity(MapTrainMixin, scale_discrete):
|
|
|
70
76
|
"""
|
|
71
77
|
|
|
72
78
|
_aesthetics = ["linetype"]
|
|
79
|
+
_: KW_ONLY
|
|
80
|
+
guide: Literal["legend"] | None = None
|
|
73
81
|
|
|
74
82
|
|
|
75
83
|
@dataclass
|
|
@@ -82,7 +90,7 @@ class scale_alpha_identity(
|
|
|
82
90
|
|
|
83
91
|
_aesthetics = ["alpha"]
|
|
84
92
|
_: KW_ONLY
|
|
85
|
-
guide: Literal["legend"] | None =
|
|
93
|
+
guide: Literal["legend"] | None = None
|
|
86
94
|
|
|
87
95
|
|
|
88
96
|
@dataclass
|
|
@@ -95,7 +103,18 @@ class scale_size_identity(
|
|
|
95
103
|
|
|
96
104
|
_aesthetics = ["size"]
|
|
97
105
|
_: KW_ONLY
|
|
98
|
-
guide: Literal["legend"] | None =
|
|
106
|
+
guide: Literal["legend"] | None = None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclass
|
|
110
|
+
class scale_stroke_identity(MapTrainMixin, scale_discrete):
|
|
111
|
+
"""
|
|
112
|
+
No stroke scaling
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
_aesthetics = ["stroke"]
|
|
116
|
+
_: KW_ONLY
|
|
117
|
+
guide: Literal["legend"] | None = None
|
|
99
118
|
|
|
100
119
|
|
|
101
120
|
# American to British spelling
|
plotnine/scales/scale_manual.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from collections.abc import Mapping
|
|
3
4
|
from dataclasses import KW_ONLY, InitVar, dataclass
|
|
4
5
|
from typing import Any, Sequence
|
|
5
6
|
from warnings import warn
|
|
@@ -21,11 +22,16 @@ class _scale_manual(scale_discrete):
|
|
|
21
22
|
"""
|
|
22
23
|
|
|
23
24
|
def __post_init__(self, values):
|
|
24
|
-
from collections.abc import Sized
|
|
25
|
+
from collections.abc import Iterable, Sized
|
|
25
26
|
|
|
26
27
|
super().__post_init__()
|
|
27
28
|
|
|
28
|
-
if
|
|
29
|
+
if (
|
|
30
|
+
isinstance(self.breaks, Iterable)
|
|
31
|
+
and isinstance(self.breaks, Sized)
|
|
32
|
+
and len(self.breaks) == len(values)
|
|
33
|
+
and not isinstance(values, Mapping)
|
|
34
|
+
):
|
|
29
35
|
values = dict(zip(self.breaks, values))
|
|
30
36
|
|
|
31
37
|
def palette(n):
|
plotnine/scales/scale_xy.py
CHANGED
|
@@ -213,7 +213,7 @@ class scale_x_discrete(scale_position_discrete):
|
|
|
213
213
|
Discrete x position
|
|
214
214
|
"""
|
|
215
215
|
|
|
216
|
-
_aesthetics = ["x", "xmin", "xmax", "xend"]
|
|
216
|
+
_aesthetics = ["x", "xmin", "xmax", "xend", "xintercept"]
|
|
217
217
|
|
|
218
218
|
|
|
219
219
|
@dataclass(kw_only=True)
|
|
@@ -222,7 +222,7 @@ class scale_y_discrete(scale_position_discrete):
|
|
|
222
222
|
Discrete y position
|
|
223
223
|
"""
|
|
224
224
|
|
|
225
|
-
_aesthetics = ["y", "ymin", "ymax", "yend"]
|
|
225
|
+
_aesthetics = ["y", "ymin", "ymax", "yend", "yintercept"]
|
|
226
226
|
|
|
227
227
|
|
|
228
228
|
# Not part of the user API
|
plotnine/stats/binning.py
CHANGED
|
@@ -165,7 +165,10 @@ def assign_bins(
|
|
|
165
165
|
if weight is None:
|
|
166
166
|
weight = np.ones(len(x))
|
|
167
167
|
else:
|
|
168
|
-
weight
|
|
168
|
+
# If weight is a dtype that isn't writeable
|
|
169
|
+
# and does not own it's memory. Using a list
|
|
170
|
+
# as an intermediate easily solves this.
|
|
171
|
+
weight = np.array(list(weight))
|
|
169
172
|
weight[np.isnan(weight)] = 0
|
|
170
173
|
|
|
171
174
|
bin_idx = pd.cut(
|
plotnine/stats/smoothers.py
CHANGED
|
@@ -12,12 +12,9 @@ from ..exceptions import PlotnineError, PlotnineWarning
|
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
import statsmodels.api as sm
|
|
15
|
-
from patsy.eval import EvalEnvironment
|
|
16
15
|
|
|
17
|
-
from plotnine.mapping import Environment
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
def predictdf(data, xseq, **params) -> pd.DataFrame:
|
|
17
|
+
def predictdf(data, xseq, params) -> pd.DataFrame:
|
|
21
18
|
"""
|
|
22
19
|
Make prediction on the data
|
|
23
20
|
|
|
@@ -49,21 +46,21 @@ def predictdf(data, xseq, **params) -> pd.DataFrame:
|
|
|
49
46
|
if not callable(method):
|
|
50
47
|
msg = (
|
|
51
48
|
"'method' should either be a string or a function"
|
|
52
|
-
"with the signature `func(data, xseq,
|
|
49
|
+
"with the signature `func(data, xseq, params)`"
|
|
53
50
|
)
|
|
54
51
|
raise PlotnineError(msg)
|
|
55
52
|
|
|
56
|
-
return method(data, xseq,
|
|
53
|
+
return method(data, xseq, params)
|
|
57
54
|
|
|
58
55
|
|
|
59
|
-
def lm(data, xseq,
|
|
56
|
+
def lm(data, xseq, params) -> pd.DataFrame:
|
|
60
57
|
"""
|
|
61
58
|
Fit OLS / WLS if data has weight
|
|
62
59
|
"""
|
|
63
60
|
import statsmodels.api as sm
|
|
64
61
|
|
|
65
62
|
if params["formula"]:
|
|
66
|
-
return lm_formula(data, xseq,
|
|
63
|
+
return lm_formula(data, xseq, params)
|
|
67
64
|
|
|
68
65
|
X = sm.add_constant(data["x"])
|
|
69
66
|
Xseq = sm.add_constant(xseq)
|
|
@@ -96,14 +93,14 @@ def lm(data, xseq, **params) -> pd.DataFrame:
|
|
|
96
93
|
return data
|
|
97
94
|
|
|
98
95
|
|
|
99
|
-
def lm_formula(data, xseq,
|
|
96
|
+
def lm_formula(data, xseq, params) -> pd.DataFrame:
|
|
100
97
|
"""
|
|
101
98
|
Fit OLS / WLS using a formula
|
|
102
99
|
"""
|
|
103
100
|
import statsmodels.api as sm
|
|
104
101
|
import statsmodels.formula.api as smf
|
|
105
102
|
|
|
106
|
-
eval_env =
|
|
103
|
+
eval_env = params["environment"].to_patsy_env()
|
|
107
104
|
formula = params["formula"]
|
|
108
105
|
weights = data.get("weight", None)
|
|
109
106
|
|
|
@@ -140,14 +137,14 @@ def lm_formula(data, xseq, **params) -> pd.DataFrame:
|
|
|
140
137
|
return data
|
|
141
138
|
|
|
142
139
|
|
|
143
|
-
def rlm(data, xseq,
|
|
140
|
+
def rlm(data, xseq, params) -> pd.DataFrame:
|
|
144
141
|
"""
|
|
145
142
|
Fit RLM
|
|
146
143
|
"""
|
|
147
144
|
import statsmodels.api as sm
|
|
148
145
|
|
|
149
146
|
if params["formula"]:
|
|
150
|
-
return rlm_formula(data, xseq,
|
|
147
|
+
return rlm_formula(data, xseq, params)
|
|
151
148
|
|
|
152
149
|
X = sm.add_constant(data["x"])
|
|
153
150
|
Xseq = sm.add_constant(xseq)
|
|
@@ -170,14 +167,14 @@ def rlm(data, xseq, **params) -> pd.DataFrame:
|
|
|
170
167
|
return data
|
|
171
168
|
|
|
172
169
|
|
|
173
|
-
def rlm_formula(data, xseq,
|
|
170
|
+
def rlm_formula(data, xseq, params) -> pd.DataFrame:
|
|
174
171
|
"""
|
|
175
172
|
Fit RLM using a formula
|
|
176
173
|
"""
|
|
177
174
|
import statsmodels.api as sm
|
|
178
175
|
import statsmodels.formula.api as smf
|
|
179
176
|
|
|
180
|
-
eval_env =
|
|
177
|
+
eval_env = params["environment"].to_patsy_env()
|
|
181
178
|
formula = params["formula"]
|
|
182
179
|
init_kwargs, fit_kwargs = separate_method_kwargs(
|
|
183
180
|
params["method_args"], sm.RLM, sm.RLM.fit
|
|
@@ -196,14 +193,14 @@ def rlm_formula(data, xseq, **params) -> pd.DataFrame:
|
|
|
196
193
|
return data
|
|
197
194
|
|
|
198
195
|
|
|
199
|
-
def gls(data, xseq,
|
|
196
|
+
def gls(data, xseq, params) -> pd.DataFrame:
|
|
200
197
|
"""
|
|
201
198
|
Fit GLS
|
|
202
199
|
"""
|
|
203
200
|
import statsmodels.api as sm
|
|
204
201
|
|
|
205
202
|
if params["formula"]:
|
|
206
|
-
return gls_formula(data, xseq,
|
|
203
|
+
return gls_formula(data, xseq, params)
|
|
207
204
|
|
|
208
205
|
X = sm.add_constant(data["x"])
|
|
209
206
|
Xseq = sm.add_constant(xseq)
|
|
@@ -227,14 +224,14 @@ def gls(data, xseq, **params) -> pd.DataFrame:
|
|
|
227
224
|
return data
|
|
228
225
|
|
|
229
226
|
|
|
230
|
-
def gls_formula(data, xseq,
|
|
227
|
+
def gls_formula(data, xseq, params):
|
|
231
228
|
"""
|
|
232
229
|
Fit GLL using a formula
|
|
233
230
|
"""
|
|
234
231
|
import statsmodels.api as sm
|
|
235
232
|
import statsmodels.formula.api as smf
|
|
236
233
|
|
|
237
|
-
eval_env =
|
|
234
|
+
eval_env = params["environment"].to_patsy_env()
|
|
238
235
|
formula = params["formula"]
|
|
239
236
|
init_kwargs, fit_kwargs = separate_method_kwargs(
|
|
240
237
|
params["method_args"], sm.GLS, sm.GLS.fit
|
|
@@ -258,14 +255,14 @@ def gls_formula(data, xseq, **params):
|
|
|
258
255
|
return data
|
|
259
256
|
|
|
260
257
|
|
|
261
|
-
def glm(data, xseq,
|
|
258
|
+
def glm(data, xseq, params) -> pd.DataFrame:
|
|
262
259
|
"""
|
|
263
260
|
Fit GLM
|
|
264
261
|
"""
|
|
265
262
|
import statsmodels.api as sm
|
|
266
263
|
|
|
267
264
|
if params["formula"]:
|
|
268
|
-
return glm_formula(data, xseq,
|
|
265
|
+
return glm_formula(data, xseq, params)
|
|
269
266
|
|
|
270
267
|
X = sm.add_constant(data["x"])
|
|
271
268
|
Xseq = sm.add_constant(xseq)
|
|
@@ -292,14 +289,14 @@ def glm(data, xseq, **params) -> pd.DataFrame:
|
|
|
292
289
|
return data
|
|
293
290
|
|
|
294
291
|
|
|
295
|
-
def glm_formula(data, xseq,
|
|
292
|
+
def glm_formula(data, xseq, params):
|
|
296
293
|
"""
|
|
297
294
|
Fit with GLM formula
|
|
298
295
|
"""
|
|
299
296
|
import statsmodels.api as sm
|
|
300
297
|
import statsmodels.formula.api as smf
|
|
301
298
|
|
|
302
|
-
eval_env =
|
|
299
|
+
eval_env = params["environment"].to_patsy_env()
|
|
303
300
|
init_kwargs, fit_kwargs = separate_method_kwargs(
|
|
304
301
|
params["method_args"], sm.GLM, sm.GLM.fit
|
|
305
302
|
)
|
|
@@ -321,7 +318,7 @@ def glm_formula(data, xseq, **params):
|
|
|
321
318
|
return data
|
|
322
319
|
|
|
323
320
|
|
|
324
|
-
def lowess(data, xseq,
|
|
321
|
+
def lowess(data, xseq, params) -> pd.DataFrame:
|
|
325
322
|
"""
|
|
326
323
|
Lowess fitting
|
|
327
324
|
"""
|
|
@@ -351,7 +348,7 @@ def lowess(data, xseq, **params) -> pd.DataFrame:
|
|
|
351
348
|
return data
|
|
352
349
|
|
|
353
350
|
|
|
354
|
-
def loess(data, xseq,
|
|
351
|
+
def loess(data, xseq, params) -> pd.DataFrame:
|
|
355
352
|
"""
|
|
356
353
|
Loess smoothing
|
|
357
354
|
"""
|
|
@@ -402,7 +399,7 @@ def loess(data, xseq, **params) -> pd.DataFrame:
|
|
|
402
399
|
return data
|
|
403
400
|
|
|
404
401
|
|
|
405
|
-
def mavg(data, xseq,
|
|
402
|
+
def mavg(data, xseq, params) -> pd.DataFrame:
|
|
406
403
|
"""
|
|
407
404
|
Fit moving average
|
|
408
405
|
"""
|
|
@@ -426,7 +423,7 @@ def mavg(data, xseq, **params) -> pd.DataFrame:
|
|
|
426
423
|
return data
|
|
427
424
|
|
|
428
425
|
|
|
429
|
-
def gpr(data, xseq,
|
|
426
|
+
def gpr(data, xseq, params):
|
|
430
427
|
"""
|
|
431
428
|
Fit gaussian process
|
|
432
429
|
"""
|
|
@@ -593,16 +590,6 @@ def separate_method_kwargs(method_args, init_method, fit_method):
|
|
|
593
590
|
return init_kwargs, fit_kwargs
|
|
594
591
|
|
|
595
592
|
|
|
596
|
-
def _to_patsy_env(environment: Environment) -> EvalEnvironment:
|
|
597
|
-
"""
|
|
598
|
-
Convert a plotnine environment to a patsy environment
|
|
599
|
-
"""
|
|
600
|
-
from patsy.eval import EvalEnvironment
|
|
601
|
-
|
|
602
|
-
eval_env = EvalEnvironment(environment.namespaces)
|
|
603
|
-
return eval_env
|
|
604
|
-
|
|
605
|
-
|
|
606
593
|
def _glm_family(family: str) -> sm.families.Family:
|
|
607
594
|
"""
|
|
608
595
|
Get glm-family instance
|