plotnine 0.15.0a4__py3-none-any.whl → 0.15.0a6__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_tree.py +4 -4
- plotnine/_utils/__init__.py +13 -0
- plotnine/composition/__init__.py +11 -0
- plotnine/{plot_composition/_compose.py → composition/_arrange.py} +48 -118
- plotnine/composition/_beside.py +47 -0
- plotnine/composition/_plot_spacer.py +49 -0
- plotnine/composition/_stack.py +47 -0
- plotnine/geoms/geom.py +29 -17
- plotnine/geoms/geom_area.py +2 -2
- plotnine/geoms/geom_bin_2d.py +2 -2
- plotnine/geoms/geom_col.py +2 -2
- plotnine/geoms/geom_count.py +2 -2
- plotnine/geoms/geom_density_2d.py +2 -2
- plotnine/geoms/geom_map.py +2 -2
- plotnine/geoms/geom_sina.py +2 -2
- plotnine/ggplot.py +12 -26
- plotnine/mapping/_atomic.py +178 -0
- plotnine/scales/scale_manual.py +2 -0
- plotnine/themes/elements/element_line.py +17 -9
- plotnine/themes/themeable.py +24 -13
- {plotnine-0.15.0a4.dist-info → plotnine-0.15.0a6.dist-info}/METADATA +1 -1
- {plotnine-0.15.0a4.dist-info → plotnine-0.15.0a6.dist-info}/RECORD +27 -24
- plotnine/plot_composition/__init__.py +0 -10
- plotnine/plot_composition/_spacer.py +0 -32
- /plotnine/{plot_composition → composition}/_plotspec.py +0 -0
- {plotnine-0.15.0a4.dist-info → plotnine-0.15.0a6.dist-info}/WHEEL +0 -0
- {plotnine-0.15.0a4.dist-info → plotnine-0.15.0a6.dist-info}/licenses/LICENSE +0 -0
- {plotnine-0.15.0a4.dist-info → plotnine-0.15.0a6.dist-info}/top_level.txt +0 -0
|
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
|
|
|
13
13
|
from matplotlib.figure import Figure
|
|
14
14
|
|
|
15
15
|
from plotnine import ggplot
|
|
16
|
-
from plotnine.
|
|
16
|
+
from plotnine.composition import Arrange
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class PlotnineLayoutEngine(LayoutEngine):
|
|
@@ -54,7 +54,7 @@ class PlotnineCompositionLayoutEngine(LayoutEngine):
|
|
|
54
54
|
_adjust_compatible = True
|
|
55
55
|
_colorbar_gridspec = False
|
|
56
56
|
|
|
57
|
-
def __init__(self, composition:
|
|
57
|
+
def __init__(self, composition: Arrange):
|
|
58
58
|
self.composition = composition
|
|
59
59
|
|
|
60
60
|
def execute(self, fig: Figure):
|
|
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, cast
|
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
|
|
10
|
-
from plotnine.
|
|
10
|
+
from plotnine.composition import Beside
|
|
11
11
|
|
|
12
12
|
from ._spaces import LayoutSpaces
|
|
13
13
|
|
|
@@ -16,7 +16,7 @@ if TYPE_CHECKING:
|
|
|
16
16
|
|
|
17
17
|
from plotnine import ggplot
|
|
18
18
|
from plotnine._mpl.gridspec import p9GridSpec
|
|
19
|
-
from plotnine.
|
|
19
|
+
from plotnine.composition import Arrange
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
@dataclass
|
|
@@ -82,7 +82,7 @@ class LayoutTree:
|
|
|
82
82
|
|
|
83
83
|
@staticmethod
|
|
84
84
|
def create(
|
|
85
|
-
cmp:
|
|
85
|
+
cmp: Arrange,
|
|
86
86
|
lookup_spaces: dict[ggplot, LayoutSpaces],
|
|
87
87
|
) -> LayoutTree:
|
|
88
88
|
"""
|
|
@@ -109,7 +109,7 @@ class LayoutTree:
|
|
|
109
109
|
else:
|
|
110
110
|
nodes.append(LayoutTree.create(item, lookup_spaces))
|
|
111
111
|
|
|
112
|
-
if isinstance(cmp,
|
|
112
|
+
if isinstance(cmp, Beside):
|
|
113
113
|
return ColumnsTree(cmp.gridspec, nodes)
|
|
114
114
|
else:
|
|
115
115
|
return RowsTree(cmp.gridspec, nodes)
|
plotnine/_utils/__init__.py
CHANGED
|
@@ -1194,3 +1194,16 @@ def va_as_float(va: VerticalJustification | float) -> float:
|
|
|
1194
1194
|
"center_baseline": 0.5,
|
|
1195
1195
|
}
|
|
1196
1196
|
return lookup[va] if isinstance(va, str) else va
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
def has_alpha_channel(c: str | tuple) -> bool:
|
|
1200
|
+
"""
|
|
1201
|
+
Return True if c a color with an alpha value
|
|
1202
|
+
|
|
1203
|
+
Either a 9 character hex string e.g. #AABBCC88 or
|
|
1204
|
+
an RGBA tuple e.g. (.6, .7, .8, .5)
|
|
1205
|
+
"""
|
|
1206
|
+
if isinstance(c, str):
|
|
1207
|
+
return c.startswith("#") and len(c) == 9
|
|
1208
|
+
else:
|
|
1209
|
+
return color_utils.is_color_tuple(c) and len(c) == 4
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import abc
|
|
4
4
|
from copy import deepcopy
|
|
5
|
-
from dataclasses import dataclass
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
6
|
from io import BytesIO
|
|
7
7
|
from typing import TYPE_CHECKING
|
|
8
8
|
|
|
@@ -24,9 +24,15 @@ if TYPE_CHECKING:
|
|
|
24
24
|
from plotnine.ggplot import PlotAddable, ggplot
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
@dataclass
|
|
28
|
+
class Arrange:
|
|
28
29
|
"""
|
|
29
|
-
|
|
30
|
+
Base class for those that create plot compositions
|
|
31
|
+
|
|
32
|
+
As a user, you will never directly work with this class, except
|
|
33
|
+
the operators [`|`](`plotnine.composition.Beside`) and
|
|
34
|
+
[`/`](`plotnine.composition.Stack`) that are powered by subclasses
|
|
35
|
+
of this class.
|
|
30
36
|
|
|
31
37
|
Parameters
|
|
32
38
|
----------
|
|
@@ -34,27 +40,26 @@ class Compose:
|
|
|
34
40
|
The objects to be put together (composed).
|
|
35
41
|
"""
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
self.operands = operands
|
|
43
|
+
operands: list[ggplot | Arrange]
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
# These are created in the _create_figure method
|
|
46
|
+
figure: Figure = field(init=False, repr=False)
|
|
47
|
+
plotspecs: list[plotspec] = field(init=False, repr=False)
|
|
48
|
+
gridspec: p9GridSpec = field(init=False, repr=False)
|
|
44
49
|
|
|
45
50
|
@abc.abstractmethod
|
|
46
|
-
def __or__(self, rhs: ggplot |
|
|
51
|
+
def __or__(self, rhs: ggplot | Arrange) -> Arrange:
|
|
47
52
|
"""
|
|
48
53
|
Add rhs as a column
|
|
49
54
|
"""
|
|
50
55
|
|
|
51
56
|
@abc.abstractmethod
|
|
52
|
-
def __truediv__(self, rhs: ggplot |
|
|
57
|
+
def __truediv__(self, rhs: ggplot | Arrange) -> Arrange:
|
|
53
58
|
"""
|
|
54
59
|
Add rhs as a row
|
|
55
60
|
"""
|
|
56
61
|
|
|
57
|
-
def __add__(self, rhs: ggplot |
|
|
62
|
+
def __add__(self, rhs: ggplot | Arrange | PlotAddable) -> Arrange:
|
|
58
63
|
"""
|
|
59
64
|
Add rhs to the composition
|
|
60
65
|
|
|
@@ -65,13 +70,13 @@ class Compose:
|
|
|
65
70
|
"""
|
|
66
71
|
from plotnine import ggplot
|
|
67
72
|
|
|
68
|
-
if not isinstance(rhs, (ggplot,
|
|
73
|
+
if not isinstance(rhs, (ggplot, Arrange)):
|
|
69
74
|
cmp = deepcopy(self)
|
|
70
75
|
cmp.last_plot = cmp.last_plot + rhs
|
|
71
76
|
return cmp
|
|
72
77
|
return self.__class__([*self, rhs])
|
|
73
78
|
|
|
74
|
-
def __sub__(self, rhs: ggplot |
|
|
79
|
+
def __sub__(self, rhs: ggplot | Arrange) -> Arrange:
|
|
75
80
|
"""
|
|
76
81
|
Add the rhs besides the composition
|
|
77
82
|
|
|
@@ -82,7 +87,7 @@ class Compose:
|
|
|
82
87
|
"""
|
|
83
88
|
return self.__class__([self, rhs])
|
|
84
89
|
|
|
85
|
-
def __and__(self, rhs: PlotAddable) ->
|
|
90
|
+
def __and__(self, rhs: PlotAddable) -> Arrange:
|
|
86
91
|
"""
|
|
87
92
|
Add rhs to all plots in the composition
|
|
88
93
|
|
|
@@ -93,9 +98,9 @@ class Compose:
|
|
|
93
98
|
"""
|
|
94
99
|
self = deepcopy(self)
|
|
95
100
|
|
|
96
|
-
def add_other(op:
|
|
101
|
+
def add_other(op: Arrange):
|
|
97
102
|
for item in op:
|
|
98
|
-
if isinstance(item,
|
|
103
|
+
if isinstance(item, Arrange):
|
|
99
104
|
add_other(item)
|
|
100
105
|
else:
|
|
101
106
|
item += rhs
|
|
@@ -103,7 +108,7 @@ class Compose:
|
|
|
103
108
|
add_other(self)
|
|
104
109
|
return self
|
|
105
110
|
|
|
106
|
-
def __mul__(self, rhs: PlotAddable) ->
|
|
111
|
+
def __mul__(self, rhs: PlotAddable) -> Arrange:
|
|
107
112
|
"""
|
|
108
113
|
Add rhs to the outermost nesting level of the composition
|
|
109
114
|
|
|
@@ -127,7 +132,7 @@ class Compose:
|
|
|
127
132
|
"""
|
|
128
133
|
return len(self.operands)
|
|
129
134
|
|
|
130
|
-
def __iter__(self) -> Iterator[ggplot |
|
|
135
|
+
def __iter__(self) -> Iterator[ggplot | Arrange]:
|
|
131
136
|
"""
|
|
132
137
|
Return an iterable of all the operands
|
|
133
138
|
"""
|
|
@@ -227,7 +232,7 @@ class Compose:
|
|
|
227
232
|
from plotnine._mpl.gridspec import p9GridSpec
|
|
228
233
|
|
|
229
234
|
def _make_plotspecs(
|
|
230
|
-
cmp:
|
|
235
|
+
cmp: Arrange, parent_gridspec: p9GridSpec | None
|
|
231
236
|
) -> Generator[plotspec]:
|
|
232
237
|
"""
|
|
233
238
|
Return the plot specification for each subplot in the composition
|
|
@@ -289,7 +294,7 @@ class Compose:
|
|
|
289
294
|
|
|
290
295
|
def draw(self, *, show: bool = False) -> Figure:
|
|
291
296
|
"""
|
|
292
|
-
Render the
|
|
297
|
+
Render the arranged plots
|
|
293
298
|
|
|
294
299
|
Parameters
|
|
295
300
|
----------
|
|
@@ -315,9 +320,15 @@ class Compose:
|
|
|
315
320
|
)
|
|
316
321
|
return figure
|
|
317
322
|
|
|
318
|
-
def save(
|
|
323
|
+
def save(
|
|
324
|
+
self,
|
|
325
|
+
filename: str | Path | BytesIO,
|
|
326
|
+
format: str | None = None,
|
|
327
|
+
dpi: int | None = None,
|
|
328
|
+
**kwargs,
|
|
329
|
+
):
|
|
319
330
|
"""
|
|
320
|
-
Save a
|
|
331
|
+
Save a composition as an image file
|
|
321
332
|
|
|
322
333
|
Parameters
|
|
323
334
|
----------
|
|
@@ -326,14 +337,25 @@ class Compose:
|
|
|
326
337
|
format :
|
|
327
338
|
Image format to use, automatically extract from
|
|
328
339
|
file name extension.
|
|
329
|
-
|
|
330
|
-
|
|
340
|
+
dpi :
|
|
341
|
+
DPI to use for raster graphics. If None, defaults to using
|
|
342
|
+
the `dpi` of theme to the first plot.
|
|
343
|
+
kwargs :
|
|
344
|
+
These are ignored. Here to "softly" match the API of
|
|
345
|
+
`ggplot.save()`.
|
|
346
|
+
"""
|
|
347
|
+
from plotnine import theme
|
|
348
|
+
|
|
349
|
+
# To set the dpi, we only need to change the dpi of
|
|
350
|
+
# the last plot and theme gets added to the last plot
|
|
351
|
+
plot = (self + theme(dpi=dpi)) if dpi else self
|
|
352
|
+
figure = plot.draw()
|
|
331
353
|
figure.savefig(filename, format=format)
|
|
332
354
|
|
|
333
355
|
|
|
334
356
|
@dataclass
|
|
335
357
|
class plot_composition_context:
|
|
336
|
-
cmp:
|
|
358
|
+
cmp: Arrange
|
|
337
359
|
show: bool
|
|
338
360
|
|
|
339
361
|
def __post_init__(self):
|
|
@@ -368,95 +390,3 @@ class plot_composition_context:
|
|
|
368
390
|
plt.close(self.cmp.figure)
|
|
369
391
|
|
|
370
392
|
self._rc_context.__exit__(exc_type, exc_value, exc_traceback)
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
class OR(Compose):
|
|
374
|
-
"""
|
|
375
|
-
Compose by adding a column
|
|
376
|
-
"""
|
|
377
|
-
|
|
378
|
-
@property
|
|
379
|
-
def nrow(self) -> int:
|
|
380
|
-
return 1
|
|
381
|
-
|
|
382
|
-
@property
|
|
383
|
-
def ncol(self) -> int:
|
|
384
|
-
return len(self)
|
|
385
|
-
|
|
386
|
-
def __or__(self, rhs: ggplot | Compose) -> Compose:
|
|
387
|
-
"""
|
|
388
|
-
Add rhs as a column
|
|
389
|
-
"""
|
|
390
|
-
# This is adjacent or i.e. (OR | rhs) so we collapse the
|
|
391
|
-
# operands into a single operation
|
|
392
|
-
return OR([*self, rhs])
|
|
393
|
-
|
|
394
|
-
def __truediv__(self, rhs: ggplot | Compose) -> Compose:
|
|
395
|
-
"""
|
|
396
|
-
Add rhs as a row
|
|
397
|
-
"""
|
|
398
|
-
return DIV([self, rhs])
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
class DIV(Compose):
|
|
402
|
-
"""
|
|
403
|
-
Compose by adding a row
|
|
404
|
-
"""
|
|
405
|
-
|
|
406
|
-
@property
|
|
407
|
-
def nrow(self) -> int:
|
|
408
|
-
return len(self)
|
|
409
|
-
|
|
410
|
-
@property
|
|
411
|
-
def ncol(self) -> int:
|
|
412
|
-
return 1
|
|
413
|
-
|
|
414
|
-
def __truediv__(self, rhs: ggplot | Compose) -> Compose:
|
|
415
|
-
"""
|
|
416
|
-
Add rhs as a row
|
|
417
|
-
"""
|
|
418
|
-
# This is an adjacent div i.e. (DIV | rhs) so we collapse the
|
|
419
|
-
# operands into a single operation
|
|
420
|
-
return DIV([*self, rhs])
|
|
421
|
-
|
|
422
|
-
def __or__(self, rhs: ggplot | Compose) -> Compose:
|
|
423
|
-
"""
|
|
424
|
-
Add rhs as a column
|
|
425
|
-
"""
|
|
426
|
-
return OR([self, rhs])
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
class ADD(Compose):
|
|
430
|
-
"""
|
|
431
|
-
Compose by adding
|
|
432
|
-
"""
|
|
433
|
-
|
|
434
|
-
@property
|
|
435
|
-
def nrow(self) -> int:
|
|
436
|
-
from plotnine.facets.facet_wrap import wrap_dims
|
|
437
|
-
|
|
438
|
-
return wrap_dims(len(self))[0]
|
|
439
|
-
|
|
440
|
-
@property
|
|
441
|
-
def ncol(self) -> int:
|
|
442
|
-
from plotnine.facets.facet_wrap import wrap_dims
|
|
443
|
-
|
|
444
|
-
return wrap_dims(len(self))[1]
|
|
445
|
-
|
|
446
|
-
def __or__(self, rhs: ggplot | Compose) -> Compose:
|
|
447
|
-
"""
|
|
448
|
-
Add rhs as a column
|
|
449
|
-
"""
|
|
450
|
-
return OR([self, rhs])
|
|
451
|
-
|
|
452
|
-
def __truediv__(self, rhs: ggplot | Compose) -> Compose:
|
|
453
|
-
"""
|
|
454
|
-
Add rhs as a row
|
|
455
|
-
"""
|
|
456
|
-
return DIV([self, rhs])
|
|
457
|
-
|
|
458
|
-
def __sub__(self, rhs: ggplot | Compose) -> Compose:
|
|
459
|
-
"""
|
|
460
|
-
Add rhs as a column
|
|
461
|
-
"""
|
|
462
|
-
return OR([self, rhs])
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from ._arrange import Arrange
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from plotnine.ggplot import ggplot
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Beside(Arrange):
|
|
12
|
+
"""
|
|
13
|
+
Place plots or compositions side by side
|
|
14
|
+
|
|
15
|
+
**Usage**
|
|
16
|
+
|
|
17
|
+
plot | plot
|
|
18
|
+
plot | composition
|
|
19
|
+
composition | plot
|
|
20
|
+
composition | composition
|
|
21
|
+
|
|
22
|
+
Typically, you will use this class through the `|` operator.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def nrow(self) -> int:
|
|
27
|
+
return 1
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def ncol(self) -> int:
|
|
31
|
+
return len(self)
|
|
32
|
+
|
|
33
|
+
def __or__(self, rhs: ggplot | Arrange) -> Arrange:
|
|
34
|
+
"""
|
|
35
|
+
Add rhs as a column
|
|
36
|
+
"""
|
|
37
|
+
# This is adjacent or i.e. (OR | rhs) so we collapse the
|
|
38
|
+
# operands into a single operation
|
|
39
|
+
return Beside([*self, rhs])
|
|
40
|
+
|
|
41
|
+
def __truediv__(self, rhs: ggplot | Arrange) -> Arrange:
|
|
42
|
+
"""
|
|
43
|
+
Add rhs as a row
|
|
44
|
+
"""
|
|
45
|
+
from ._stack import Stack
|
|
46
|
+
|
|
47
|
+
return Stack([self, rhs])
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
|
|
5
|
+
from plotnine import element_rect, ggplot, theme, theme_void
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class plot_spacer(ggplot):
|
|
9
|
+
"""
|
|
10
|
+
Blank area as wide or as tall as a plot
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
fill :
|
|
15
|
+
Background color. The default is a transparent area, but it
|
|
16
|
+
can be changed through this parameter.
|
|
17
|
+
|
|
18
|
+
The color can also be modified by adding a [](`~plotnine.theme`)
|
|
19
|
+
and setting the [](`~plotnine.themes.themeable.plot_background`).
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
fill: (
|
|
25
|
+
str
|
|
26
|
+
| tuple[float, float, float]
|
|
27
|
+
| tuple[float, float, float, float]
|
|
28
|
+
| None
|
|
29
|
+
) = None,
|
|
30
|
+
):
|
|
31
|
+
super().__init__()
|
|
32
|
+
self.theme = theme_void()
|
|
33
|
+
if fill:
|
|
34
|
+
self.theme += theme(plot_background=element_rect(fill=fill))
|
|
35
|
+
|
|
36
|
+
def __add__(self, rhs) -> plot_spacer:
|
|
37
|
+
"""
|
|
38
|
+
Add to spacer
|
|
39
|
+
|
|
40
|
+
All added objects are no ops except the `plot_background` in
|
|
41
|
+
in a theme.
|
|
42
|
+
"""
|
|
43
|
+
self = deepcopy(self)
|
|
44
|
+
if isinstance(rhs, theme):
|
|
45
|
+
fill = rhs.getp(("plot_background", "facecolor"))
|
|
46
|
+
self.theme += theme(
|
|
47
|
+
plot_background=element_rect(fill=fill),
|
|
48
|
+
)
|
|
49
|
+
return self
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from ._arrange import Arrange
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from plotnine.ggplot import ggplot
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Stack(Arrange):
|
|
12
|
+
"""
|
|
13
|
+
Place plots or compositions on top of each other
|
|
14
|
+
|
|
15
|
+
**Usage**
|
|
16
|
+
|
|
17
|
+
plot / plot
|
|
18
|
+
plot / composition
|
|
19
|
+
composition / plot
|
|
20
|
+
composition / composition
|
|
21
|
+
|
|
22
|
+
Typically, you will use this class through the `/` operator.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def nrow(self) -> int:
|
|
27
|
+
return len(self)
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def ncol(self) -> int:
|
|
31
|
+
return 1
|
|
32
|
+
|
|
33
|
+
def __truediv__(self, rhs: ggplot | Arrange) -> Arrange:
|
|
34
|
+
"""
|
|
35
|
+
Add rhs as a row
|
|
36
|
+
"""
|
|
37
|
+
# This is an adjacent div i.e. (DIV | rhs) so we collapse the
|
|
38
|
+
# operands into a single operation
|
|
39
|
+
return Stack([*self, rhs])
|
|
40
|
+
|
|
41
|
+
def __or__(self, rhs: ggplot | Arrange) -> Arrange:
|
|
42
|
+
"""
|
|
43
|
+
Add rhs as a column
|
|
44
|
+
"""
|
|
45
|
+
from ._beside import Beside
|
|
46
|
+
|
|
47
|
+
return Beside([self, rhs])
|
plotnine/geoms/geom.py
CHANGED
|
@@ -2,9 +2,12 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import typing
|
|
4
4
|
from abc import ABC
|
|
5
|
+
from contextlib import suppress
|
|
5
6
|
from copy import deepcopy
|
|
6
7
|
from itertools import chain, repeat
|
|
7
8
|
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
8
11
|
from .._utils import (
|
|
9
12
|
data_mapping_as_kwargs,
|
|
10
13
|
remove_missing,
|
|
@@ -12,7 +15,7 @@ from .._utils import (
|
|
|
12
15
|
from .._utils.registry import Register, Registry
|
|
13
16
|
from ..exceptions import PlotnineError
|
|
14
17
|
from ..layer import layer
|
|
15
|
-
from ..mapping.aes import
|
|
18
|
+
from ..mapping.aes import rename_aesthetics
|
|
16
19
|
from ..mapping.evaluation import evaluate
|
|
17
20
|
from ..positions.position import position
|
|
18
21
|
from ..stats.stat import stat
|
|
@@ -243,6 +246,9 @@ class geom(ABC, metaclass=Register):
|
|
|
243
246
|
:
|
|
244
247
|
Data used for drawing the geom.
|
|
245
248
|
"""
|
|
249
|
+
from plotnine.mapping import _atomic as atomic
|
|
250
|
+
from plotnine.mapping._atomic import ae_value
|
|
251
|
+
|
|
246
252
|
missing_aes = (
|
|
247
253
|
self.DEFAULT_AES.keys()
|
|
248
254
|
- self.aes_params.keys()
|
|
@@ -261,25 +267,31 @@ class geom(ABC, metaclass=Register):
|
|
|
261
267
|
num_panels = len(data["PANEL"].unique()) if "PANEL" in data else 1
|
|
262
268
|
across_panels = num_panels > 1 and not self.params["inherit_aes"]
|
|
263
269
|
|
|
264
|
-
# Aesthetics set as parameters
|
|
270
|
+
# Aesthetics set as parameters in the geom/stat
|
|
265
271
|
for ae, value in self.aes_params.items():
|
|
266
|
-
|
|
272
|
+
if isinstance(value, (str, int, float, np.integer, np.floating)):
|
|
267
273
|
data[ae] = value
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
274
|
+
elif isinstance(value, ae_value):
|
|
275
|
+
data[ae] = value * len(data)
|
|
276
|
+
elif across_panels:
|
|
277
|
+
value = list(chain(*repeat(value, num_panels)))
|
|
278
|
+
data[ae] = value
|
|
279
|
+
else:
|
|
280
|
+
# Try to make sense of aesthetics whose values can be tuples
|
|
281
|
+
# or sequences of sorts.
|
|
282
|
+
ae_value_cls: type[ae_value] | None = getattr(atomic, ae, None)
|
|
283
|
+
if ae_value_cls:
|
|
284
|
+
with suppress(ValueError):
|
|
285
|
+
data[ae] = ae_value_cls(value) * len(data)
|
|
286
|
+
continue
|
|
287
|
+
|
|
288
|
+
# This should catch the aesthetic assignments to
|
|
289
|
+
# non-numeric or non-string values or sequence of values.
|
|
290
|
+
# e.g. x=datetime, x=Sequence[datetime],
|
|
291
|
+
# x=Sequence[float], shape=Sequence[str]
|
|
292
|
+
try:
|
|
276
293
|
data[ae] = value
|
|
277
|
-
|
|
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))
|
|
282
|
-
else:
|
|
294
|
+
except ValueError as e:
|
|
283
295
|
msg = f"'{ae}={value}' does not look like a valid value"
|
|
284
296
|
raise PlotnineError(msg) from e
|
|
285
297
|
|
plotnine/geoms/geom_area.py
CHANGED
|
@@ -14,12 +14,12 @@ class geom_area(geom_ribbon):
|
|
|
14
14
|
"""
|
|
15
15
|
Area plot
|
|
16
16
|
|
|
17
|
+
{usage}
|
|
18
|
+
|
|
17
19
|
An area plot is a special case of geom_ribbon,
|
|
18
20
|
where the minimum of the range is fixed to 0,
|
|
19
21
|
and the position adjustment defaults to 'stack'.
|
|
20
22
|
|
|
21
|
-
{usage}
|
|
22
|
-
|
|
23
23
|
Parameters
|
|
24
24
|
----------
|
|
25
25
|
{common_parameters}
|
plotnine/geoms/geom_bin_2d.py
CHANGED
|
@@ -7,13 +7,13 @@ class geom_bin_2d(geom_rect):
|
|
|
7
7
|
"""
|
|
8
8
|
Heatmap of 2d bin counts
|
|
9
9
|
|
|
10
|
+
{usage}
|
|
11
|
+
|
|
10
12
|
Divides the plane into rectangles, counts the number of
|
|
11
13
|
cases in each rectangle, and then (by default) maps the number
|
|
12
14
|
of cases to the rectangle's fill. This is a useful alternative
|
|
13
15
|
to geom_point in the presence of overplotting.
|
|
14
16
|
|
|
15
|
-
{usage}
|
|
16
|
-
|
|
17
17
|
Parameters
|
|
18
18
|
----------
|
|
19
19
|
{common_parameters}
|
plotnine/geoms/geom_col.py
CHANGED
|
@@ -7,13 +7,13 @@ class geom_col(geom_bar):
|
|
|
7
7
|
"""
|
|
8
8
|
Bar plot with base on the x-axis
|
|
9
9
|
|
|
10
|
+
{usage}
|
|
11
|
+
|
|
10
12
|
This is an alternate version of [](`~plotnine.geoms.geom_bar`) that maps
|
|
11
13
|
the height of bars to an existing variable in your data. If
|
|
12
14
|
you want the height of the bar to represent a count of cases,
|
|
13
15
|
use [](`~plotnine.geoms.geom_bar`).
|
|
14
16
|
|
|
15
|
-
{usage}
|
|
16
|
-
|
|
17
17
|
Parameters
|
|
18
18
|
----------
|
|
19
19
|
{common_parameters}
|
plotnine/geoms/geom_count.py
CHANGED
|
@@ -7,12 +7,12 @@ class geom_count(geom_point):
|
|
|
7
7
|
"""
|
|
8
8
|
Plot overlapping points
|
|
9
9
|
|
|
10
|
+
{usage}
|
|
11
|
+
|
|
10
12
|
This is a variant [](`~plotnine.geoms.geom_point`) that counts the number
|
|
11
13
|
of observations at each location, then maps the count to point
|
|
12
14
|
area. It useful when you have discrete data and overplotting.
|
|
13
15
|
|
|
14
|
-
{usage}
|
|
15
|
-
|
|
16
16
|
Parameters
|
|
17
17
|
----------
|
|
18
18
|
{common_parameters}
|
|
@@ -7,10 +7,10 @@ class geom_density_2d(geom_path):
|
|
|
7
7
|
"""
|
|
8
8
|
2D density estimate
|
|
9
9
|
|
|
10
|
-
This is a 2d version of [](`~plotnine.geoms.geom_density`).
|
|
11
|
-
|
|
12
10
|
{usage}
|
|
13
11
|
|
|
12
|
+
This is a 2d version of [](`~plotnine.geoms.geom_density`).
|
|
13
|
+
|
|
14
14
|
Parameters
|
|
15
15
|
----------
|
|
16
16
|
{common_parameters}
|
plotnine/geoms/geom_map.py
CHANGED
plotnine/geoms/geom_sina.py
CHANGED
|
@@ -7,13 +7,13 @@ class geom_sina(geom_point):
|
|
|
7
7
|
"""
|
|
8
8
|
Draw a sina plot
|
|
9
9
|
|
|
10
|
+
{usage}
|
|
11
|
+
|
|
10
12
|
A sina plot is a data visualization chart suitable for plotting
|
|
11
13
|
any single variable in a multiclass dataset. It is an enhanced
|
|
12
14
|
jitter strip chart, where the width of the jitter is controlled
|
|
13
15
|
by the density distribution of the data within each class.
|
|
14
16
|
|
|
15
|
-
{usage}
|
|
16
|
-
|
|
17
17
|
Parameters
|
|
18
18
|
----------
|
|
19
19
|
{common_parameters}
|
plotnine/ggplot.py
CHANGED
|
@@ -13,7 +13,6 @@ from typing import (
|
|
|
13
13
|
Iterable,
|
|
14
14
|
Optional,
|
|
15
15
|
cast,
|
|
16
|
-
overload,
|
|
17
16
|
)
|
|
18
17
|
from warnings import warn
|
|
19
18
|
|
|
@@ -53,10 +52,10 @@ if TYPE_CHECKING:
|
|
|
53
52
|
|
|
54
53
|
from plotnine import watermark
|
|
55
54
|
from plotnine._mpl.gridspec import p9GridSpec
|
|
55
|
+
from plotnine.composition import Arrange
|
|
56
56
|
from plotnine.coords.coord import coord
|
|
57
57
|
from plotnine.facets.facet import facet
|
|
58
58
|
from plotnine.layer import layer
|
|
59
|
-
from plotnine.plot_composition import Compose
|
|
60
59
|
from plotnine.typing import DataLike
|
|
61
60
|
|
|
62
61
|
class PlotAddable(Protocol):
|
|
@@ -230,18 +229,10 @@ class ggplot:
|
|
|
230
229
|
other.__radd__(self)
|
|
231
230
|
return self
|
|
232
231
|
|
|
233
|
-
@overload
|
|
234
|
-
def __add__(
|
|
235
|
-
self, rhs: PlotAddable | list[PlotAddable] | None
|
|
236
|
-
) -> ggplot: ...
|
|
237
|
-
|
|
238
|
-
@overload
|
|
239
|
-
def __add__(self, rhs: ggplot | Compose) -> Compose: ...
|
|
240
|
-
|
|
241
232
|
def __add__(
|
|
242
233
|
self,
|
|
243
|
-
rhs: PlotAddable | list[PlotAddable] | None
|
|
244
|
-
) -> ggplot
|
|
234
|
+
rhs: PlotAddable | list[PlotAddable] | None,
|
|
235
|
+
) -> ggplot:
|
|
245
236
|
"""
|
|
246
237
|
Add to ggplot
|
|
247
238
|
|
|
@@ -251,37 +242,32 @@ class ggplot:
|
|
|
251
242
|
Either an object that knows how to "radd"
|
|
252
243
|
itself to a ggplot, or a list of such objects.
|
|
253
244
|
"""
|
|
254
|
-
from .plot_composition import ADD, Compose
|
|
255
|
-
|
|
256
|
-
if isinstance(rhs, (ggplot, Compose)):
|
|
257
|
-
return ADD([self, rhs])
|
|
258
|
-
|
|
259
245
|
self = deepcopy(self)
|
|
260
246
|
return self.__iadd__(rhs)
|
|
261
247
|
|
|
262
|
-
def __or__(self, rhs: ggplot |
|
|
248
|
+
def __or__(self, rhs: ggplot | Arrange) -> Arrange:
|
|
263
249
|
"""
|
|
264
250
|
Compose 2 plots columnwise
|
|
265
251
|
"""
|
|
266
|
-
from .
|
|
252
|
+
from .composition import Beside
|
|
267
253
|
|
|
268
|
-
return
|
|
254
|
+
return Beside([self, rhs])
|
|
269
255
|
|
|
270
|
-
def __truediv__(self, rhs: ggplot |
|
|
256
|
+
def __truediv__(self, rhs: ggplot | Arrange) -> Arrange:
|
|
271
257
|
"""
|
|
272
258
|
Compose 2 plots rowwise
|
|
273
259
|
"""
|
|
274
|
-
from .
|
|
260
|
+
from .composition import Stack
|
|
275
261
|
|
|
276
|
-
return
|
|
262
|
+
return Stack([self, rhs])
|
|
277
263
|
|
|
278
|
-
def __sub__(self, rhs: ggplot |
|
|
264
|
+
def __sub__(self, rhs: ggplot | Arrange) -> Arrange:
|
|
279
265
|
"""
|
|
280
266
|
Compose 2 plots columnwise
|
|
281
267
|
"""
|
|
282
|
-
from .
|
|
268
|
+
from .composition import Beside
|
|
283
269
|
|
|
284
|
-
return
|
|
270
|
+
return Beside([self, rhs])
|
|
285
271
|
|
|
286
272
|
def __rrshift__(self, other: DataLike) -> ggplot:
|
|
287
273
|
"""
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextlib import suppress
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import (
|
|
6
|
+
Any,
|
|
7
|
+
Generic,
|
|
8
|
+
Literal,
|
|
9
|
+
Sequence,
|
|
10
|
+
TypeAlias,
|
|
11
|
+
TypeVar,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
from mizani._colors.utils import is_color_tuple
|
|
16
|
+
|
|
17
|
+
# NOTE:For now we shall use these class privately and not list them
|
|
18
|
+
# in documentation. We can't deal with assigning Sequence[ae_value]
|
|
19
|
+
# to an aesthetic.
|
|
20
|
+
|
|
21
|
+
__all__ = (
|
|
22
|
+
"linetype",
|
|
23
|
+
"color",
|
|
24
|
+
"colour",
|
|
25
|
+
"fill",
|
|
26
|
+
"shape",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
T = TypeVar("T")
|
|
30
|
+
|
|
31
|
+
ShapeType: TypeAlias = (
|
|
32
|
+
str | tuple[int, Literal[0, 1, 2], float] | Sequence[tuple[float, float]]
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class ae_value(Generic[T]):
|
|
38
|
+
"""
|
|
39
|
+
Atomic aesthetic value
|
|
40
|
+
|
|
41
|
+
The goal of this base class is simplify working with the more complex
|
|
42
|
+
aesthetic values. e.g. if a value is a tuple, we don't want it to be
|
|
43
|
+
seen as a sequence of values when assigning it to a dataframe column.
|
|
44
|
+
The subclasses should be able to recognise valid aesthetic values and
|
|
45
|
+
repeat (using multiplication) the value any number of times.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
value: T
|
|
49
|
+
|
|
50
|
+
def __mul__(self, n: int) -> Sequence[T]:
|
|
51
|
+
"""
|
|
52
|
+
Repeat value n times
|
|
53
|
+
"""
|
|
54
|
+
return [self.value] * n
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class linetype(ae_value[str | tuple]):
|
|
59
|
+
"""
|
|
60
|
+
A single linetype value
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __post_init__(self):
|
|
64
|
+
value = self.value
|
|
65
|
+
named = {
|
|
66
|
+
" ",
|
|
67
|
+
"",
|
|
68
|
+
"-",
|
|
69
|
+
"--",
|
|
70
|
+
"-.",
|
|
71
|
+
":",
|
|
72
|
+
"None",
|
|
73
|
+
"none",
|
|
74
|
+
"dashdot",
|
|
75
|
+
"dashed",
|
|
76
|
+
"dotted",
|
|
77
|
+
"solid",
|
|
78
|
+
}
|
|
79
|
+
if self.value in named:
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
# tuple of the form (offset, (on, off, on, off, ...))
|
|
83
|
+
# e.g (0, (1, 2))
|
|
84
|
+
if (
|
|
85
|
+
isinstance(value, tuple)
|
|
86
|
+
and isinstance(value[0], int)
|
|
87
|
+
and isinstance(value[1], tuple)
|
|
88
|
+
and len(value[1]) % 2 == 0
|
|
89
|
+
and all(isinstance(x, int) for x in value[1])
|
|
90
|
+
):
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
raise ValueError(f"{value} is not a known linetype.")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@dataclass
|
|
97
|
+
class color(ae_value[str | tuple]):
|
|
98
|
+
"""
|
|
99
|
+
A single color value
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def __post_init__(self):
|
|
103
|
+
if isinstance(self.value, str):
|
|
104
|
+
return
|
|
105
|
+
elif is_color_tuple(self.value):
|
|
106
|
+
self.value = tuple(self.value)
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
raise ValueError(f"{self.value} is not a known color.")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
colour = color
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclass
|
|
116
|
+
class fill(color):
|
|
117
|
+
"""
|
|
118
|
+
A single color value
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@dataclass
|
|
123
|
+
class shape(ae_value[ShapeType]):
|
|
124
|
+
"""
|
|
125
|
+
A single shape value
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __post_init__(self):
|
|
129
|
+
from matplotlib.path import Path
|
|
130
|
+
|
|
131
|
+
from ..scales.scale_shape import FILLED_SHAPES, UNFILLED_SHAPES
|
|
132
|
+
|
|
133
|
+
value = self.value
|
|
134
|
+
|
|
135
|
+
with suppress(TypeError):
|
|
136
|
+
if value in (FILLED_SHAPES | UNFILLED_SHAPES):
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
if isinstance(value, Path):
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
# tuple of the form (numsides, style, angle)
|
|
143
|
+
# where style is in the range [0, 3]
|
|
144
|
+
# e.g (4, 1, 45)
|
|
145
|
+
if (
|
|
146
|
+
isinstance(value, tuple)
|
|
147
|
+
and len(value) == 3
|
|
148
|
+
and isinstance(value[0], int)
|
|
149
|
+
and value[1] in (0, 1, 2)
|
|
150
|
+
and isinstance(value[2], (float, int))
|
|
151
|
+
):
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
if is_shape_points(value):
|
|
155
|
+
self.value = tuple(value) # pyright: ignore[reportAttributeAccessIssue]
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
raise ValueError(f"{value} is not a known shape.")
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def is_shape_points(obj: Any) -> bool:
|
|
162
|
+
"""
|
|
163
|
+
Return True if obj is like Sequence[tuple[float, float]]
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
def is_numeric(obj) -> bool:
|
|
167
|
+
"""
|
|
168
|
+
Return True if obj is a python or numpy float or integer
|
|
169
|
+
"""
|
|
170
|
+
return isinstance(obj, (float, int, np.floating, np.integer))
|
|
171
|
+
|
|
172
|
+
if not iter(obj):
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
return all(is_numeric(a) and is_numeric(b) for a, b in obj)
|
|
177
|
+
except (ValueError, TypeError):
|
|
178
|
+
return False
|
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
|
|
@@ -29,6 +30,7 @@ class _scale_manual(scale_discrete):
|
|
|
29
30
|
isinstance(self.breaks, Iterable)
|
|
30
31
|
and isinstance(self.breaks, Sized)
|
|
31
32
|
and len(self.breaks) == len(values)
|
|
33
|
+
and not isinstance(values, Mapping)
|
|
32
34
|
):
|
|
33
35
|
values = dict(zip(self.breaks, values))
|
|
34
36
|
|
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
|
|
|
5
5
|
from .element_base import element_base
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
|
-
from typing import Any, Literal,
|
|
8
|
+
from typing import Any, Literal, Sequence
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class element_line(element_base):
|
|
@@ -26,6 +26,8 @@ class element_line(element_base):
|
|
|
26
26
|
using tuples, see [](`~matplotlib.lines.line2D.set_linestyle`).
|
|
27
27
|
size : float
|
|
28
28
|
line thickness
|
|
29
|
+
alpha : float
|
|
30
|
+
Opacity value
|
|
29
31
|
kwargs : dict
|
|
30
32
|
Parameters recognised by [](`~matplotlib.lines.line2d`).
|
|
31
33
|
"""
|
|
@@ -33,31 +35,37 @@ class element_line(element_base):
|
|
|
33
35
|
def __init__(
|
|
34
36
|
self,
|
|
35
37
|
*,
|
|
36
|
-
color:
|
|
38
|
+
color: (
|
|
37
39
|
str
|
|
38
40
|
| tuple[float, float, float]
|
|
39
41
|
| tuple[float, float, float, float]
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
| None
|
|
43
|
+
) = None,
|
|
44
|
+
size: float | None = None,
|
|
45
|
+
linetype: str | Sequence[int] | None = None,
|
|
46
|
+
lineend: Literal["butt", "projecting", "round"] | None = None,
|
|
47
|
+
colour: (
|
|
45
48
|
str
|
|
46
49
|
| tuple[float, float, float]
|
|
47
50
|
| tuple[float, float, float, float]
|
|
48
|
-
|
|
51
|
+
| None
|
|
52
|
+
) = None,
|
|
53
|
+
alpha: float | None = None,
|
|
49
54
|
**kwargs: Any,
|
|
50
55
|
):
|
|
51
56
|
super().__init__()
|
|
52
57
|
self.properties.update(**kwargs)
|
|
53
58
|
|
|
54
|
-
color = color if color else colour
|
|
59
|
+
color = color if color is not None else colour
|
|
60
|
+
|
|
55
61
|
if color:
|
|
56
62
|
self.properties["color"] = color
|
|
57
63
|
if size:
|
|
58
64
|
self.properties["linewidth"] = size
|
|
59
65
|
if linetype:
|
|
60
66
|
self.properties["linestyle"] = linetype
|
|
67
|
+
if alpha is not None:
|
|
68
|
+
self.properties["alpha"] = alpha
|
|
61
69
|
|
|
62
70
|
if linetype in ("solid", "-") and lineend:
|
|
63
71
|
self.properties["solid_capstyle"] = lineend
|
plotnine/themes/themeable.py
CHANGED
|
@@ -17,7 +17,7 @@ from warnings import warn
|
|
|
17
17
|
|
|
18
18
|
import numpy as np
|
|
19
19
|
|
|
20
|
-
from .._utils import to_rgba
|
|
20
|
+
from .._utils import has_alpha_channel, to_rgba
|
|
21
21
|
from .._utils.registry import RegistryHierarchyMeta
|
|
22
22
|
from ..exceptions import PlotnineError, deprecated_themeable_name
|
|
23
23
|
from .elements import element_blank
|
|
@@ -504,6 +504,25 @@ class MixinSequenceOfValues(themeable):
|
|
|
504
504
|
a.set(**{name: value})
|
|
505
505
|
|
|
506
506
|
|
|
507
|
+
def blend_alpha(
|
|
508
|
+
properties: dict[str, Any], key: str = "color"
|
|
509
|
+
) -> dict[str, Any]:
|
|
510
|
+
"""
|
|
511
|
+
Blend color with alpha
|
|
512
|
+
|
|
513
|
+
When setting color property values of matplotlib objects,
|
|
514
|
+
for a color with an alpha channel, we don't want the alpha
|
|
515
|
+
property if any to have any effect on that color.
|
|
516
|
+
"""
|
|
517
|
+
if (color := properties.get(key)) is not None:
|
|
518
|
+
if "alpha" in properties:
|
|
519
|
+
properties[key] = to_rgba(color, properties["alpha"])
|
|
520
|
+
properties["alpha"] = None
|
|
521
|
+
elif has_alpha_channel(color):
|
|
522
|
+
properties["alpha"] = None
|
|
523
|
+
return properties
|
|
524
|
+
|
|
525
|
+
|
|
507
526
|
# element_text themeables
|
|
508
527
|
|
|
509
528
|
|
|
@@ -1366,7 +1385,7 @@ class panel_grid_major_x(themeable):
|
|
|
1366
1385
|
|
|
1367
1386
|
def apply_ax(self, ax: Axes):
|
|
1368
1387
|
super().apply_ax(ax)
|
|
1369
|
-
ax.xaxis.grid(which="major", **self.properties)
|
|
1388
|
+
ax.xaxis.grid(which="major", **blend_alpha(self.properties))
|
|
1370
1389
|
|
|
1371
1390
|
def blank_ax(self, ax: Axes):
|
|
1372
1391
|
super().blank_ax(ax)
|
|
@@ -1384,7 +1403,7 @@ class panel_grid_major_y(themeable):
|
|
|
1384
1403
|
|
|
1385
1404
|
def apply_ax(self, ax: Axes):
|
|
1386
1405
|
super().apply_ax(ax)
|
|
1387
|
-
ax.yaxis.grid(which="major", **self.properties)
|
|
1406
|
+
ax.yaxis.grid(which="major", **blend_alpha(self.properties))
|
|
1388
1407
|
|
|
1389
1408
|
def blank_ax(self, ax: Axes):
|
|
1390
1409
|
super().blank_ax(ax)
|
|
@@ -1612,11 +1631,7 @@ class panel_background(legend_key):
|
|
|
1612
1631
|
|
|
1613
1632
|
def apply_ax(self, ax: Axes):
|
|
1614
1633
|
super().apply_ax(ax)
|
|
1615
|
-
d = self.properties
|
|
1616
|
-
if "facecolor" in d and "alpha" in d:
|
|
1617
|
-
d["facecolor"] = to_rgba(d["facecolor"], d["alpha"])
|
|
1618
|
-
del d["alpha"]
|
|
1619
|
-
|
|
1634
|
+
d = blend_alpha(self.properties, "facecolor")
|
|
1620
1635
|
d["edgecolor"] = "none"
|
|
1621
1636
|
d["linewidth"] = 0
|
|
1622
1637
|
ax.patch.set(**d)
|
|
@@ -1642,16 +1657,12 @@ class panel_border(MixinSequenceOfValues):
|
|
|
1642
1657
|
if not (rects := targets.panel_border):
|
|
1643
1658
|
return
|
|
1644
1659
|
|
|
1645
|
-
d = self.properties
|
|
1660
|
+
d = blend_alpha(self.properties, "edgecolor")
|
|
1646
1661
|
|
|
1647
1662
|
with suppress(KeyError):
|
|
1648
1663
|
if d["edgecolor"] == "none" or d["size"] == 0:
|
|
1649
1664
|
return
|
|
1650
1665
|
|
|
1651
|
-
if "edgecolor" in d and "alpha" in d:
|
|
1652
|
-
d["edgecolor"] = to_rgba(d["edgecolor"], d["alpha"])
|
|
1653
|
-
del d["alpha"]
|
|
1654
|
-
|
|
1655
1666
|
self.set(rects, d)
|
|
1656
1667
|
|
|
1657
1668
|
def blank_figure(self, figure: Figure, targets: ThemeTargets):
|
|
@@ -2,7 +2,7 @@ plotnine/__init__.py,sha256=HrJhd65bnny1t-TawUgvApVj4p-gDZ0ftpr2NKZeW_s,10316
|
|
|
2
2
|
plotnine/animation.py,sha256=izJZ4Gy0cBHEBc8ehofsWSWOzZW8UEroy1Uvw86Igb0,7521
|
|
3
3
|
plotnine/doctools.py,sha256=JBF55q1MX2fXYQcGDpVrGPdlKf5OiQ5gyTdWhnM_IzU,14558
|
|
4
4
|
plotnine/exceptions.py,sha256=SgTxBHkV65HjGI3aFy2q1_lHP9HAdiuxVLN3U-PJWSQ,1616
|
|
5
|
-
plotnine/ggplot.py,sha256=
|
|
5
|
+
plotnine/ggplot.py,sha256=Nl41v1zRUulyIBMzrzIE4IgIrHU0bicL2YH4x01wHt8,24457
|
|
6
6
|
plotnine/helpers.py,sha256=4R3KZmtGH46-kRNSGOA0JxZaLKBo0ge8Vnx1cDQ8_gI,966
|
|
7
7
|
plotnine/iapi.py,sha256=jNLmUSoh5g9kNdhOoXSqNcqOdd2-6xdWAmst-YGU41U,9095
|
|
8
8
|
plotnine/labels.py,sha256=3pOXth2Xma_qCqB_xXAGIkPQ9gcaUaaFEAsa5if1iR0,2830
|
|
@@ -21,17 +21,23 @@ plotnine/_mpl/ticker.py,sha256=RY_7AdTggc7QBq9_t0KBJXg36oxKfB-Vtc9FzLnaGnQ,393
|
|
|
21
21
|
plotnine/_mpl/transforms.py,sha256=DNaOlNq76xlT696sN8ot1bmYyp4mmrjXQHk3kTi4HIg,76
|
|
22
22
|
plotnine/_mpl/utils.py,sha256=0GY3CWWXZiNHe333v9GAquRe5naO54JeiPt67-UvVYw,4147
|
|
23
23
|
plotnine/_mpl/layout_manager/__init__.py,sha256=IXpPF5Oycc45uFpK4MJ6kcQCe1u5VUfnHLNZGcnrJCg,157
|
|
24
|
-
plotnine/_mpl/layout_manager/_engine.py,sha256=
|
|
24
|
+
plotnine/_mpl/layout_manager/_engine.py,sha256=m6FDFc1XELfzNSIXaFiN3ruoTgub2PaV3wPQWP0IBYE,2804
|
|
25
25
|
plotnine/_mpl/layout_manager/_layout_items.py,sha256=3XRBl7xEdBKdhrexRmnVe7k919po6nkyE0q5Hx7j5cQ,29642
|
|
26
|
-
plotnine/_mpl/layout_manager/_layout_tree.py,sha256=
|
|
26
|
+
plotnine/_mpl/layout_manager/_layout_tree.py,sha256=yFNt7fsv2VhEt3261OfLP7kgPYNd_npLoNDLp_QJi3Y,26445
|
|
27
27
|
plotnine/_mpl/layout_manager/_spaces.py,sha256=ahBpKt-q1kVOTGiGliwl_DNB6pTEekAzA_7-GXwFlBk,35789
|
|
28
|
-
plotnine/_utils/__init__.py,sha256=
|
|
28
|
+
plotnine/_utils/__init__.py,sha256=3tcSTng6Mtk1o6NPEikALHKD3ZGuAYofjQJREHX84a4,30992
|
|
29
29
|
plotnine/_utils/context.py,sha256=HPQy_uyNXdS0s9URD7ZePyuc5hFU2XrRBLDTqRDLJzY,1708
|
|
30
30
|
plotnine/_utils/dev.py,sha256=0qgRbMhcd4dfuLuYxx0skocKAtfwHF02ntyILRBogbg,1629
|
|
31
31
|
plotnine/_utils/ipython.py,sha256=5Obr73xJ-4dzJEdBrFA8z9TXuxY7pIjKmzdTzWwnxNk,1884
|
|
32
32
|
plotnine/_utils/quarto.py,sha256=bwbU3ork8wuUIW5VDJ73J_DbWWzpYWpAd76cHMzCRao,890
|
|
33
33
|
plotnine/_utils/registry.py,sha256=HoiB2NnbEHufXjYnVJyrJKflk2RwKtTYk2L3n7tH4XA,3321
|
|
34
34
|
plotnine/_utils/yippie.py,sha256=DbmxvVrd34P24CCmOZrAulyGQ35rXNaSr8BuutS2Uio,2392
|
|
35
|
+
plotnine/composition/__init__.py,sha256=WXkLaqbgpOquCbq_hakUUQlcIka9fN0IMKNm-DOns6E,198
|
|
36
|
+
plotnine/composition/_arrange.py,sha256=-ZPe3JydfVAvVYiTI0W7aJaUySonpE2o76aKaxfjS84,11040
|
|
37
|
+
plotnine/composition/_beside.py,sha256=DAMZfIrOfz4mQSqro22GpooaCnm8ns6lG6vIp8SnAtg,993
|
|
38
|
+
plotnine/composition/_plot_spacer.py,sha256=gjAymmjOVZwwT0Y01MqHA3tAEDAr0mcWSX07npzo1Hw,1298
|
|
39
|
+
plotnine/composition/_plotspec.py,sha256=0F7q7PjDMDqcallpnBdX3N2iSRjdBTyjSvMFf83uvPU,1015
|
|
40
|
+
plotnine/composition/_stack.py,sha256=TXvy8FQ7CNNzntkjz4h4R_6-LxYSNFF5ri9E82dv17Q,1007
|
|
35
41
|
plotnine/coords/__init__.py,sha256=inM-9JwSPvYLlOy6opye0YV2EGWsI4708wGKdHvQvVM,301
|
|
36
42
|
plotnine/coords/coord.py,sha256=Yu2xj4BqFBWlS0XHNDa6h8Dek0_PA6Lssv3M9EneSB8,6883
|
|
37
43
|
plotnine/coords/coord_cartesian.py,sha256=vGeSFe9UyycUkbjdjoPNSQJwnnU7fEmsk0nl_oBvzxY,3038
|
|
@@ -69,18 +75,18 @@ plotnine/geoms/__init__.py,sha256=HEfhNmmNH4xm4rpXnFRXY4eLkJha3XPM72IIwVjv5Lc,26
|
|
|
69
75
|
plotnine/geoms/annotate.py,sha256=T5RxepV55HVNzPfkq43BWxduNIZPslRfPD1yx4bJtoo,4165
|
|
70
76
|
plotnine/geoms/annotation_logticks.py,sha256=6iGdo5szck0_nXdHnvMaRMZuRbH8Tg87tJ_aan_frqg,8969
|
|
71
77
|
plotnine/geoms/annotation_stripes.py,sha256=4Cw7TJ4SZChm_ioqfiiku0cPNnLruGuAP-4vyRao-9Y,6080
|
|
72
|
-
plotnine/geoms/geom.py,sha256=
|
|
78
|
+
plotnine/geoms/geom.py,sha256=ayhBEoPc-9MLpu18HkwLoby4NIKC68ED4Pq0ioa4I9c,17687
|
|
73
79
|
plotnine/geoms/geom_abline.py,sha256=6oxAJl_yFKKmf7OTHvACw6fg6kgJEN54hGKkyWOLr6o,3188
|
|
74
|
-
plotnine/geoms/geom_area.py,sha256=
|
|
80
|
+
plotnine/geoms/geom_area.py,sha256=QtS9NO17v-i5gvfakN8L96xwjt3dFuyZdYN8oXaXWOY,818
|
|
75
81
|
plotnine/geoms/geom_bar.py,sha256=SnqS4hPTfqXzdPh1U-kNuBg0LNX9_tQC9OKhIlB7cy0,1732
|
|
76
|
-
plotnine/geoms/geom_bin_2d.py,sha256=
|
|
82
|
+
plotnine/geoms/geom_bin_2d.py,sha256=IhlpR62o4Y6qe-tNUywOMjhseJNPV3c-x2QB6D11EPU,574
|
|
77
83
|
plotnine/geoms/geom_blank.py,sha256=38BpE9iSh3ktbOe9ayNer6IDD1q7E4EiKS_KnFSko0c,770
|
|
78
84
|
plotnine/geoms/geom_boxplot.py,sha256=OISXd3CCuH06sKqOB8xlfUPrwUX_FUKQauSqE7L-PhQ,9778
|
|
79
|
-
plotnine/geoms/geom_col.py,sha256=
|
|
80
|
-
plotnine/geoms/geom_count.py,sha256=
|
|
85
|
+
plotnine/geoms/geom_col.py,sha256=KCpme3vsc_tTbjGvL7ZhJZ4KFrQDQw3K8JyO6EBFn4E,1187
|
|
86
|
+
plotnine/geoms/geom_count.py,sha256=4quwvXQSrdDdlQfaSRmkBzpEP6qCRPB9SS6_5UV037M,511
|
|
81
87
|
plotnine/geoms/geom_crossbar.py,sha256=0NGUY4fhL8VxpGaBgjKQNJy85XJeLc6GiPCs5LZQuCc,6242
|
|
82
88
|
plotnine/geoms/geom_density.py,sha256=UwUkJxI79L3z19tmoSI6ZYs418XTbRznd-Abzrec3VY,526
|
|
83
|
-
plotnine/geoms/geom_density_2d.py,sha256=
|
|
89
|
+
plotnine/geoms/geom_density_2d.py,sha256=xEtQTTQnBzOs5PMDViZNQoSiXduU93SGmTN0u77RPj4,400
|
|
84
90
|
plotnine/geoms/geom_dotplot.py,sha256=ZHmRCEGS3Nv2tiNwALBC7ewBg1prcb_FOxAImTGKP3c,8680
|
|
85
91
|
plotnine/geoms/geom_errorbar.py,sha256=jpCuQ8CE2RKfRR8Aw_3s473HXrOk-WPOl46n4oC-8UI,2199
|
|
86
92
|
plotnine/geoms/geom_errorbarh.py,sha256=mgRo8KngGyKVSPPF8vUYlzdslBJEIe9QJC--DlWL5t4,2213
|
|
@@ -91,7 +97,7 @@ plotnine/geoms/geom_jitter.py,sha256=mJCLt2SoZN_tx_3XS8oF5JojtL2m5HO1bEqdN-JEPUg
|
|
|
91
97
|
plotnine/geoms/geom_label.py,sha256=lPxmDlS6VgEHqD3Ac1Q-Zxs75JcnduoXjJ_D1AzzWew,2835
|
|
92
98
|
plotnine/geoms/geom_line.py,sha256=IzKVPwF_Oe3uwRpuh5Xu9LFvz0XcKqCL4pN03iGHBdk,522
|
|
93
99
|
plotnine/geoms/geom_linerange.py,sha256=dkgQ28YZfjyMb_y8s0wfcAz5mHF7f7y_h1Den5wHyJk,1282
|
|
94
|
-
plotnine/geoms/geom_map.py,sha256=
|
|
100
|
+
plotnine/geoms/geom_map.py,sha256=tvzFVNOtSOfof1NQFduB8SV143o2irNsQrM8BgYiEKU,9602
|
|
95
101
|
plotnine/geoms/geom_path.py,sha256=nDrlmqPn869vyUF4RYbLKkbQka14SWJu9pqCN-cdKbQ,17106
|
|
96
102
|
plotnine/geoms/geom_point.py,sha256=KWM511zxIpKQzxI_NJpFqEszNWg5E7iHZJylKvvaK2c,4607
|
|
97
103
|
plotnine/geoms/geom_pointdensity.py,sha256=TKaxAUPwdrVMw0MVYe14apBsRfW_5_zXFLSlOS60Zlw,372
|
|
@@ -105,7 +111,7 @@ plotnine/geoms/geom_rect.py,sha256=dGOAkVXf5NSJgIbaVezg1ac78GqimElB9Ua0L_SH_YM,3
|
|
|
105
111
|
plotnine/geoms/geom_ribbon.py,sha256=af9uElWaJQSc4taTLZ7ZDzng2Zw4IrIfNPrus4OuTmM,5771
|
|
106
112
|
plotnine/geoms/geom_rug.py,sha256=2Sdarf5z3w4JbklVUk7JykWdbBNKaT0Wl5NMBl4LqT4,3520
|
|
107
113
|
plotnine/geoms/geom_segment.py,sha256=v63MoKlK4aNengCXYdPeQrcBIgBCis8QobpF3CJOf5U,3052
|
|
108
|
-
plotnine/geoms/geom_sina.py,sha256=
|
|
114
|
+
plotnine/geoms/geom_sina.py,sha256=JspG0o_K4px0KKZVaVeuBnk0iGjVJnB9PGjt8UAf08I,912
|
|
109
115
|
plotnine/geoms/geom_smooth.py,sha256=8jSxQFTdoo7RmeTrGl2j-iE6lfydUPitz-dKvxKk0Kw,3545
|
|
110
116
|
plotnine/geoms/geom_spoke.py,sha256=s-kug0H-YGhyjso9W43XvzJf9-g6inh8zzuSFeXzSaU,977
|
|
111
117
|
plotnine/geoms/geom_step.py,sha256=tTohADfGyC3M4zCzxLUhzkA2XsHBeH0eaWq-m2DQEQQ,2371
|
|
@@ -120,14 +126,11 @@ plotnine/guides/guide_colorbar.py,sha256=gL0218k3iJPNEpj6hWKxau3R8qRpT2bPkS1q9Pt
|
|
|
120
126
|
plotnine/guides/guide_legend.py,sha256=CrcV3iCAcEfUnSbkGtsS31c3OFQMWKiHqZZXejxOdno,14212
|
|
121
127
|
plotnine/guides/guides.py,sha256=cV7CwoYNrjkeaDHZ2AGcS2Dij5RpPovSiB-v47E7vhQ,15471
|
|
122
128
|
plotnine/mapping/__init__.py,sha256=DLu9E0kwwuHxzTUenoVjCNTTdkWMwIDtkExLleBq1MI,205
|
|
129
|
+
plotnine/mapping/_atomic.py,sha256=TbobHVJlHRoSHibi6OOWMVM2J1r_kKQJMS6G5zvEhrg,4029
|
|
123
130
|
plotnine/mapping/_env.py,sha256=ZXlTt2THRIcWb2WGk9fCpCMdVynlUX_BpG0Uwj7axi0,6072
|
|
124
131
|
plotnine/mapping/_eval_environment.py,sha256=PTrnnqrxMXqjt23t2NGRcU9i8Jie3ZaMe6W5aKtI7bI,2502
|
|
125
132
|
plotnine/mapping/aes.py,sha256=eqNTBHqFnSBPoVNdrUB7pYM-ShlUTYvmwdQRXh9beV4,16717
|
|
126
133
|
plotnine/mapping/evaluation.py,sha256=kblTxVv3M4xIGnHyReUU0RtmmIN77Or2JBcci0nGGfE,5913
|
|
127
|
-
plotnine/plot_composition/__init__.py,sha256=ZJYpfVF158cQZ1zREXy6wHNJ4FbSmqWxIkHWZwX3QT8,148
|
|
128
|
-
plotnine/plot_composition/_compose.py,sha256=6UgXs6VBH0LIXW2uQlBQy-URh_mM948F5GOwN6IV734,12167
|
|
129
|
-
plotnine/plot_composition/_plotspec.py,sha256=0F7q7PjDMDqcallpnBdX3N2iSRjdBTyjSvMFf83uvPU,1015
|
|
130
|
-
plotnine/plot_composition/_spacer.py,sha256=vaC4F5tHhvL7T7Ns9zxUbytqwB6MLNhm5jtiKG0vAiU,798
|
|
131
134
|
plotnine/positions/__init__.py,sha256=DQZE6duMUNRQifpa6SBrOKxZpGDk4NqQSGZLr9Yc9GI,595
|
|
132
135
|
plotnine/positions/position.py,sha256=BRD_dUh9dhdSP4SQfl8_u_VpaY_s3A2zuznwOn5M_80,8325
|
|
133
136
|
plotnine/positions/position_dodge.py,sha256=6PbAhWDfNK7NK6FZZOcfr4mdmVOUV9VLH8xD0dbQFEU,3941
|
|
@@ -151,7 +154,7 @@ plotnine/scales/scale_datetime.py,sha256=OM9gfHKGkQIBrgqCEE00zUV0cMXXxTIS7wc9dYQ
|
|
|
151
154
|
plotnine/scales/scale_discrete.py,sha256=UwAB0icMljH-eW6mK3g0YWAVzuE4P_91ZMYJhoMoMV4,9431
|
|
152
155
|
plotnine/scales/scale_identity.py,sha256=-PL9vJn0C_wOgrOQpqYSQbTWFSALoRPFzGXzebABTv8,2211
|
|
153
156
|
plotnine/scales/scale_linetype.py,sha256=pwLTmglN6d4bnChbuBi6HWbDP_nsE4viXD8CK6XKBPw,1373
|
|
154
|
-
plotnine/scales/scale_manual.py,sha256=
|
|
157
|
+
plotnine/scales/scale_manual.py,sha256=FmzF4gRxdiDVgryodN2Mx6Gr1FHW0grKO3WiL1ffkTc,4609
|
|
155
158
|
plotnine/scales/scale_shape.py,sha256=I0JdYDreCbznQilaTi6m27F91N5daCj9D2ZPuP2Szls,2081
|
|
156
159
|
plotnine/scales/scale_size.py,sha256=04nUQzKB_qpuQrcg8C7jYmLDYtdnlg6PkY7SivP5OlY,3273
|
|
157
160
|
plotnine/scales/scale_stroke.py,sha256=7LKAg6-Q3VTuw78N2XEjFFzBa8yY5NVnfBSoJGsBS5o,1711
|
|
@@ -203,16 +206,16 @@ plotnine/themes/theme_seaborn.py,sha256=8giJh8QvkMJ8pr_XXHPDMfkysoI2_yr0vJhz48_s
|
|
|
203
206
|
plotnine/themes/theme_tufte.py,sha256=qUOrZhQyfJgc0fmy8Es7tT7aYqUSzCjvkP7-dBilwHE,1926
|
|
204
207
|
plotnine/themes/theme_void.py,sha256=gYXr367zYNhvt_NXlDUx0rBPUYJa1F6adnANn5HEx5A,3407
|
|
205
208
|
plotnine/themes/theme_xkcd.py,sha256=5MOdj_H23Kr_jbDnepmq1DQDbn8-TfUmtzYBZTb4RB4,2207
|
|
206
|
-
plotnine/themes/themeable.py,sha256=
|
|
209
|
+
plotnine/themes/themeable.py,sha256=Px91yh5pft24mC-4uUFLYpU-45iX6cE64j3t-a20wug,71744
|
|
207
210
|
plotnine/themes/elements/__init__.py,sha256=xV1la_mTv1BQ3zQp7ZGVGPdV6KohvEVhKAi1uPTeAs0,327
|
|
208
211
|
plotnine/themes/elements/element_base.py,sha256=D7cfEglzsSuhW91KpZVAZ2MAHWZp64r9Aajoh8uMGZ4,832
|
|
209
212
|
plotnine/themes/elements/element_blank.py,sha256=4r7-6HeR1494oWNIGQh0ASrFQ4SLvYa6aQHA85eH-Ds,187
|
|
210
|
-
plotnine/themes/elements/element_line.py,sha256=
|
|
213
|
+
plotnine/themes/elements/element_line.py,sha256=yZvj9B3M2a7a8o2J0n-q0uviNv34PtJVUr_UefZ-v5I,2005
|
|
211
214
|
plotnine/themes/elements/element_rect.py,sha256=w5cLH-Sr4cTRXVdkRiu8kBqFt3TXHhIb1MUITfi89gE,1767
|
|
212
215
|
plotnine/themes/elements/element_text.py,sha256=8yhwBa9s9JKCtBcqcBNybbCGK6ieDnZv4SHiC4Sy2qc,6255
|
|
213
216
|
plotnine/themes/elements/margin.py,sha256=jMHe-UKHHer_VYwAVDC-Tz2-AP_4YDuXPTWAuacoqgU,4080
|
|
214
|
-
plotnine-0.15.
|
|
215
|
-
plotnine-0.15.
|
|
216
|
-
plotnine-0.15.
|
|
217
|
-
plotnine-0.15.
|
|
218
|
-
plotnine-0.15.
|
|
217
|
+
plotnine-0.15.0a6.dist-info/licenses/LICENSE,sha256=GY4tQiUd17Tq3wWR42Zs9MRTFOTf6ahIXhZTcwAdOeU,1082
|
|
218
|
+
plotnine-0.15.0a6.dist-info/METADATA,sha256=FDNoeUumEK4SXTfxin727GT3RL94ky46x9L5e5fNQ-g,9407
|
|
219
|
+
plotnine-0.15.0a6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
220
|
+
plotnine-0.15.0a6.dist-info/top_level.txt,sha256=t340Mbko1ZbmvYPkQ81dIiPHcaQdTUszYz-bWUpr8ys,9
|
|
221
|
+
plotnine-0.15.0a6.dist-info/RECORD,,
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from copy import deepcopy
|
|
4
|
-
|
|
5
|
-
from plotnine import element_rect, ggplot, theme, theme_void
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class spacer(ggplot):
|
|
9
|
-
"""
|
|
10
|
-
An empty plot
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
def __init__(self):
|
|
14
|
-
super().__init__()
|
|
15
|
-
self.theme = theme_void()
|
|
16
|
-
|
|
17
|
-
def __add__(self, rhs) -> spacer: # pyright: ignore[reportIncompatibleMethodOverride]
|
|
18
|
-
"""
|
|
19
|
-
Add to spacer
|
|
20
|
-
|
|
21
|
-
All added objects are no ops except the plot_background,
|
|
22
|
-
i.e.:
|
|
23
|
-
|
|
24
|
-
theme(plot_background=element_rect(fill="red"))
|
|
25
|
-
"""
|
|
26
|
-
self = deepcopy(self)
|
|
27
|
-
if isinstance(rhs, theme):
|
|
28
|
-
fill = rhs.getp(("plot_background", "facecolor"))
|
|
29
|
-
self.theme += theme(
|
|
30
|
-
plot_background=element_rect(fill=fill),
|
|
31
|
-
)
|
|
32
|
-
return self
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|