plotnine 0.15.0a2__py3-none-any.whl → 0.15.0a4__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/_layout_tree.py +16 -6
- plotnine/_utils/__init__.py +7 -103
- plotnine/doctools.py +3 -3
- plotnine/geoms/annotate.py +10 -10
- plotnine/geoms/annotation_logticks.py +5 -8
- plotnine/geoms/annotation_stripes.py +4 -6
- plotnine/geoms/geom.py +41 -20
- plotnine/geoms/geom_abline.py +3 -2
- plotnine/geoms/geom_blank.py +0 -3
- plotnine/geoms/geom_boxplot.py +4 -4
- plotnine/geoms/geom_crossbar.py +3 -3
- plotnine/geoms/geom_dotplot.py +1 -1
- plotnine/geoms/geom_errorbar.py +2 -2
- plotnine/geoms/geom_errorbarh.py +2 -2
- plotnine/geoms/geom_hline.py +3 -2
- plotnine/geoms/geom_linerange.py +2 -2
- plotnine/geoms/geom_map.py +3 -3
- plotnine/geoms/geom_path.py +10 -11
- plotnine/geoms/geom_point.py +4 -5
- plotnine/geoms/geom_pointrange.py +3 -5
- plotnine/geoms/geom_polygon.py +2 -3
- 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_smooth.py +3 -3
- plotnine/geoms/geom_step.py +2 -2
- plotnine/geoms/geom_text.py +2 -3
- plotnine/geoms/geom_violin.py +4 -5
- plotnine/geoms/geom_vline.py +3 -2
- plotnine/guides/guides.py +1 -1
- plotnine/layer.py +20 -12
- plotnine/mapping/_eval_environment.py +1 -1
- plotnine/mapping/aes.py +75 -45
- plotnine/scales/scale_color.py +46 -14
- plotnine/scales/scale_continuous.py +4 -3
- plotnine/scales/scale_datetime.py +28 -14
- plotnine/scales/scale_discrete.py +1 -1
- plotnine/scales/scale_xy.py +2 -2
- plotnine/stats/smoothers.py +19 -19
- plotnine/stats/stat.py +15 -25
- plotnine/stats/stat_bin.py +2 -5
- plotnine/stats/stat_bin_2d.py +7 -9
- plotnine/stats/stat_bindot.py +5 -8
- plotnine/stats/stat_boxplot.py +5 -5
- plotnine/stats/stat_count.py +5 -9
- plotnine/stats/stat_density.py +5 -8
- plotnine/stats/stat_density_2d.py +11 -8
- plotnine/stats/stat_ecdf.py +6 -5
- plotnine/stats/stat_ellipse.py +5 -6
- plotnine/stats/stat_function.py +6 -8
- plotnine/stats/stat_hull.py +2 -3
- plotnine/stats/stat_identity.py +1 -2
- plotnine/stats/stat_pointdensity.py +4 -7
- plotnine/stats/stat_qq.py +45 -20
- plotnine/stats/stat_qq_line.py +15 -11
- plotnine/stats/stat_quantile.py +6 -7
- plotnine/stats/stat_sina.py +12 -14
- plotnine/stats/stat_smooth.py +7 -10
- plotnine/stats/stat_sum.py +1 -2
- plotnine/stats/stat_summary.py +6 -9
- plotnine/stats/stat_summary_bin.py +10 -13
- plotnine/stats/stat_unique.py +1 -2
- plotnine/stats/stat_ydensity.py +7 -10
- {plotnine-0.15.0a2.dist-info → plotnine-0.15.0a4.dist-info}/METADATA +4 -4
- {plotnine-0.15.0a2.dist-info → plotnine-0.15.0a4.dist-info}/RECORD +70 -70
- {plotnine-0.15.0a2.dist-info → plotnine-0.15.0a4.dist-info}/WHEEL +1 -1
- {plotnine-0.15.0a2.dist-info → plotnine-0.15.0a4.dist-info}/licenses/LICENSE +0 -0
- {plotnine-0.15.0a2.dist-info → plotnine-0.15.0a4.dist-info}/top_level.txt +0 -0
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import abc
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from functools import cached_property
|
|
6
|
-
from typing import TYPE_CHECKING
|
|
6
|
+
from typing import TYPE_CHECKING, cast
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
|
|
@@ -528,8 +528,9 @@ class ColumnsTree(LayoutTree):
|
|
|
528
528
|
if self.bottom_tags_align:
|
|
529
529
|
return
|
|
530
530
|
|
|
531
|
-
values =
|
|
532
|
-
|
|
531
|
+
values = cast(
|
|
532
|
+
"Sequence[float]",
|
|
533
|
+
max(self.bottom_tag_heights) - np.array(self.bottom_tag_heights),
|
|
533
534
|
)
|
|
534
535
|
for item, value in zip(self.nodes, values):
|
|
535
536
|
if isinstance(item, LayoutSpaces):
|
|
@@ -541,7 +542,10 @@ class ColumnsTree(LayoutTree):
|
|
|
541
542
|
if self.top_tags_align:
|
|
542
543
|
return
|
|
543
544
|
|
|
544
|
-
values =
|
|
545
|
+
values = cast(
|
|
546
|
+
"Sequence[float]",
|
|
547
|
+
max(self.top_tag_heights) - np.array(self.top_tag_heights),
|
|
548
|
+
)
|
|
545
549
|
for item, value in zip(self.nodes, values):
|
|
546
550
|
if isinstance(item, LayoutSpaces):
|
|
547
551
|
item.t.tag_alignment = value
|
|
@@ -776,7 +780,10 @@ class RowsTree(LayoutTree):
|
|
|
776
780
|
if self.left_tags_align:
|
|
777
781
|
return
|
|
778
782
|
|
|
779
|
-
values =
|
|
783
|
+
values = cast(
|
|
784
|
+
"Sequence[float]",
|
|
785
|
+
max(self.left_tag_widths) - np.array(self.left_tag_widths),
|
|
786
|
+
)
|
|
780
787
|
for item, value in zip(self.nodes, values):
|
|
781
788
|
if isinstance(item, LayoutSpaces):
|
|
782
789
|
item.l.tag_alignment = value
|
|
@@ -787,7 +794,10 @@ class RowsTree(LayoutTree):
|
|
|
787
794
|
if self.right_tags_align:
|
|
788
795
|
return
|
|
789
796
|
|
|
790
|
-
values =
|
|
797
|
+
values = cast(
|
|
798
|
+
"Sequence[float]",
|
|
799
|
+
max(self.right_tag_widths) - np.array(self.right_tag_widths),
|
|
800
|
+
)
|
|
791
801
|
for item, value in zip(self.nodes, values):
|
|
792
802
|
if isinstance(item, LayoutSpaces):
|
|
793
803
|
item.r.tag_alignment = value
|
plotnine/_utils/__init__.py
CHANGED
|
@@ -12,9 +12,10 @@ from collections.abc import Iterable, Sequence
|
|
|
12
12
|
from contextlib import suppress
|
|
13
13
|
from copy import deepcopy
|
|
14
14
|
from dataclasses import field
|
|
15
|
-
from typing import TYPE_CHECKING
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
16
16
|
from warnings import warn
|
|
17
17
|
|
|
18
|
+
import mizani._colors.utils as color_utils
|
|
18
19
|
import numpy as np
|
|
19
20
|
import pandas as pd
|
|
20
21
|
from pandas.core.groupby import DataFrameGroupBy
|
|
@@ -26,12 +27,10 @@ if TYPE_CHECKING:
|
|
|
26
27
|
from typing import Any, Callable, Literal, TypeVar
|
|
27
28
|
|
|
28
29
|
import numpy.typing as npt
|
|
29
|
-
from matplotlib.typing import ColorType
|
|
30
30
|
from typing_extensions import TypeGuard
|
|
31
31
|
|
|
32
32
|
from plotnine.typing import (
|
|
33
33
|
AnyArrayLike,
|
|
34
|
-
AnySeries,
|
|
35
34
|
DataLike,
|
|
36
35
|
FloatArray,
|
|
37
36
|
FloatArrayLike,
|
|
@@ -60,6 +59,8 @@ BOX_LOCATIONS: dict[str, tuple[float, float]] = {
|
|
|
60
59
|
"centre": (0.5, 0.5),
|
|
61
60
|
}
|
|
62
61
|
|
|
62
|
+
to_rgba = color_utils.to_rgba
|
|
63
|
+
|
|
63
64
|
|
|
64
65
|
def is_scalar(val):
|
|
65
66
|
"""
|
|
@@ -346,6 +347,8 @@ def _id_var(x: AnyArrayLike, drop: bool = False) -> list[int]:
|
|
|
346
347
|
# NaNs are -1, we give them the highest code
|
|
347
348
|
nan_code = -1
|
|
348
349
|
new_nan_code = np.max(x.cat.codes) + 1
|
|
350
|
+
# TODO: We are assuming that x is of type Sequence[int|nan]
|
|
351
|
+
# is that accurate.
|
|
349
352
|
lst = [val if val != nan_code else new_nan_code for val in x]
|
|
350
353
|
else:
|
|
351
354
|
lst = list(x.cat.codes + 1)
|
|
@@ -513,7 +516,7 @@ def remove_missing(
|
|
|
513
516
|
if finite:
|
|
514
517
|
lst = [np.inf, -np.inf]
|
|
515
518
|
to_replace = {v: lst for v in vars}
|
|
516
|
-
data.replace(to_replace, np.nan, inplace=True)
|
|
519
|
+
data.replace(to_replace, np.nan, inplace=True)
|
|
517
520
|
txt = "non-finite"
|
|
518
521
|
else:
|
|
519
522
|
txt = "missing"
|
|
@@ -528,105 +531,6 @@ def remove_missing(
|
|
|
528
531
|
return data
|
|
529
532
|
|
|
530
533
|
|
|
531
|
-
@overload
|
|
532
|
-
def to_rgba(colors: ColorType, alpha: float) -> ColorType: ...
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
@overload
|
|
536
|
-
def to_rgba(
|
|
537
|
-
colors: Sequence[ColorType], alpha: float
|
|
538
|
-
) -> Sequence[ColorType] | ColorType: ...
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
@overload
|
|
542
|
-
def to_rgba(
|
|
543
|
-
colors: AnySeries, alpha: AnySeries
|
|
544
|
-
) -> Sequence[ColorType] | ColorType: ...
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
def to_rgba(
|
|
548
|
-
colors: Sequence[ColorType] | AnySeries | ColorType,
|
|
549
|
-
alpha: float | Sequence[float] | AnySeries,
|
|
550
|
-
) -> Sequence[ColorType] | ColorType:
|
|
551
|
-
"""
|
|
552
|
-
Convert hex colors to rgba values.
|
|
553
|
-
|
|
554
|
-
Parameters
|
|
555
|
-
----------
|
|
556
|
-
colors : iterable | str
|
|
557
|
-
colors to convert
|
|
558
|
-
alphas : iterable | float
|
|
559
|
-
alpha values
|
|
560
|
-
|
|
561
|
-
Returns
|
|
562
|
-
-------
|
|
563
|
-
out : ndarray | tuple
|
|
564
|
-
rgba color(s)
|
|
565
|
-
|
|
566
|
-
Notes
|
|
567
|
-
-----
|
|
568
|
-
Matplotlib plotting functions only accept scalar
|
|
569
|
-
alpha values. Hence no two objects with different
|
|
570
|
-
alpha values may be plotted in one call. This would
|
|
571
|
-
make plots with continuous alpha values innefficient.
|
|
572
|
-
However :), the colors can be rgba hex values or
|
|
573
|
-
list-likes and the alpha dimension will be respected.
|
|
574
|
-
"""
|
|
575
|
-
|
|
576
|
-
def is_iterable(var):
|
|
577
|
-
return np.iterable(var) and not isinstance(var, str)
|
|
578
|
-
|
|
579
|
-
def has_alpha(c):
|
|
580
|
-
return (isinstance(c, tuple) and len(c) == 4) or (
|
|
581
|
-
isinstance(c, str) and len(c) == 9 and c[0] == "#"
|
|
582
|
-
)
|
|
583
|
-
|
|
584
|
-
def no_color(c):
|
|
585
|
-
return c is None or c.lower() in ("none", "")
|
|
586
|
-
|
|
587
|
-
def to_rgba_hex(c: ColorType, a: float) -> str:
|
|
588
|
-
"""
|
|
589
|
-
Convert rgb color to rgba hex value
|
|
590
|
-
|
|
591
|
-
If color c has an alpha channel, then alpha value
|
|
592
|
-
a is ignored
|
|
593
|
-
"""
|
|
594
|
-
from matplotlib.colors import colorConverter, to_hex
|
|
595
|
-
|
|
596
|
-
if c in ("None", "none"):
|
|
597
|
-
return c
|
|
598
|
-
|
|
599
|
-
_has_alpha = has_alpha(c)
|
|
600
|
-
c = to_hex(c, keep_alpha=_has_alpha)
|
|
601
|
-
|
|
602
|
-
if not _has_alpha:
|
|
603
|
-
arr = colorConverter.to_rgba(c, a)
|
|
604
|
-
return to_hex(arr, keep_alpha=True)
|
|
605
|
-
|
|
606
|
-
return c
|
|
607
|
-
|
|
608
|
-
if is_iterable(colors):
|
|
609
|
-
colors = cast("Sequence[ColorType]", colors)
|
|
610
|
-
|
|
611
|
-
if all(no_color(c) for c in colors):
|
|
612
|
-
return "none"
|
|
613
|
-
|
|
614
|
-
if isinstance(alpha, (Sequence, pd.Series)):
|
|
615
|
-
return [to_rgba_hex(c, a) for c, a in zip(colors, alpha)]
|
|
616
|
-
else:
|
|
617
|
-
return [to_rgba_hex(c, alpha) for c in colors]
|
|
618
|
-
else:
|
|
619
|
-
colors = cast("ColorType", colors)
|
|
620
|
-
|
|
621
|
-
if no_color(colors):
|
|
622
|
-
return colors
|
|
623
|
-
|
|
624
|
-
if isinstance(alpha, (Sequence, pd.Series)):
|
|
625
|
-
return [to_rgba_hex(colors, a) for a in alpha]
|
|
626
|
-
else:
|
|
627
|
-
return to_rgba_hex(colors, alpha)
|
|
628
|
-
|
|
629
|
-
|
|
630
534
|
def groupby_apply(
|
|
631
535
|
df: pd.DataFrame,
|
|
632
536
|
cols: str | list[str],
|
plotnine/doctools.py
CHANGED
|
@@ -71,7 +71,7 @@ STAT_SIGNATURE_TPL = """
|
|
|
71
71
|
|
|
72
72
|
common_params_doc = {
|
|
73
73
|
"mapping": """\
|
|
74
|
-
Aesthetic mappings created with [aes](:class:`plotnine.mapping.aes`). If \
|
|
74
|
+
Aesthetic mappings created with [aes](:class:`plotnine.mapping.aes.aes`). If \
|
|
75
75
|
specified and `inherit_aes=True`{.py}, it is combined with the default \
|
|
76
76
|
mapping for the plot. You must supply mapping if there is no plot mapping.""",
|
|
77
77
|
"data": """\
|
|
@@ -103,7 +103,7 @@ the final image is in vector format.""",
|
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
GEOM_PARAMS_TPL = """
|
|
106
|
-
mapping : ~plotnine.mapping.aes, default=None
|
|
106
|
+
mapping : ~plotnine.mapping.aes.aes, default=None
|
|
107
107
|
{mapping}
|
|
108
108
|
{_aesthetics_doc}
|
|
109
109
|
data : ~pandas.DataFrame, default=None
|
|
@@ -124,7 +124,7 @@ raster : bool, default={default_raster}
|
|
|
124
124
|
"""
|
|
125
125
|
|
|
126
126
|
STAT_PARAMS_TPL = """
|
|
127
|
-
mapping : ~plotnine.mapping.aes, default=None
|
|
127
|
+
mapping : ~plotnine.mapping.aes.aes, default=None
|
|
128
128
|
{mapping}
|
|
129
129
|
{_aesthetics_doc}
|
|
130
130
|
data : ~pandas.DataFrame, default=None
|
plotnine/geoms/annotate.py
CHANGED
|
@@ -64,16 +64,16 @@ class annotate:
|
|
|
64
64
|
def __init__(
|
|
65
65
|
self,
|
|
66
66
|
geom: str | type[geom_base_class],
|
|
67
|
-
x: float | None = None,
|
|
68
|
-
y: float | None = None,
|
|
69
|
-
xmin: float | None = None,
|
|
70
|
-
xmax: float | None = None,
|
|
71
|
-
xend: float | None = None,
|
|
72
|
-
xintercept: float | None = None,
|
|
73
|
-
ymin: float | None = None,
|
|
74
|
-
ymax: float | None = None,
|
|
75
|
-
yend: float | None = None,
|
|
76
|
-
yintercept: float | None = None,
|
|
67
|
+
x: float | list[float] | None = None,
|
|
68
|
+
y: float | list[float] | None = None,
|
|
69
|
+
xmin: float | list[float] | None = None,
|
|
70
|
+
xmax: float | list[float] | None = None,
|
|
71
|
+
xend: float | list[float] | None = None,
|
|
72
|
+
xintercept: float | list[float] | None = None,
|
|
73
|
+
ymin: float | list[float] | None = None,
|
|
74
|
+
ymax: float | list[float] | None = None,
|
|
75
|
+
yend: float | list[float] | None = None,
|
|
76
|
+
yintercept: float | list[float] | None = None,
|
|
77
77
|
**kwargs: Any,
|
|
78
78
|
):
|
|
79
79
|
variables = locals()
|
|
@@ -46,9 +46,7 @@ class _geom_logticks(geom_rug):
|
|
|
46
46
|
}
|
|
47
47
|
draw_legend = staticmethod(geom_path.draw_legend)
|
|
48
48
|
|
|
49
|
-
def draw_layer(
|
|
50
|
-
self, data: pd.DataFrame, layout: Layout, coord: coord, **params: Any
|
|
51
|
-
):
|
|
49
|
+
def draw_layer(self, data: pd.DataFrame, layout: Layout, coord: coord):
|
|
52
50
|
"""
|
|
53
51
|
Draw ticks on every panel
|
|
54
52
|
"""
|
|
@@ -56,7 +54,7 @@ class _geom_logticks(geom_rug):
|
|
|
56
54
|
ploc = pid - 1
|
|
57
55
|
panel_params = layout.panel_params[ploc]
|
|
58
56
|
ax = layout.axs[ploc]
|
|
59
|
-
self.draw_panel(data, panel_params, coord, ax
|
|
57
|
+
self.draw_panel(data, panel_params, coord, ax)
|
|
60
58
|
|
|
61
59
|
@staticmethod
|
|
62
60
|
def _check_log_scale(
|
|
@@ -184,8 +182,8 @@ class _geom_logticks(geom_rug):
|
|
|
184
182
|
panel_params: panel_view,
|
|
185
183
|
coord: coord,
|
|
186
184
|
ax: Axes,
|
|
187
|
-
**params: Any,
|
|
188
185
|
):
|
|
186
|
+
params = self.params
|
|
189
187
|
# Any passed data is ignored, the relevant data is created
|
|
190
188
|
sides = params["sides"]
|
|
191
189
|
lengths = params["lengths"]
|
|
@@ -203,9 +201,8 @@ class _geom_logticks(geom_rug):
|
|
|
203
201
|
):
|
|
204
202
|
for position, length in zip(tick_positions, lengths):
|
|
205
203
|
data = pd.DataFrame({axis: position, **_aesthetics})
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
)
|
|
204
|
+
params["length"] = length
|
|
205
|
+
geom.draw_group(data, panel_params, coord, ax, params)
|
|
209
206
|
|
|
210
207
|
if isinstance(coord, coord_flip):
|
|
211
208
|
tick_range_x = panel_params.y.range
|
|
@@ -96,9 +96,7 @@ class _geom_stripes(geom):
|
|
|
96
96
|
}
|
|
97
97
|
draw_legend = staticmethod(geom_polygon.draw_legend)
|
|
98
98
|
|
|
99
|
-
def draw_layer(
|
|
100
|
-
self, data: pd.DataFrame, layout: Layout, coord: coord, **params: Any
|
|
101
|
-
):
|
|
99
|
+
def draw_layer(self, data: pd.DataFrame, layout: Layout, coord: coord):
|
|
102
100
|
"""
|
|
103
101
|
Draw stripes on every panel
|
|
104
102
|
"""
|
|
@@ -106,7 +104,7 @@ class _geom_stripes(geom):
|
|
|
106
104
|
ploc = pid - 1
|
|
107
105
|
panel_params = layout.panel_params[ploc]
|
|
108
106
|
ax = layout.axs[ploc]
|
|
109
|
-
self.draw_group(data, panel_params, coord, ax,
|
|
107
|
+
self.draw_group(data, panel_params, coord, ax, self.params)
|
|
110
108
|
|
|
111
109
|
@staticmethod
|
|
112
110
|
def draw_group(
|
|
@@ -114,7 +112,7 @@ class _geom_stripes(geom):
|
|
|
114
112
|
panel_params: panel_view,
|
|
115
113
|
coord: coord,
|
|
116
114
|
ax: Axes,
|
|
117
|
-
|
|
115
|
+
params: dict[str, Any],
|
|
118
116
|
):
|
|
119
117
|
extend = params["extend"]
|
|
120
118
|
fill_range = params["fill_range"]
|
|
@@ -195,4 +193,4 @@ class _geom_stripes(geom):
|
|
|
195
193
|
}
|
|
196
194
|
)
|
|
197
195
|
|
|
198
|
-
return geom_rect.draw_group(data, panel_params, coord, ax,
|
|
196
|
+
return geom_rect.draw_group(data, panel_params, coord, ax, params)
|
plotnine/geoms/geom.py
CHANGED
|
@@ -7,13 +7,12 @@ from itertools import chain, repeat
|
|
|
7
7
|
|
|
8
8
|
from .._utils import (
|
|
9
9
|
data_mapping_as_kwargs,
|
|
10
|
-
is_list_like,
|
|
11
10
|
remove_missing,
|
|
12
11
|
)
|
|
13
12
|
from .._utils.registry import Register, Registry
|
|
14
13
|
from ..exceptions import PlotnineError
|
|
15
14
|
from ..layer import layer
|
|
16
|
-
from ..mapping.aes import
|
|
15
|
+
from ..mapping.aes import RepeatAesthetic, rename_aesthetics
|
|
17
16
|
from ..mapping.evaluation import evaluate
|
|
18
17
|
from ..positions.position import position
|
|
19
18
|
from ..stats.stat import stat
|
|
@@ -171,6 +170,26 @@ class geom(ABC, metaclass=Register):
|
|
|
171
170
|
|
|
172
171
|
return result
|
|
173
172
|
|
|
173
|
+
def setup_params(self, data: pd.DataFrame):
|
|
174
|
+
"""
|
|
175
|
+
Override this method to verify and/or adjust parameters
|
|
176
|
+
|
|
177
|
+
Parameters
|
|
178
|
+
----------
|
|
179
|
+
data :
|
|
180
|
+
Data
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
def setup_aes_params(self, data: pd.DataFrame):
|
|
184
|
+
"""
|
|
185
|
+
Override this method to verify and/or adjust aesthetic parameters
|
|
186
|
+
|
|
187
|
+
Parameters
|
|
188
|
+
----------
|
|
189
|
+
data :
|
|
190
|
+
Data
|
|
191
|
+
"""
|
|
192
|
+
|
|
174
193
|
def setup_data(self, data: pd.DataFrame) -> pd.DataFrame:
|
|
175
194
|
"""
|
|
176
195
|
Modify the data before drawing takes place
|
|
@@ -240,30 +259,33 @@ class geom(ABC, metaclass=Register):
|
|
|
240
259
|
data[ae] = evaled[ae]
|
|
241
260
|
|
|
242
261
|
num_panels = len(data["PANEL"].unique()) if "PANEL" in data else 1
|
|
262
|
+
across_panels = num_panels > 1 and not self.params["inherit_aes"]
|
|
243
263
|
|
|
244
264
|
# Aesthetics set as parameters to the geom/stat
|
|
245
265
|
for ae, value in self.aes_params.items():
|
|
246
266
|
try:
|
|
247
267
|
data[ae] = value
|
|
248
268
|
except ValueError as e:
|
|
249
|
-
#
|
|
250
|
-
#
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
269
|
+
# NOTE: Handling of the edgecases in this exception is not
|
|
270
|
+
# foolproof.
|
|
271
|
+
repeat_ae = getattr(RepeatAesthetic, ae, None)
|
|
272
|
+
if across_panels:
|
|
273
|
+
# Adding an annotation/abline/hline/vhline with multiple
|
|
274
|
+
# items across to more than 1 panel
|
|
275
|
+
value = list(chain(*repeat(value, num_panels)))
|
|
276
|
+
data[ae] = value
|
|
277
|
+
elif repeat_ae:
|
|
278
|
+
# Some aesthetics may have valid values that are not
|
|
279
|
+
# scalar. e.g. sequences. For such case, we need to
|
|
280
|
+
# insert a sequence of the same value.
|
|
281
|
+
data[ae] = repeat_ae(value, len(data))
|
|
255
282
|
else:
|
|
256
|
-
msg =
|
|
257
|
-
f"'{value}' does not look like a "
|
|
258
|
-
f"valid value for `{ae}`"
|
|
259
|
-
)
|
|
283
|
+
msg = f"'{ae}={value}' does not look like a valid value"
|
|
260
284
|
raise PlotnineError(msg) from e
|
|
261
285
|
|
|
262
286
|
return data
|
|
263
287
|
|
|
264
|
-
def draw_layer(
|
|
265
|
-
self, data: pd.DataFrame, layout: Layout, coord: coord, **params: Any
|
|
266
|
-
):
|
|
288
|
+
def draw_layer(self, data: pd.DataFrame, layout: Layout, coord: coord):
|
|
267
289
|
"""
|
|
268
290
|
Draw layer across all panels
|
|
269
291
|
|
|
@@ -289,7 +311,7 @@ class geom(ABC, metaclass=Register):
|
|
|
289
311
|
ploc = pdata["PANEL"].iloc[0] - 1
|
|
290
312
|
panel_params = layout.panel_params[ploc]
|
|
291
313
|
ax = layout.axs[ploc]
|
|
292
|
-
self.draw_panel(pdata, panel_params, coord, ax
|
|
314
|
+
self.draw_panel(pdata, panel_params, coord, ax)
|
|
293
315
|
|
|
294
316
|
def draw_panel(
|
|
295
317
|
self,
|
|
@@ -297,7 +319,6 @@ class geom(ABC, metaclass=Register):
|
|
|
297
319
|
panel_params: panel_view,
|
|
298
320
|
coord: coord,
|
|
299
321
|
ax: Axes,
|
|
300
|
-
**params: Any,
|
|
301
322
|
):
|
|
302
323
|
"""
|
|
303
324
|
Plot all groups
|
|
@@ -331,7 +352,7 @@ class geom(ABC, metaclass=Register):
|
|
|
331
352
|
"""
|
|
332
353
|
for _, gdata in data.groupby("group"):
|
|
333
354
|
gdata.reset_index(inplace=True, drop=True)
|
|
334
|
-
self.draw_group(gdata, panel_params, coord, ax,
|
|
355
|
+
self.draw_group(gdata, panel_params, coord, ax, self.params)
|
|
335
356
|
|
|
336
357
|
@staticmethod
|
|
337
358
|
def draw_group(
|
|
@@ -339,7 +360,7 @@ class geom(ABC, metaclass=Register):
|
|
|
339
360
|
panel_params: panel_view,
|
|
340
361
|
coord: coord,
|
|
341
362
|
ax: Axes,
|
|
342
|
-
|
|
363
|
+
params: dict[str, Any],
|
|
343
364
|
):
|
|
344
365
|
"""
|
|
345
366
|
Plot data belonging to a group.
|
|
@@ -376,7 +397,7 @@ class geom(ABC, metaclass=Register):
|
|
|
376
397
|
panel_params: panel_view,
|
|
377
398
|
coord: coord,
|
|
378
399
|
ax: Axes,
|
|
379
|
-
|
|
400
|
+
params: dict[str, Any],
|
|
380
401
|
):
|
|
381
402
|
"""
|
|
382
403
|
Plot data belonging to a unit.
|
plotnine/geoms/geom_abline.py
CHANGED
|
@@ -102,7 +102,6 @@ class geom_abline(geom):
|
|
|
102
102
|
panel_params: panel_view,
|
|
103
103
|
coord: coord,
|
|
104
104
|
ax: Axes,
|
|
105
|
-
**params: Any,
|
|
106
105
|
):
|
|
107
106
|
"""
|
|
108
107
|
Plot all groups
|
|
@@ -116,4 +115,6 @@ class geom_abline(geom):
|
|
|
116
115
|
|
|
117
116
|
for _, gdata in data.groupby("group"):
|
|
118
117
|
gdata.reset_index(inplace=True)
|
|
119
|
-
geom_segment.draw_group(
|
|
118
|
+
geom_segment.draw_group(
|
|
119
|
+
gdata, panel_params, coord, ax, self.params
|
|
120
|
+
)
|
plotnine/geoms/geom_blank.py
CHANGED
|
@@ -6,8 +6,6 @@ from ..doctools import document
|
|
|
6
6
|
from .geom import geom
|
|
7
7
|
|
|
8
8
|
if typing.TYPE_CHECKING:
|
|
9
|
-
from typing import Any
|
|
10
|
-
|
|
11
9
|
import pandas as pd
|
|
12
10
|
from matplotlib.axes import Axes
|
|
13
11
|
|
|
@@ -39,7 +37,6 @@ class geom_blank(geom):
|
|
|
39
37
|
panel_params: panel_view,
|
|
40
38
|
coord: coord,
|
|
41
39
|
ax: Axes,
|
|
42
|
-
**params: Any,
|
|
43
40
|
):
|
|
44
41
|
pass
|
|
45
42
|
|
plotnine/geoms/geom_boxplot.py
CHANGED
|
@@ -183,7 +183,7 @@ class geom_boxplot(geom):
|
|
|
183
183
|
panel_params: panel_view,
|
|
184
184
|
coord: coord,
|
|
185
185
|
ax: Axes,
|
|
186
|
-
|
|
186
|
+
params: dict[str, Any],
|
|
187
187
|
):
|
|
188
188
|
def flat(*args: pd.Series[Any]) -> npt.NDArray[Any]:
|
|
189
189
|
"""Flatten list-likes"""
|
|
@@ -245,11 +245,11 @@ class geom_boxplot(geom):
|
|
|
245
245
|
outliers["shape"] = outlier_value("shape")
|
|
246
246
|
outliers["size"] = outlier_value("size")
|
|
247
247
|
outliers["stroke"] = outlier_value("stroke")
|
|
248
|
-
geom_point.draw_group(outliers, panel_params, coord, ax,
|
|
248
|
+
geom_point.draw_group(outliers, panel_params, coord, ax, params)
|
|
249
249
|
|
|
250
250
|
# plot
|
|
251
|
-
geom_segment.draw_group(whiskers, panel_params, coord, ax,
|
|
252
|
-
geom_crossbar.draw_group(box, panel_params, coord, ax,
|
|
251
|
+
geom_segment.draw_group(whiskers, panel_params, coord, ax, params)
|
|
252
|
+
geom_crossbar.draw_group(box, panel_params, coord, ax, params)
|
|
253
253
|
|
|
254
254
|
@staticmethod
|
|
255
255
|
def draw_legend(
|
plotnine/geoms/geom_crossbar.py
CHANGED
|
@@ -78,7 +78,7 @@ class geom_crossbar(geom):
|
|
|
78
78
|
panel_params: panel_view,
|
|
79
79
|
coord: coord,
|
|
80
80
|
ax: Axes,
|
|
81
|
-
|
|
81
|
+
params: dict[str, Any],
|
|
82
82
|
):
|
|
83
83
|
y = data["y"]
|
|
84
84
|
xmin = data["xmin"]
|
|
@@ -160,8 +160,8 @@ class geom_crossbar(geom):
|
|
|
160
160
|
)
|
|
161
161
|
|
|
162
162
|
copy_missing_columns(box, data)
|
|
163
|
-
geom_polygon.draw_group(box, panel_params, coord, ax,
|
|
164
|
-
geom_segment.draw_group(middle, panel_params, coord, ax,
|
|
163
|
+
geom_polygon.draw_group(box, panel_params, coord, ax, params)
|
|
164
|
+
geom_segment.draw_group(middle, panel_params, coord, ax, params)
|
|
165
165
|
|
|
166
166
|
@staticmethod
|
|
167
167
|
def draw_legend(
|
plotnine/geoms/geom_dotplot.py
CHANGED
plotnine/geoms/geom_errorbar.py
CHANGED
|
@@ -67,7 +67,7 @@ class geom_errorbar(geom):
|
|
|
67
67
|
panel_params: panel_view,
|
|
68
68
|
coord: coord,
|
|
69
69
|
ax: Axes,
|
|
70
|
-
|
|
70
|
+
params: dict[str, Any],
|
|
71
71
|
):
|
|
72
72
|
f = np.hstack
|
|
73
73
|
# create (two horizontal bars) + vertical bar
|
|
@@ -81,4 +81,4 @@ class geom_errorbar(geom):
|
|
|
81
81
|
)
|
|
82
82
|
|
|
83
83
|
copy_missing_columns(bars, data)
|
|
84
|
-
geom_segment.draw_group(bars, panel_params, coord, ax,
|
|
84
|
+
geom_segment.draw_group(bars, panel_params, coord, ax, params)
|
plotnine/geoms/geom_errorbarh.py
CHANGED
|
@@ -67,7 +67,7 @@ class geom_errorbarh(geom):
|
|
|
67
67
|
panel_params: panel_view,
|
|
68
68
|
coord: coord,
|
|
69
69
|
ax: Axes,
|
|
70
|
-
|
|
70
|
+
params: dict[str, Any],
|
|
71
71
|
):
|
|
72
72
|
f = np.hstack
|
|
73
73
|
# create (two vertical bars) + horizontal bar
|
|
@@ -81,4 +81,4 @@ class geom_errorbarh(geom):
|
|
|
81
81
|
)
|
|
82
82
|
|
|
83
83
|
copy_missing_columns(bars, data)
|
|
84
|
-
geom_segment.draw_group(bars, panel_params, coord, ax,
|
|
84
|
+
geom_segment.draw_group(bars, panel_params, coord, ax, params)
|
plotnine/geoms/geom_hline.py
CHANGED
|
@@ -80,7 +80,6 @@ class geom_hline(geom):
|
|
|
80
80
|
panel_params: panel_view,
|
|
81
81
|
coord: coord,
|
|
82
82
|
ax: Axes,
|
|
83
|
-
**params: Any,
|
|
84
83
|
):
|
|
85
84
|
"""
|
|
86
85
|
Plot all groups
|
|
@@ -94,4 +93,6 @@ class geom_hline(geom):
|
|
|
94
93
|
|
|
95
94
|
for _, gdata in data.groupby("group"):
|
|
96
95
|
gdata.reset_index(inplace=True)
|
|
97
|
-
geom_segment.draw_group(
|
|
96
|
+
geom_segment.draw_group(
|
|
97
|
+
gdata, panel_params, coord, ax, self.params
|
|
98
|
+
)
|
plotnine/geoms/geom_linerange.py
CHANGED
|
@@ -49,7 +49,7 @@ class geom_linerange(geom):
|
|
|
49
49
|
panel_params: panel_view,
|
|
50
50
|
coord: coord,
|
|
51
51
|
ax: Axes,
|
|
52
|
-
|
|
52
|
+
params: dict[str, Any],
|
|
53
53
|
):
|
|
54
54
|
data.eval(
|
|
55
55
|
"""
|
|
@@ -59,4 +59,4 @@ class geom_linerange(geom):
|
|
|
59
59
|
""",
|
|
60
60
|
inplace=True,
|
|
61
61
|
)
|
|
62
|
-
geom_segment.draw_group(data, panel_params, coord, ax,
|
|
62
|
+
geom_segment.draw_group(data, panel_params, coord, ax, params)
|
plotnine/geoms/geom_map.py
CHANGED
|
@@ -119,11 +119,11 @@ class geom_map(geom):
|
|
|
119
119
|
panel_params: panel_view,
|
|
120
120
|
coord: coord,
|
|
121
121
|
ax: Axes,
|
|
122
|
-
**params: Any,
|
|
123
122
|
):
|
|
124
123
|
if not len(data):
|
|
125
124
|
return
|
|
126
125
|
|
|
126
|
+
params = self.params
|
|
127
127
|
data.loc[data["color"].isna(), "color"] = "none"
|
|
128
128
|
data.loc[data["fill"].isna(), "fill"] = "none"
|
|
129
129
|
data["fill"] = to_rgba(data["fill"], data["alpha"])
|
|
@@ -153,7 +153,7 @@ class geom_map(geom):
|
|
|
153
153
|
for _, gdata in data.groupby("group"):
|
|
154
154
|
gdata.reset_index(inplace=True, drop=True)
|
|
155
155
|
gdata.is_copy = None
|
|
156
|
-
geom_point.draw_group(gdata, panel_params, coord, ax,
|
|
156
|
+
geom_point.draw_group(gdata, panel_params, coord, ax, params)
|
|
157
157
|
elif geom_type == "MultiPoint":
|
|
158
158
|
# Where n is the length of the dataframe (no. of multipoints),
|
|
159
159
|
# m is the number of all points in all multipoints
|
|
@@ -168,7 +168,7 @@ class geom_map(geom):
|
|
|
168
168
|
data = data.explode("points", ignore_index=True)
|
|
169
169
|
data["x"] = [p[0] for p in data["points"]]
|
|
170
170
|
data["y"] = [p[1] for p in data["points"]]
|
|
171
|
-
geom_point.draw_group(data, panel_params, coord, ax,
|
|
171
|
+
geom_point.draw_group(data, panel_params, coord, ax, params)
|
|
172
172
|
elif geom_type in ("LineString", "MultiLineString"):
|
|
173
173
|
from matplotlib.collections import LineCollection
|
|
174
174
|
|