plotille 6.0.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.

Potentially problematic release.


This version of plotille might be problematic. Click here for more details.

@@ -0,0 +1,295 @@
1
+ # The MIT License
2
+
3
+ # Copyright (c) 2017 - 2025 Tammo Ippen, tammo.ippen@posteo.de
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ """Data container classes for plotille.
24
+
25
+ Architecture Note:
26
+ ------------------
27
+ All input data (X, Y values) is normalized to float immediately upon construction:
28
+
29
+ - Numeric values (int, float) are converted to float
30
+ - Datetime values are converted to timestamps (float)
31
+ - Original type information is preserved in DataMetadata objects
32
+ - This allows type-safe internal operations while maintaining a flexible
33
+ public API
34
+
35
+ The normalization happens in each class's __init__ method using InputFormatter.
36
+ Display formatting (axis labels, etc.) uses the metadata to format values
37
+ correctly for the original type.
38
+ """
39
+
40
+ from collections.abc import Sequence
41
+ from typing import Literal, final
42
+
43
+ from plotille._canvas import Canvas
44
+ from plotille._colors import ColorDefinition
45
+ from plotille._data_metadata import DataMetadata
46
+ from plotille._input_formatter import InputFormatter
47
+
48
+ from . import Colormap, _cmaps
49
+ from ._util import DataValues, hist
50
+
51
+
52
+ class Plot:
53
+ def __init__(
54
+ self,
55
+ X: DataValues,
56
+ Y: DataValues,
57
+ lc: ColorDefinition,
58
+ interp: Literal["linear"] | None,
59
+ label: str | None,
60
+ marker: str | None,
61
+ formatter: InputFormatter | None = None,
62
+ ) -> None:
63
+ if len(X) != len(Y):
64
+ raise ValueError("X and Y dim have to be the same.")
65
+ if interp not in ("linear", None):
66
+ raise ValueError('Only "linear" and None are allowed values for `interp`.')
67
+
68
+ self._formatter = formatter if formatter is not None else InputFormatter()
69
+ self.X_metadata = DataMetadata.from_sequence(X)
70
+ self.Y_metadata = DataMetadata.from_sequence(Y)
71
+ self.X = [self._formatter.convert(x) for x in X]
72
+ self.Y = [self._formatter.convert(y) for y in Y]
73
+
74
+ self.lc = lc
75
+ self.interp = interp
76
+ self.label = label
77
+ self.marker = marker
78
+
79
+ def width_vals(self) -> list[float]:
80
+ """Return X values as floats for limit calculation."""
81
+ return self.X
82
+
83
+ def height_vals(self) -> list[float]:
84
+ """Return Y values as floats for limit calculation."""
85
+ return self.Y
86
+
87
+ def write(self, canvas: Canvas, with_colors: bool, in_fmt: InputFormatter) -> None:
88
+ from_points = zip(self.X, self.Y, strict=True)
89
+ to_points = zip(self.X, self.Y, strict=True)
90
+
91
+ # remove first point of to_points
92
+ (x0, y0) = next(to_points)
93
+
94
+ color = self.lc if with_colors else None
95
+
96
+ # print first point
97
+ canvas.point(x0, y0, color=color, marker=self.marker)
98
+
99
+ # plot other points and lines
100
+ for (x0, y0), (x, y) in zip(from_points, to_points, strict=False):
101
+ canvas.point(x, y, color=color, marker=self.marker)
102
+ if self.interp == "linear":
103
+ # no marker for interpolated values
104
+ canvas.line(x0, y0, x, y, color=color)
105
+
106
+
107
+ @final
108
+ class Histogram:
109
+ def __init__(self, X: DataValues, bins: int, lc: ColorDefinition) -> None:
110
+ # Normalize data first
111
+ self._formatter = InputFormatter()
112
+ self.X_metadata = DataMetadata.from_sequence(X)
113
+ self.X = [self._formatter.convert(x) for x in X]
114
+ # Histogram Y values are always numeric (frequency counts)
115
+ self.Y_metadata = DataMetadata(is_datetime=False, timezone=None)
116
+
117
+ # Compute histogram on normalized data
118
+ frequencies, buckets = hist(self.X, bins)
119
+
120
+ # Store everything
121
+ self.bins = bins
122
+ self.frequencies = frequencies
123
+ self.buckets = buckets
124
+ self.lc = lc
125
+
126
+ def width_vals(self) -> list[float]:
127
+ """Return normalized X values as floats."""
128
+ return self.X
129
+
130
+ def height_vals(self) -> list[int]:
131
+ """Return histogram frequencies."""
132
+ return self.frequencies
133
+
134
+ def write(self, canvas: Canvas, with_colors: bool, in_fmt: InputFormatter) -> None:
135
+ # how fat will one bar of the histogram be
136
+ x_diff = canvas.dots_between(self.buckets[0], 0, self.buckets[1], 0)[0] or 1
137
+ bin_size = (self.buckets[1] - self.buckets[0]) / x_diff
138
+
139
+ color = self.lc if with_colors else None
140
+ for i in range(self.bins):
141
+ # for each bucket
142
+ if self.frequencies[i] > 0:
143
+ for j in range(x_diff):
144
+ # print bar
145
+ x_ = self.buckets[i] + j * bin_size
146
+
147
+ if canvas.xmin <= x_ <= canvas.xmax:
148
+ canvas.line(x_, 0, x_, self.frequencies[i], color=color)
149
+
150
+
151
+ @final
152
+ class Text:
153
+ def __init__(
154
+ self,
155
+ X: DataValues,
156
+ Y: DataValues,
157
+ texts: Sequence[str],
158
+ lc: ColorDefinition,
159
+ formatter: InputFormatter | None = None,
160
+ ) -> None:
161
+ if len(X) != len(Y) != len(texts):
162
+ raise ValueError("X, Y and texts dim have to be the same.")
163
+
164
+ self._formatter = formatter if formatter is not None else InputFormatter()
165
+ self.X_metadata = DataMetadata.from_sequence(X)
166
+ self.Y_metadata = DataMetadata.from_sequence(Y)
167
+ self.X = [self._formatter.convert(x) for x in X]
168
+ self.Y = [self._formatter.convert(y) for y in Y]
169
+ self.texts = texts
170
+ self.lc = lc
171
+
172
+ def width_vals(self) -> list[float]:
173
+ """Return X values as floats for limit calculation."""
174
+ return self.X
175
+
176
+ def height_vals(self) -> list[float]:
177
+ """Return Y values as floats for limit calculation."""
178
+ return self.Y
179
+
180
+ def write(self, canvas: Canvas, with_colors: bool, in_fmt: InputFormatter) -> None:
181
+ points = zip(self.X, self.Y, self.texts, strict=True)
182
+
183
+ color = self.lc if with_colors else None
184
+
185
+ # plot texts with color
186
+ for x, y, text in points:
187
+ canvas.text(x, y, text, color=color)
188
+
189
+
190
+ class Span:
191
+ def __init__(
192
+ self,
193
+ xmin: float,
194
+ xmax: float,
195
+ ymin: float,
196
+ ymax: float,
197
+ lc: ColorDefinition | None = None,
198
+ ):
199
+ if not (0 <= xmin <= xmax <= 1):
200
+ raise ValueError(
201
+ "xmin has to be <= xmax and both have to be within [0, 1]."
202
+ )
203
+ if not (0 <= ymin <= ymax <= 1):
204
+ raise ValueError(
205
+ "ymin has to be <= ymax and both have to be within [0, 1]."
206
+ )
207
+ self.xmin: float = xmin
208
+ self.xmax: float = xmax
209
+ self.ymin: float = ymin
210
+ self.ymax: float = ymax
211
+ self.lc: ColorDefinition | None = lc
212
+
213
+ def write(self, canvas: Canvas, with_colors: bool) -> None:
214
+ color = self.lc if with_colors else None
215
+
216
+ # plot texts with color
217
+ xdelta = canvas.xmax_inside - canvas.xmin
218
+ assert xdelta > 0
219
+
220
+ ydelta = canvas.ymax_inside - canvas.ymin
221
+ assert ydelta > 0
222
+
223
+ canvas.rect(
224
+ canvas.xmin + self.xmin * xdelta,
225
+ canvas.ymin + self.ymin * ydelta,
226
+ canvas.xmin + self.xmax * xdelta,
227
+ canvas.ymin + self.ymax * ydelta,
228
+ color=color,
229
+ )
230
+
231
+
232
+ HeatInput = Sequence[Sequence[float]] | Sequence[Sequence[Sequence[float]]]
233
+
234
+
235
+ @final
236
+ class Heat:
237
+ def __init__(self, X: HeatInput, cmap: str | Colormap | None = None):
238
+ """Initialize a Heat-class.
239
+
240
+ Parameters
241
+ ----------
242
+ X: array-like
243
+ The image data. Supported array shapes are:
244
+ - (M, N): an image with scalar data. The values are mapped
245
+ to colors using a colormap. The values have to be in
246
+ the 0-1 (float) range. Out of range, invalid type and
247
+ None values are handled by the cmap.
248
+ - (M, N, 3): an image with RGB values (0-1 float or 0-255 int).
249
+
250
+ The first two dimensions (M, N) define the rows and columns of the image.
251
+
252
+ cmap: cmapstr or Colormap, default: 'viridis'
253
+ The Colormap instance or registered colormap name used
254
+ to map scalar data to colors. This parameter is ignored
255
+ for RGB data.
256
+ """
257
+ assert len(X)
258
+ assert cmap is None or isinstance(cmap, (str, _cmaps.Colormap))
259
+ len_first = len(X[0])
260
+ assert all(len(x) == len_first for x in X)
261
+ self._X: HeatInput = X
262
+
263
+ if cmap is None:
264
+ cmap = "viridis"
265
+
266
+ if isinstance(cmap, str):
267
+ cmap = _cmaps.cmaps[cmap]()
268
+ self.cmap = cmap
269
+
270
+ @property
271
+ def X(self) -> HeatInput:
272
+ return self._X
273
+
274
+ def write(self, canvas: Canvas) -> None:
275
+ assert len(self.X)
276
+ assert canvas.height == len(self.X)
277
+ assert canvas.width == len(self.X[0])
278
+
279
+ flat = [x for xs in self.X for x in xs]
280
+ try:
281
+ assert all(len(pixel) == 3 for pixel in flat) # type: ignore[arg-type]
282
+ # assume rgb
283
+ if all(
284
+ isinstance(v, float) and 0 <= v <= 1
285
+ for pixel in flat
286
+ for v in pixel # type: ignore[union-attr]
287
+ ):
288
+ # 0 - 1 values => make 0-255 int values
289
+ flat = [ # type: ignore[misc]
290
+ (round(r * 255), round(g * 255), round(b * 255)) for r, g, b in flat
291
+ ]
292
+ canvas.image(flat) # type: ignore[arg-type]
293
+ except TypeError:
294
+ # cannot call len on a float
295
+ canvas.image(self.cmap(flat)) # type: ignore[arg-type]
plotille/_graphs.py ADDED
@@ -0,0 +1,373 @@
1
+ # The MIT License
2
+
3
+ # Copyright (c) 2017 - 2025 Tammo Ippen, tammo.ippen@posteo.de
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ import os
24
+ from collections.abc import Sequence
25
+ from datetime import timedelta
26
+ from math import log
27
+ from typing import Literal
28
+
29
+ from ._colors import ColorDefinition, ColorMode, color
30
+ from ._data_metadata import DataMetadata
31
+ from ._figure import Figure
32
+ from ._input_formatter import InputFormatter
33
+ from ._util import DataValue, DataValues
34
+ from ._util import hist as compute_hist
35
+
36
+
37
+ def hist_aggregated(
38
+ counts: list[int],
39
+ bins: Sequence[float],
40
+ width: int = 80,
41
+ log_scale: bool = False,
42
+ linesep: str = os.linesep,
43
+ lc: ColorDefinition = None,
44
+ bg: ColorDefinition = None,
45
+ color_mode: ColorMode = "names",
46
+ meta: DataMetadata | None = None,
47
+ ) -> str:
48
+ """
49
+ Create histogram for aggregated data.
50
+
51
+ Parameters:
52
+ counts: List[int] Counts for each bucket.
53
+ bins: List[float] Limits for the bins for the provided counts: limits for
54
+ bin `i` are `[bins[i], bins[i+1])`.
55
+ Hence, `len(bins) == len(counts) + 1`.
56
+ width: int The number of characters for the width (columns).
57
+ log_scale: bool Scale the histogram with `log` function.
58
+ linesep: str The requested line separator. default: os.linesep
59
+ lc: ColorDefinition Give the line color.
60
+ bg: ColorDefinition Give the background color.
61
+ color_mode: ColorMode Specify color input mode; 'names' (default), 'byte' or
62
+ 'rgb' see plotille.color.__docs__
63
+ meta: DataMetadata | None For conversion of datetime values.
64
+ Returns:
65
+ str: histogram over `X` from left to right.
66
+ """
67
+
68
+ def _scale(a: int) -> float | int:
69
+ if log_scale and a > 0:
70
+ return log(a)
71
+ return a
72
+
73
+ if meta is None:
74
+ meta = DataMetadata(is_datetime=False)
75
+
76
+ h = counts
77
+ b = bins
78
+
79
+ ipf = InputFormatter()
80
+ h_max = _scale(max(h)) or 1
81
+ max_ = b[-1]
82
+ min_ = b[0]
83
+ # bins are always normalized to float
84
+ delta = max_ - min_
85
+ delta_display = timedelta(seconds=delta) if meta.is_datetime else delta
86
+
87
+ bins_count = len(h)
88
+
89
+ canvas = [" bucket | {} {}".format("_" * width, "Total Counts")]
90
+ lasts = ["", "⠂", "⠆", "⠇", "⡇", "⡗", "⡷", "⡿"]
91
+ for i in range(bins_count):
92
+ height = int(width * 8 * _scale(h[i]) / h_max)
93
+ canvas += [
94
+ "[{}, {}) | {} {}".format(
95
+ ipf.fmt(
96
+ meta.convert_for_display(b[i]),
97
+ delta=delta_display,
98
+ chars=8,
99
+ left=True,
100
+ ),
101
+ ipf.fmt(
102
+ meta.convert_for_display(b[i + 1]),
103
+ delta=delta_display,
104
+ chars=8,
105
+ left=False,
106
+ ),
107
+ color(
108
+ "⣿" * (height // 8) + lasts[height % 8],
109
+ fg=lc,
110
+ bg=bg,
111
+ mode=color_mode,
112
+ )
113
+ + color(
114
+ "\u2800" * (width - (height // 8) + int(height % 8 == 0)),
115
+ bg=bg,
116
+ mode=color_mode,
117
+ ),
118
+ h[i],
119
+ )
120
+ ]
121
+ canvas += ["‾" * (2 * 8 + 2 + 3 + width + 12)]
122
+ return linesep.join(canvas)
123
+
124
+
125
+ def hist(
126
+ X: DataValues,
127
+ bins: int = 40,
128
+ width: int = 80,
129
+ log_scale: bool = False,
130
+ linesep: str = os.linesep,
131
+ lc: ColorDefinition = None,
132
+ bg: ColorDefinition = None,
133
+ color_mode: ColorMode = "names",
134
+ ) -> str:
135
+ """Create histogram over `X` from left to right
136
+
137
+ The values on the left are the center of the bucket, i.e. `(bin[i] + bin[i+1]) / 2`.
138
+ The values on the right are the total counts of this bucket.
139
+
140
+ Parameters:
141
+ X: List[float] The items to count over.
142
+ bins: int The number of bins to put X entries in (rows).
143
+ width: int The number of characters for the width (columns).
144
+ log_scale: bool Scale the histogram with `log` function.
145
+ linesep: str The requested line separator. default: os.linesep
146
+ lc: ColorDefinition Give the line color.
147
+ bg: ColorDefinition Give the background color.
148
+ color_mode: ColorMode Specify color input mode; 'names' (default), 'byte' or
149
+ 'rgb' see plotille.color.__docs__
150
+
151
+ Returns:
152
+ str: histogram over `X` from left to right.
153
+ """
154
+ # Normalize data to float before computing histogram
155
+ formatter = InputFormatter()
156
+ metadata = DataMetadata.from_sequence(X)
157
+ X_floats = [formatter.convert(x) for x in X]
158
+
159
+ counts, bins_list = compute_hist(X_floats, bins)
160
+
161
+ # bins_list are floats, use metadata for display
162
+ return hist_aggregated(
163
+ counts=counts,
164
+ bins=bins_list,
165
+ width=width,
166
+ log_scale=log_scale,
167
+ linesep=linesep,
168
+ lc=lc,
169
+ bg=bg,
170
+ color_mode=color_mode,
171
+ meta=metadata,
172
+ )
173
+
174
+
175
+ def histogram(
176
+ X: DataValues,
177
+ bins: int = 160,
178
+ width: int = 80,
179
+ height: int = 40,
180
+ X_label: str = "X",
181
+ Y_label: str = "Counts",
182
+ linesep: str = os.linesep,
183
+ x_min: DataValue | None = None,
184
+ x_max: DataValue | None = None,
185
+ y_min: DataValue | None = None,
186
+ y_max: DataValue | None = None,
187
+ lc: ColorDefinition = None,
188
+ bg: ColorDefinition = None,
189
+ color_mode: ColorMode = "names",
190
+ ) -> str:
191
+ """Create histogram over `X`
192
+
193
+ In contrast to `hist`, this is the more `usual` histogram from bottom
194
+ to up. The X-axis represents the values in `X` and the Y-axis is the
195
+ corresponding frequency.
196
+
197
+ Parameters:
198
+ X: List[float] The items to count over.
199
+ bins: int The number of bins to put X entries in (columns).
200
+ height: int The number of characters for the height (rows).
201
+ X_label: str Label for X-axis.
202
+ Y_label: str Label for Y-axis. max 8 characters.
203
+ linesep: str The requested line separator. default: os.linesep
204
+ x_min, x_max: float Limits for the displayed X values.
205
+ y_min, y_max: float Limits for the displayed Y values.
206
+ lc: ColorDefinition Give the line color.
207
+ bg: ColorDefinition Give the background color.
208
+ color_mode: ColorMode Specify color input mode; 'names' (default), 'byte' or
209
+ 'rgb' see plotille.color.__docs__
210
+
211
+ Returns:
212
+ str: histogram over `X`.
213
+ """
214
+ fig = Figure()
215
+ fig.width = width
216
+ fig.height = height
217
+ fig.x_label = X_label
218
+ fig.y_label = Y_label
219
+ fig.linesep = linesep
220
+ if x_min is not None:
221
+ fig.set_x_limits(min_=x_min)
222
+ if x_max is not None:
223
+ fig.set_x_limits(max_=x_max)
224
+ if y_min is not None:
225
+ fig.set_y_limits(min_=y_min)
226
+ if y_max is not None:
227
+ fig.set_y_limits(max_=y_max)
228
+ fig.background = bg
229
+ fig.color_mode = color_mode
230
+
231
+ if lc is None and bg is None:
232
+ fig.with_colors = False
233
+
234
+ fig.histogram(X, bins, lc)
235
+
236
+ return fig.show()
237
+
238
+
239
+ def scatter(
240
+ X: DataValues,
241
+ Y: DataValues,
242
+ width: int = 80,
243
+ height: int = 40,
244
+ X_label: str = "X",
245
+ Y_label: str = "Y",
246
+ linesep: str = os.linesep,
247
+ x_min: DataValue | None = None,
248
+ x_max: DataValue | None = None,
249
+ y_min: DataValue | None = None,
250
+ y_max: DataValue | None = None,
251
+ lc: ColorDefinition = None,
252
+ bg: ColorDefinition = None,
253
+ color_mode: ColorMode = "names",
254
+ origin: bool = True,
255
+ marker: str | None = None,
256
+ ) -> str:
257
+ """Create scatter plot with X , Y values
258
+
259
+ Basically plotting without interpolation:
260
+ `plot(X, Y, ... , interp=None)`
261
+
262
+ Parameters:
263
+ X: List[float] X values.
264
+ Y: List[float] Y values. X and Y must have the same number of entries.
265
+ width: int The number of characters for the width (columns) of the
266
+ canvas.
267
+ height: int The number of characters for the hight (rows) of the
268
+ canvas.
269
+ X_label: str Label for X-axis.
270
+ Y_label: str Label for Y-axis. max 8 characters.
271
+ linesep: str The requested line separator. default: os.linesep
272
+ x_min, x_max: float Limits for the displayed X values.
273
+ y_min, y_max: float Limits for the displayed Y values.
274
+ lc: ColorDefinition Give the line color.
275
+ bg: ColorDefinition Give the background color.
276
+ color_mode: ColorMode Specify color input mode; 'names' (default), 'byte' or
277
+ 'rgb' see plotille.color.__docs__
278
+ origin: bool Whether to print the origin. default: True
279
+ marker: str Instead of braille dots set a marker char.
280
+
281
+ Returns:
282
+ str: scatter plot over `X`, `Y`.
283
+ """
284
+ return plot(
285
+ X,
286
+ Y,
287
+ width,
288
+ height,
289
+ X_label,
290
+ Y_label,
291
+ linesep,
292
+ None,
293
+ x_min,
294
+ x_max,
295
+ y_min,
296
+ y_max,
297
+ lc,
298
+ bg,
299
+ color_mode,
300
+ origin,
301
+ marker,
302
+ )
303
+
304
+
305
+ def plot(
306
+ X: DataValues,
307
+ Y: DataValues,
308
+ width: int = 80,
309
+ height: int = 40,
310
+ X_label: str = "X",
311
+ Y_label: str = "Y",
312
+ linesep: str = os.linesep,
313
+ interp: Literal["linear"] | None = "linear",
314
+ x_min: DataValue | None = None,
315
+ x_max: DataValue | None = None,
316
+ y_min: DataValue | None = None,
317
+ y_max: DataValue | None = None,
318
+ lc: ColorDefinition = None,
319
+ bg: ColorDefinition = None,
320
+ color_mode: ColorMode = "names",
321
+ origin: bool = True,
322
+ marker: str | None = None,
323
+ ) -> str:
324
+ """Create plot with X , Y values and linear interpolation between points
325
+
326
+ Parameters:
327
+ X: List[float] X values.
328
+ Y: List[float] Y values. X and Y must have the same number of entries.
329
+ width: int The number of characters for the width (columns) of the
330
+ canvas.
331
+ height: int The number of characters for the hight (rows) of the
332
+ canvas.
333
+ X_label: str Label for X-axis.
334
+ Y_label: str Label for Y-axis. max 8 characters.
335
+ linesep: str The requested line separator. default: os.linesep
336
+ interp: Optional[str] Specify interpolation; values None, 'linear'
337
+ x_min, x_max: float Limits for the displayed X values.
338
+ y_min, y_max: float Limits for the displayed Y values.
339
+ lc: ColorDefinition Give the line color.
340
+ bg: ColorDefinition Give the background color.
341
+ color_mode: ColorMode Specify color input mode; 'names' (default), 'byte' or
342
+ 'rgb' see plotille.color.__docs__
343
+ origin: bool Whether to print the origin. default: True
344
+ marker: str Instead of braille dots set a marker char for actual
345
+ values.
346
+
347
+ Returns:
348
+ str: plot over `X`, `Y`.
349
+ """
350
+ fig = Figure()
351
+ fig.width = width
352
+ fig.height = height
353
+ fig.x_label = X_label
354
+ fig.y_label = Y_label
355
+ fig.linesep = linesep
356
+ fig.origin = origin
357
+ if x_min is not None:
358
+ fig.set_x_limits(min_=x_min)
359
+ if x_max is not None:
360
+ fig.set_x_limits(max_=x_max)
361
+ if y_min is not None:
362
+ fig.set_y_limits(min_=y_min)
363
+ if y_max is not None:
364
+ fig.set_y_limits(max_=y_max)
365
+ fig.background = bg
366
+ fig.color_mode = color_mode
367
+
368
+ if lc is None and bg is None:
369
+ fig.with_colors = False
370
+
371
+ fig.plot(X, Y, lc, interp, marker=marker)
372
+
373
+ return fig.show()