plot-io 0.1.0__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.
- plot_io/__init__.py +66 -0
- plot_io/_backend.py +114 -0
- plot_io/axis.py +574 -0
- plot_io/colorbar.py +342 -0
- plot_io/decorations.py +496 -0
- plot_io/defaults.py +766 -0
- plot_io/density.py +97 -0
- plot_io/enums.py +63 -0
- plot_io/facets.py +184 -0
- plot_io/figure.py +331 -0
- plot_io/labels.py +63 -0
- plot_io/legend.py +498 -0
- plot_io/limits.py +92 -0
- plot_io/maps/__init__.py +365 -0
- plot_io/maps/data.py +333 -0
- plot_io/markers.py +117 -0
- plot_io/numbers.py +289 -0
- plot_io/occupancy.py +219 -0
- plot_io/palettes.py +142 -0
- plot_io/plot.py +435 -0
- plot_io/render.py +1122 -0
- plot_io/scales.py +93 -0
- plot_io/series/__init__.py +498 -0
- plot_io/series/area.py +181 -0
- plot_io/series/bar.py +294 -0
- plot_io/series/box.py +401 -0
- plot_io/series/catdata.py +23 -0
- plot_io/series/catpoints.py +182 -0
- plot_io/series/contour.py +294 -0
- plot_io/series/curves.py +128 -0
- plot_io/series/ecdf.py +143 -0
- plot_io/series/errorbar.py +111 -0
- plot_io/series/errors.py +100 -0
- plot_io/series/fill.py +168 -0
- plot_io/series/heatmap.py +304 -0
- plot_io/series/hexbin.py +216 -0
- plot_io/series/hist.py +379 -0
- plot_io/series/hist2d.py +163 -0
- plot_io/series/kde.py +228 -0
- plot_io/series/kde2d.py +153 -0
- plot_io/series/line.py +173 -0
- plot_io/series/pie.py +304 -0
- plot_io/series/points.py +341 -0
- plot_io/series/regression.py +250 -0
- plot_io/series/scatter.py +103 -0
- plot_io/series/stem.py +112 -0
- plot_io/series/violin.py +321 -0
- plot_io/settings.py +281 -0
- plot_io/shapes.py +57 -0
- plot_io/sharing.py +160 -0
- plot_io/slots.py +52 -0
- plot_io/stacking.py +63 -0
- plot_io/stats.py +140 -0
- plot_io/style.py +71 -0
- plot_io/styles/__init__.py +234 -0
- plot_io/styles/library.py +732 -0
- plot_io/tables.py +141 -0
- plot_io/text.py +109 -0
- plot_io/ticks.py +284 -0
- plot_io/times.py +199 -0
- plot_io/values.py +161 -0
- plot_io-0.1.0.dist-info/METADATA +102 -0
- plot_io-0.1.0.dist-info/RECORD +65 -0
- plot_io-0.1.0.dist-info/WHEEL +4 -0
- plot_io-0.1.0.dist-info/licenses/LICENSE +28 -0
plot_io/plot.py
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
"""Plots: one area of a figure, with axes, series, title, legend and colorbars."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .axis import Axis
|
|
6
|
+
from .settings import (Setting, Settings, boolean, canonical_kwargs, color, either, literal,
|
|
7
|
+
number, one_of, route, table, text)
|
|
8
|
+
from .numbers import number_settings
|
|
9
|
+
from .maps import check_cmap
|
|
10
|
+
from .style import check_palette
|
|
11
|
+
|
|
12
|
+
__all__ = ["Plot", "Title", "Legend"]
|
|
13
|
+
|
|
14
|
+
_SPINES = ("left", "bottom", "right", "top")
|
|
15
|
+
_POSITIONS = ("auto", "top right", "top left", "bottom right", "bottom left", "top", "bottom",
|
|
16
|
+
"outside right", "outside top", "outside bottom", "none")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _spines(v: Any) -> frozenset[str]:
|
|
20
|
+
if isinstance(v, str):
|
|
21
|
+
s = v.strip().lower()
|
|
22
|
+
if s == "all":
|
|
23
|
+
return frozenset(_SPINES)
|
|
24
|
+
if s == "none":
|
|
25
|
+
return frozenset()
|
|
26
|
+
parts = s.replace(",", " ").split()
|
|
27
|
+
else:
|
|
28
|
+
parts = [str(p).lower() for p in v]
|
|
29
|
+
bad = [p for p in parts if p not in _SPINES]
|
|
30
|
+
if bad:
|
|
31
|
+
raise ValueError(f"unknown spine {bad[0]!r} (use left, bottom, right, top, all, none)")
|
|
32
|
+
return frozenset(parts)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _position(v: Any) -> str:
|
|
36
|
+
s = str(getattr(v, "value", v)).strip().lower().replace("_", " ").replace("-", " ")
|
|
37
|
+
s = " ".join(s.split())
|
|
38
|
+
if s not in _POSITIONS:
|
|
39
|
+
raise ValueError(f"expected one of {', '.join(map(repr, _POSITIONS))}, got {v!r}")
|
|
40
|
+
return s
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _plot(v: Any) -> Any:
|
|
44
|
+
if not isinstance(v, Plot):
|
|
45
|
+
raise ValueError(f"expected a Plot, got {v!r}")
|
|
46
|
+
return v
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
_INHERITED = dict(
|
|
50
|
+
font_size=Setting(number(positive=True), inherited=True, doc="Base font size (pt).", length=True),
|
|
51
|
+
font_family=Setting(str, inherited=True, doc="Font family."),
|
|
52
|
+
text_color=Setting(color, inherited=True, doc="Text color."),
|
|
53
|
+
line_width=Setting(number(lo=0), inherited=True, doc="Default line width (pt).", length=True),
|
|
54
|
+
palette=Setting(check_palette, inherited=True, doc="Colors of series without one."),
|
|
55
|
+
alpha=Setting(number(0, 1), inherited=True, doc="Default opacity of series."),
|
|
56
|
+
rasterize=Setting(either(boolean, number(positive=True)), inherited=True,
|
|
57
|
+
doc="`True` or a dpi: series drawn as images inside the PDF."),
|
|
58
|
+
simplify=Setting(either(literal("auto"), boolean, number(positive=True)), inherited=True,
|
|
59
|
+
doc="pdf_io's `simplify` for every series: `\"auto\"`, `True`, a dpi or "
|
|
60
|
+
"`False`."),
|
|
61
|
+
color_map=Setting(check_cmap, inherited=True,
|
|
62
|
+
doc="Default color map (alias `cmap`): of heatmaps, contours and "
|
|
63
|
+
"numbers given as colors."),
|
|
64
|
+
data=Setting(lambda v: v, inherited=True,
|
|
65
|
+
doc="A table (dict, pandas or polars data frame, numpy structured array): "
|
|
66
|
+
"strings in the data arguments of series name its columns."),
|
|
67
|
+
**number_settings(),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Title(Settings):
|
|
72
|
+
"""The title of a plot (``plot.title``)."""
|
|
73
|
+
|
|
74
|
+
SETTINGS = table(
|
|
75
|
+
text=Setting(text, doc="Title text: a string or rich content."),
|
|
76
|
+
font_size=Setting(number(positive=True),
|
|
77
|
+
doc="Font size (pt); default 1.2 × the base font size.", length=True),
|
|
78
|
+
font_weight=Setting(one_of("normal", "bold"), doc="`normal` or `bold`."),
|
|
79
|
+
color=Setting(color, doc="Text color."),
|
|
80
|
+
align=Setting(one_of("center", "left", "right"),
|
|
81
|
+
doc="Alignment over the axes."),
|
|
82
|
+
pad=Setting(number(lo=0), doc="Gap above the axes (pt).", length=True),
|
|
83
|
+
over=Setting(one_of("axes", "plot"),
|
|
84
|
+
doc="Width the title is aligned within: the axes, or the axes and the "
|
|
85
|
+
"y axis labels."),
|
|
86
|
+
font_family=Setting(str, inherited=True, doc="Font family."),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def __init__(self, plot: "Plot") -> None:
|
|
90
|
+
self._plot = plot
|
|
91
|
+
super().__init__()
|
|
92
|
+
|
|
93
|
+
def _parent(self) -> Settings:
|
|
94
|
+
return self._plot
|
|
95
|
+
|
|
96
|
+
def __call__(self, text: Any = None, **settings: Any) -> "Title":
|
|
97
|
+
if text is not None:
|
|
98
|
+
settings["text"] = text
|
|
99
|
+
return self.set(**settings)
|
|
100
|
+
|
|
101
|
+
def __repr__(self) -> str:
|
|
102
|
+
return f"Title({self.get('text')!r})"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class Legend(Settings):
|
|
106
|
+
"""The legend of a plot (``plot.legend``): an entry per named series."""
|
|
107
|
+
|
|
108
|
+
SETTINGS = table(
|
|
109
|
+
position=Setting(_position,
|
|
110
|
+
doc="`auto`, `top right`, `top left`, `bottom right`, `bottom left`, "
|
|
111
|
+
"`top`, `bottom`, `outside right`, `outside top`, `outside bottom` "
|
|
112
|
+
"or `none`."),
|
|
113
|
+
columns=Setting(number(lo=1, integer=True), doc="Number of columns."),
|
|
114
|
+
frame=Setting(boolean, doc="Draw a frame and background."),
|
|
115
|
+
title=Setting(text,
|
|
116
|
+
doc="Legend title; default the `group` column name."),
|
|
117
|
+
font_size=Setting(number(positive=True),
|
|
118
|
+
doc="Font size (pt).", length=True),
|
|
119
|
+
color=Setting(color, doc="Text color."),
|
|
120
|
+
background=Setting(color, doc="Background of a framed legend."),
|
|
121
|
+
frame_color=Setting(color, doc="Frame color."),
|
|
122
|
+
pad=Setting(number(lo=0), doc="Gap to the axes (pt).", length=True),
|
|
123
|
+
frame_width=Setting(number(lo=0), doc="Frame line width (pt).", length=True),
|
|
124
|
+
frame_radius=Setting(number(lo=0), doc="Corner radius of the frame (pt).",
|
|
125
|
+
length=True),
|
|
126
|
+
frame_alpha=Setting(number(0, 1), doc="Opacity of the frame and background."),
|
|
127
|
+
glyph_alpha=Setting(number(0, 1),
|
|
128
|
+
doc="Fill opacity of `box` and `circle` symbols, outlined in the "
|
|
129
|
+
"opaque color; default: the series' own fill."),
|
|
130
|
+
glyph=Setting(one_of("series", "box", "circle", "line"),
|
|
131
|
+
doc="Entry symbol: as the series draws itself, or a filled `box`, a "
|
|
132
|
+
"`circle` or a `line` in the series color."),
|
|
133
|
+
orientation=Setting(one_of("auto", "vertical", "horizontal"),
|
|
134
|
+
doc="`horizontal`: entries side by side; `auto`: horizontal at "
|
|
135
|
+
"`top`, `bottom`, `outside top` and `outside bottom`."),
|
|
136
|
+
glyph_width=Setting(number(positive=True),
|
|
137
|
+
doc="Width of an entry's symbol, in font sizes."),
|
|
138
|
+
row_height=Setting(number(positive=True), doc="Height of an entry, in font sizes."),
|
|
139
|
+
font_family=Setting(str, inherited=True, doc="Font family."),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def __init__(self, plot: "Plot") -> None:
|
|
143
|
+
self._plot = plot
|
|
144
|
+
super().__init__()
|
|
145
|
+
|
|
146
|
+
def _parent(self) -> Settings:
|
|
147
|
+
return self._plot
|
|
148
|
+
|
|
149
|
+
def __call__(self, position: Any = None, **settings: Any) -> "Legend":
|
|
150
|
+
if position is not None:
|
|
151
|
+
settings["position"] = position
|
|
152
|
+
return self.set(**settings)
|
|
153
|
+
|
|
154
|
+
def __repr__(self) -> str:
|
|
155
|
+
return f"Legend({self.get('position')!r})"
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class Plot(Settings):
|
|
159
|
+
"""One plot: axes, series and decorations.
|
|
160
|
+
|
|
161
|
+
``pl.Plot(**settings)`` creates a one-plot figure (``plot.figure``);
|
|
162
|
+
``figure.plot(…)`` adds a plot to an existing figure.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
SETTINGS = table(
|
|
166
|
+
spines=Setting(_spines,
|
|
167
|
+
doc="Axis lines drawn: any of `left`, `bottom`, `right`, `top`, or "
|
|
168
|
+
"`all`, `none`."),
|
|
169
|
+
aspect=Setting(number(positive=True),
|
|
170
|
+
doc="Data aspect ratio: 1 makes one unit as long on both axes."),
|
|
171
|
+
background=Setting(color, doc="Background color of the axes area."),
|
|
172
|
+
border=Setting(color, doc="Color of a frame around the axes area (drawn under the "
|
|
173
|
+
"axis lines); default none."),
|
|
174
|
+
border_width=Setting(number(lo=0), doc="Width of the frame (pt).", length=True),
|
|
175
|
+
clip=Setting(boolean, doc="Clip series to the axes area."),
|
|
176
|
+
axes=Setting(boolean, doc="Draw axes; `False` hides both axes, drops padding "
|
|
177
|
+
"and fixes the aspect to 1 (set by pie charts)."),
|
|
178
|
+
share_x=Setting(_plot, doc="Another plot of the figure whose x axis this plot "
|
|
179
|
+
"shares (groups chain); overrides the figure's `share_x`."),
|
|
180
|
+
share_y=Setting(_plot, doc="Another plot whose y axis this plot shares."),
|
|
181
|
+
share=Setting(_plot, doc="Another plot whose x and y axes this plot shares."),
|
|
182
|
+
**_INHERITED,
|
|
183
|
+
)
|
|
184
|
+
#: Keywords accepted besides settings (``title="…"`` sets the title text).
|
|
185
|
+
EXTRA = ("title",)
|
|
186
|
+
|
|
187
|
+
def __init__(self, figure: Any = None, id: Any = None, _twin: Any = None,
|
|
188
|
+
**settings: Any) -> None:
|
|
189
|
+
from .figure import Figure
|
|
190
|
+
|
|
191
|
+
own, fig_kw = route(settings, [Plot, Figure], extra={0: Plot.EXTRA})
|
|
192
|
+
title = own.pop("title", None)
|
|
193
|
+
if figure is None:
|
|
194
|
+
figure = Figure(**fig_kw)
|
|
195
|
+
elif fig_kw:
|
|
196
|
+
figure.set(**fig_kw)
|
|
197
|
+
self.figure = figure
|
|
198
|
+
self.x_axis = Axis(self, "x")
|
|
199
|
+
self.y_axis = Axis(self, "y")
|
|
200
|
+
self.title = Title(self)
|
|
201
|
+
self.legend = Legend(self)
|
|
202
|
+
self.series: list[Any] = []
|
|
203
|
+
self.decorations: list[Any] = []
|
|
204
|
+
self.colorbars: list[Any] = []
|
|
205
|
+
self.twins: list[Plot] = []
|
|
206
|
+
#: ``(main plot, "y" or "x")`` for a twin: the axis it has of its own.
|
|
207
|
+
self.twin_of: tuple[Plot, str] | None = _twin
|
|
208
|
+
super().__init__(**own)
|
|
209
|
+
if title is not None:
|
|
210
|
+
self.title.set(text=title)
|
|
211
|
+
self.id = figure._attach(self, id) if _twin is None else None
|
|
212
|
+
|
|
213
|
+
def _parent(self) -> Settings:
|
|
214
|
+
return self.twin_of[0] if self.twin_of is not None else self.figure
|
|
215
|
+
|
|
216
|
+
def twin_y(self, **settings: Any) -> "Plot":
|
|
217
|
+
"""A plot on the same axes with its own y axis (on the right) and the x axis of
|
|
218
|
+
this plot: add series to it and set its ``y_axis``."""
|
|
219
|
+
t = Plot(figure=self.figure, _twin=(self, "y"), **settings)
|
|
220
|
+
t.x_axis.set(visible=False)
|
|
221
|
+
if not t.y_axis.is_set("side"):
|
|
222
|
+
t.y_axis.set(side="right")
|
|
223
|
+
if not t.is_set("spines"):
|
|
224
|
+
t.set(spines="left") # drawn on the right with the axis
|
|
225
|
+
self.twins.append(t)
|
|
226
|
+
return t
|
|
227
|
+
|
|
228
|
+
def twin_x(self, **settings: Any) -> "Plot":
|
|
229
|
+
"""A plot on the same axes with its own x axis (on top) and the y axis of this
|
|
230
|
+
plot."""
|
|
231
|
+
t = Plot(figure=self.figure, _twin=(self, "x"), **settings)
|
|
232
|
+
t.y_axis.set(visible=False)
|
|
233
|
+
if not t.x_axis.is_set("side"):
|
|
234
|
+
t.x_axis.set(side="top")
|
|
235
|
+
if not t.is_set("spines"):
|
|
236
|
+
t.set(spines="bottom")
|
|
237
|
+
self.twins.append(t)
|
|
238
|
+
return t
|
|
239
|
+
|
|
240
|
+
def set(self, **settings: Any) -> "Plot":
|
|
241
|
+
settings = canonical_kwargs(settings, self.SETTINGS, "Plot")
|
|
242
|
+
fig_share = {k: settings.pop(k) for k in ("share_x", "share_y", "share")
|
|
243
|
+
if k in settings and settings[k] is not None
|
|
244
|
+
and not isinstance(settings[k], Plot)}
|
|
245
|
+
if fig_share:
|
|
246
|
+
self.figure.set(**fig_share)
|
|
247
|
+
title = settings.pop("title", None)
|
|
248
|
+
if title is not None:
|
|
249
|
+
self.title.set(text=title)
|
|
250
|
+
super().set(**settings)
|
|
251
|
+
return self
|
|
252
|
+
|
|
253
|
+
def __repr__(self) -> str:
|
|
254
|
+
return f"Plot({self.id!r})"
|
|
255
|
+
|
|
256
|
+
def own_share(self, role: str) -> Any:
|
|
257
|
+
"""The plot this plot's ``role`` axis is paired with, or ``None``."""
|
|
258
|
+
return self.get(f"share_{role}") or self.get("share")
|
|
259
|
+
|
|
260
|
+
def _add_series(self, s: Any) -> None:
|
|
261
|
+
self.series.append(s)
|
|
262
|
+
|
|
263
|
+
# -- series ----------------------------------------------------------------------
|
|
264
|
+
|
|
265
|
+
def scatter(self, x: Any, y: Any, **settings: Any) -> Any:
|
|
266
|
+
"""Markers at points ``(x, y)``: see :class:`~plot_io.Scatter`."""
|
|
267
|
+
from .series.scatter import Scatter
|
|
268
|
+
return Scatter(x, y, plot=self, **settings)
|
|
269
|
+
|
|
270
|
+
def line(self, x: Any, y: Any = None, **settings: Any) -> Any:
|
|
271
|
+
"""A line through points: see :class:`~plot_io.Line`."""
|
|
272
|
+
from .series.line import Line
|
|
273
|
+
return Line(x, y, plot=self, **settings)
|
|
274
|
+
|
|
275
|
+
def fill(self, x: Any, y1: Any, y2: Any = 0.0, **settings: Any) -> Any:
|
|
276
|
+
"""The area between two curves: see :class:`~plot_io.Fill`."""
|
|
277
|
+
from .series.fill import Fill
|
|
278
|
+
return Fill(x, y1, y2, plot=self, **settings)
|
|
279
|
+
|
|
280
|
+
def box(self, values: Any, **settings: Any) -> Any:
|
|
281
|
+
"""Box plots: see :class:`~plot_io.Box`."""
|
|
282
|
+
from .series.box import Box
|
|
283
|
+
return Box(values, plot=self, **settings)
|
|
284
|
+
|
|
285
|
+
def heatmap(self, values: Any, **settings: Any) -> Any:
|
|
286
|
+
"""A grid of values as colored cells: see :class:`~plot_io.Heatmap`."""
|
|
287
|
+
from .series.heatmap import Heatmap
|
|
288
|
+
return Heatmap(values, plot=self, **settings)
|
|
289
|
+
|
|
290
|
+
def bar(self, x: Any, height: Any = None, **settings: Any) -> Any:
|
|
291
|
+
"""Bars: see :class:`~plot_io.Bar`."""
|
|
292
|
+
from .series.bar import Bar
|
|
293
|
+
return Bar(x, height, plot=self, **settings)
|
|
294
|
+
|
|
295
|
+
def hist(self, values: Any, **settings: Any) -> Any:
|
|
296
|
+
"""A histogram: see :class:`~plot_io.Hist`."""
|
|
297
|
+
from .series.hist import Hist
|
|
298
|
+
return Hist(values, plot=self, **settings)
|
|
299
|
+
|
|
300
|
+
def kde(self, values: Any, y: Any = None, **settings: Any) -> Any:
|
|
301
|
+
"""A kernel density estimate: see :class:`~plot_io.Kde`; with ``y``, 2-D
|
|
302
|
+
density contours (:class:`~plot_io.Kde2d`)."""
|
|
303
|
+
if y is not None:
|
|
304
|
+
from .series.kde2d import Kde2d
|
|
305
|
+
return Kde2d(values, y, plot=self, **settings)
|
|
306
|
+
from .series.kde import Kde
|
|
307
|
+
return Kde(values, plot=self, **settings)
|
|
308
|
+
|
|
309
|
+
def ecdf(self, values: Any, **settings: Any) -> Any:
|
|
310
|
+
"""An empirical cumulative distribution: see :class:`~plot_io.Ecdf`."""
|
|
311
|
+
from .series.ecdf import Ecdf
|
|
312
|
+
return Ecdf(values, plot=self, **settings)
|
|
313
|
+
|
|
314
|
+
def violin(self, values: Any, **settings: Any) -> Any:
|
|
315
|
+
"""Violin plots: see :class:`~plot_io.Violin`."""
|
|
316
|
+
from .series.violin import Violin
|
|
317
|
+
return Violin(values, plot=self, **settings)
|
|
318
|
+
|
|
319
|
+
def strip(self, values: Any, **settings: Any) -> Any:
|
|
320
|
+
"""Jittered points per category: see :class:`~plot_io.Strip`."""
|
|
321
|
+
from .series.catpoints import Strip
|
|
322
|
+
return Strip(values, plot=self, **settings)
|
|
323
|
+
|
|
324
|
+
def swarm(self, values: Any, **settings: Any) -> Any:
|
|
325
|
+
"""Non-overlapping points per category: see :class:`~plot_io.Swarm`."""
|
|
326
|
+
from .series.catpoints import Swarm
|
|
327
|
+
return Swarm(values, plot=self, **settings)
|
|
328
|
+
|
|
329
|
+
def area(self, x: Any, ys: Any, **settings: Any) -> Any:
|
|
330
|
+
"""Stacked areas: see :class:`~plot_io.Area`."""
|
|
331
|
+
from .series.area import Area
|
|
332
|
+
return Area(x, ys, plot=self, **settings)
|
|
333
|
+
|
|
334
|
+
def errorbar(self, x: Any, y: Any, **settings: Any) -> Any:
|
|
335
|
+
"""Error bars: see :class:`~plot_io.Errorbar`."""
|
|
336
|
+
from .series.errorbar import Errorbar
|
|
337
|
+
return Errorbar(x, y, plot=self, **settings)
|
|
338
|
+
|
|
339
|
+
def stem(self, x: Any, y: Any = None, **settings: Any) -> Any:
|
|
340
|
+
"""Stems with markers: see :class:`~plot_io.Stem`."""
|
|
341
|
+
from .series.stem import Stem
|
|
342
|
+
return Stem(x, y, plot=self, **settings)
|
|
343
|
+
|
|
344
|
+
def regression(self, x: Any, y: Any, **settings: Any) -> Any:
|
|
345
|
+
"""A fitted line with a confidence band: see :class:`~plot_io.Regression`."""
|
|
346
|
+
from .series.regression import Regression
|
|
347
|
+
return Regression(x, y, plot=self, **settings)
|
|
348
|
+
|
|
349
|
+
def hist2d(self, x: Any, y: Any, **settings: Any) -> Any:
|
|
350
|
+
"""A 2-D histogram: see :class:`~plot_io.Hist2d`."""
|
|
351
|
+
from .series.hist2d import Hist2d
|
|
352
|
+
return Hist2d(x, y, plot=self, **settings)
|
|
353
|
+
|
|
354
|
+
def hexbin(self, x: Any, y: Any, **settings: Any) -> Any:
|
|
355
|
+
"""A hexagonal 2-D histogram: see :class:`~plot_io.Hexbin`."""
|
|
356
|
+
from .series.hexbin import Hexbin
|
|
357
|
+
return Hexbin(x, y, plot=self, **settings)
|
|
358
|
+
|
|
359
|
+
def contour(self, values: Any, **settings: Any) -> Any:
|
|
360
|
+
"""Contour lines and bands of a grid: see :class:`~plot_io.Contour`."""
|
|
361
|
+
from .series.contour import Contour
|
|
362
|
+
return Contour(values, plot=self, **settings)
|
|
363
|
+
|
|
364
|
+
def pie(self, values: Any, **settings: Any) -> Any:
|
|
365
|
+
"""A pie or donut chart: see :class:`~plot_io.Pie`."""
|
|
366
|
+
from .series.pie import Pie
|
|
367
|
+
return Pie(values, plot=self, **settings)
|
|
368
|
+
|
|
369
|
+
# -- decorations -----------------------------------------------------------------
|
|
370
|
+
|
|
371
|
+
def hline(self, y: Any, **settings: Any) -> Any:
|
|
372
|
+
"""A horizontal reference line at ``y``."""
|
|
373
|
+
from .decorations import HLine
|
|
374
|
+
return HLine(self, y, **settings)
|
|
375
|
+
|
|
376
|
+
def vline(self, x: Any, **settings: Any) -> Any:
|
|
377
|
+
"""A vertical reference line at ``x``."""
|
|
378
|
+
from .decorations import VLine
|
|
379
|
+
return VLine(self, x, **settings)
|
|
380
|
+
|
|
381
|
+
def axline(self, point: Any, slope: Any = None, point2: Any = None,
|
|
382
|
+
**settings: Any) -> Any:
|
|
383
|
+
"""An infinite line through ``point`` with ``slope``, or through ``point2``."""
|
|
384
|
+
from .decorations import AxLine
|
|
385
|
+
return AxLine(self, point, slope, point2, **settings)
|
|
386
|
+
|
|
387
|
+
def hspan(self, y0: Any, y1: Any, **settings: Any) -> Any:
|
|
388
|
+
"""A horizontal band from ``y0`` to ``y1``."""
|
|
389
|
+
from .decorations import HSpan
|
|
390
|
+
return HSpan(self, y0, y1, **settings)
|
|
391
|
+
|
|
392
|
+
def vspan(self, x0: Any, x1: Any, **settings: Any) -> Any:
|
|
393
|
+
"""A vertical band from ``x0`` to ``x1``."""
|
|
394
|
+
from .decorations import VSpan
|
|
395
|
+
return VSpan(self, x0, x1, **settings)
|
|
396
|
+
|
|
397
|
+
def text(self, x: Any, y: Any, content: Any, **settings: Any) -> Any:
|
|
398
|
+
"""Text at ``(x, y)``."""
|
|
399
|
+
from .decorations import Text
|
|
400
|
+
return Text(self, x, y, content, **settings)
|
|
401
|
+
|
|
402
|
+
def annotate(self, content: Any, xy: Any, xytext: Any = None, **settings: Any) -> Any:
|
|
403
|
+
"""Text at ``xytext`` with an arrow to ``xy``."""
|
|
404
|
+
from .decorations import Annotate
|
|
405
|
+
return Annotate(self, content, xy, xytext, **settings)
|
|
406
|
+
|
|
407
|
+
def colorbar(self, source: Any = None, **settings: Any) -> Any:
|
|
408
|
+
"""A colorbar attached to this plot (``source``: a mapped series or a
|
|
409
|
+
``ColorMap``; default: the last mapped series)."""
|
|
410
|
+
from .colorbar import Colorbar
|
|
411
|
+
cb = Colorbar(source, plot=self, **settings)
|
|
412
|
+
self.colorbars.append(cb)
|
|
413
|
+
return cb
|
|
414
|
+
|
|
415
|
+
# -- output ----------------------------------------------------------------------
|
|
416
|
+
|
|
417
|
+
def draw(self, canvas: Any, x: float = 0.0, y: float = 0.0) -> tuple[float, float]:
|
|
418
|
+
"""Draw the plot alone on a pdf_io canvas with its top-left corner at ``(x, y)``
|
|
419
|
+
(page units); returns its size in points."""
|
|
420
|
+
from . import render
|
|
421
|
+
return render.draw(self.figure, canvas, x, y, only=self)
|
|
422
|
+
|
|
423
|
+
def save(self, path: Any, **options: Any) -> None:
|
|
424
|
+
"""Save the plot alone: PDF, PNG, JPEG or JPEG XL by the extension (see
|
|
425
|
+
:meth:`Figure.save`)."""
|
|
426
|
+
from . import render
|
|
427
|
+
render.save(self.figure, path, only=self, **options)
|
|
428
|
+
|
|
429
|
+
def to_bytes(self, format: str = "pdf", **options: Any) -> bytes:
|
|
430
|
+
"""The plot alone as ``"pdf"``, ``"png"``, ``"jpeg"`` or ``"jxl"`` bytes."""
|
|
431
|
+
from . import render
|
|
432
|
+
return render.to_bytes(self.figure, format, only=self, **options)
|
|
433
|
+
|
|
434
|
+
def _repr_png_(self) -> bytes:
|
|
435
|
+
return self.to_bytes("png")
|