scales-python 1.4.0.9000__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.
- scales/__init__.py +295 -0
- scales/_colors.py +272 -0
- scales/_palettes_data.py +595 -0
- scales/_utils.py +579 -0
- scales/bounds.py +512 -0
- scales/breaks.py +627 -0
- scales/breaks_log.py +268 -0
- scales/colour_manip.py +681 -0
- scales/colour_mapping.py +593 -0
- scales/colour_ramp.py +126 -0
- scales/labels.py +2144 -0
- scales/minor_breaks.py +197 -0
- scales/palettes.py +1328 -0
- scales/py.typed +0 -0
- scales/range.py +223 -0
- scales/scale_continuous.py +146 -0
- scales/scale_discrete.py +196 -0
- scales/transforms.py +1338 -0
- scales_python-1.4.0.9000.dist-info/METADATA +73 -0
- scales_python-1.4.0.9000.dist-info/RECORD +22 -0
- scales_python-1.4.0.9000.dist-info/WHEEL +4 -0
- scales_python-1.4.0.9000.dist-info/licenses/LICENSE +3 -0
scales/_utils.py
ADDED
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for the scales package.
|
|
3
|
+
|
|
4
|
+
Python port of utility functions from the R scales package
|
|
5
|
+
(https://github.com/r-lib/scales). Corresponds primarily to:
|
|
6
|
+
- R/utils.R
|
|
7
|
+
- R/range.R
|
|
8
|
+
- R/full-seq.R
|
|
9
|
+
- R/round.R
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import sys
|
|
15
|
+
from datetime import timedelta
|
|
16
|
+
from typing import Any, Callable, Optional, Sequence, Union
|
|
17
|
+
|
|
18
|
+
import numpy as np
|
|
19
|
+
from numpy.typing import ArrayLike
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"zero_range",
|
|
23
|
+
"expand_range",
|
|
24
|
+
"rescale_common",
|
|
25
|
+
"recycle_common",
|
|
26
|
+
"fullseq",
|
|
27
|
+
"round_any",
|
|
28
|
+
"offset_by",
|
|
29
|
+
"precision",
|
|
30
|
+
"demo_continuous",
|
|
31
|
+
"demo_log10",
|
|
32
|
+
"demo_discrete",
|
|
33
|
+
"demo_datetime",
|
|
34
|
+
"demo_time",
|
|
35
|
+
"demo_timespan",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def zero_range(
|
|
40
|
+
x: ArrayLike, tol: float = 1000 * sys.float_info.epsilon
|
|
41
|
+
) -> bool:
|
|
42
|
+
"""
|
|
43
|
+
Check if a range has (effectively) zero extent.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
x : array-like of length 2
|
|
48
|
+
A ``[min, max]`` pair describing a range.
|
|
49
|
+
tol : float, optional
|
|
50
|
+
Tolerance for comparison, by default ``1000 * sys.float_info.epsilon``.
|
|
51
|
+
The range is considered zero when
|
|
52
|
+
``abs(max - min) < tol * abs(mean(min, max))``.
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
bool
|
|
57
|
+
``True`` if the range is effectively zero, ``False`` otherwise.
|
|
58
|
+
|
|
59
|
+
Notes
|
|
60
|
+
-----
|
|
61
|
+
* If either element is ``None`` / ``np.nan``, returns ``True``
|
|
62
|
+
(consistent with R's ``NA`` handling).
|
|
63
|
+
* If both elements are infinite with the same sign, returns ``False``.
|
|
64
|
+
* If elements are infinite with different signs, returns ``False``.
|
|
65
|
+
"""
|
|
66
|
+
x = np.asarray(x, dtype=float)
|
|
67
|
+
if x.shape != (2,):
|
|
68
|
+
raise ValueError("x must have exactly 2 elements")
|
|
69
|
+
|
|
70
|
+
# R returns NA for NaN inputs (which causes an error in `if`).
|
|
71
|
+
# In Python, returning True would incorrectly treat NaN ranges as
|
|
72
|
+
# zero-width. Return False so downstream code takes the safe path.
|
|
73
|
+
if np.any(np.isnan(x)):
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
# Both the same (including both +Inf or both -Inf)
|
|
77
|
+
if x[0] == x[1]:
|
|
78
|
+
return True
|
|
79
|
+
|
|
80
|
+
# Mixed infinities → definitely not zero range
|
|
81
|
+
if np.any(np.isinf(x)):
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
diff = np.abs(x[1] - x[0])
|
|
85
|
+
mean = np.abs(x[0] + x[1]) / 2.0
|
|
86
|
+
|
|
87
|
+
if mean == 0.0:
|
|
88
|
+
# Avoid 0/0; compare diff to tol directly
|
|
89
|
+
return diff < tol
|
|
90
|
+
|
|
91
|
+
return (diff / mean) < tol
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def expand_range(
|
|
95
|
+
range: ArrayLike,
|
|
96
|
+
mul: float = 0,
|
|
97
|
+
add: float = 0,
|
|
98
|
+
zero_width: float = 1,
|
|
99
|
+
) -> tuple[float, float]:
|
|
100
|
+
"""
|
|
101
|
+
Expand a numeric range by multiplicative and additive amounts.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
range : array-like of length 2
|
|
106
|
+
``[min, max]`` of the data range.
|
|
107
|
+
mul : float, optional
|
|
108
|
+
Multiplicative expansion factor (default 0). The range is expanded
|
|
109
|
+
outward by ``mul * (max - min)`` on each side.
|
|
110
|
+
add : float, optional
|
|
111
|
+
Additive expansion amount (default 0). Added to each side of the
|
|
112
|
+
range after multiplicative expansion.
|
|
113
|
+
zero_width : float, optional
|
|
114
|
+
If the range has zero width (see :func:`zero_range`), expand by
|
|
115
|
+
this amount instead (default 1). Half is subtracted from the
|
|
116
|
+
minimum and half is added to the maximum.
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
tuple of float
|
|
121
|
+
``(new_min, new_max)`` after expansion.
|
|
122
|
+
"""
|
|
123
|
+
range = np.asarray(range, dtype=float)
|
|
124
|
+
if range.shape != (2,):
|
|
125
|
+
raise ValueError("range must have exactly 2 elements")
|
|
126
|
+
|
|
127
|
+
# Matches R exactly:
|
|
128
|
+
# width <- if (zero_range(range)) zero_width else diff(range)
|
|
129
|
+
# range + c(-1, 1) * (width * mul + add)
|
|
130
|
+
# So with the defaults (mul=0, add=0) a zero-range input returns
|
|
131
|
+
# unchanged, and the `zero_width` only participates via `mul`.
|
|
132
|
+
width = zero_width if zero_range(range) else range[1] - range[0]
|
|
133
|
+
delta = width * mul + add
|
|
134
|
+
return (float(range[0] - delta), float(range[1] + delta))
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def rescale_common(
|
|
138
|
+
x: ArrayLike,
|
|
139
|
+
to: tuple[float, float],
|
|
140
|
+
from_range: tuple[float, float],
|
|
141
|
+
) -> np.ndarray:
|
|
142
|
+
"""
|
|
143
|
+
Linearly rescale *x* from ``from_range`` into ``to``.
|
|
144
|
+
|
|
145
|
+
Parameters
|
|
146
|
+
----------
|
|
147
|
+
x : array-like
|
|
148
|
+
Numeric values to rescale.
|
|
149
|
+
to : tuple of float
|
|
150
|
+
``(new_min, new_max)`` target range.
|
|
151
|
+
from_range : tuple of float
|
|
152
|
+
``(old_min, old_max)`` source range.
|
|
153
|
+
|
|
154
|
+
Returns
|
|
155
|
+
-------
|
|
156
|
+
numpy.ndarray
|
|
157
|
+
Rescaled values.
|
|
158
|
+
"""
|
|
159
|
+
x = np.asarray(x, dtype=float)
|
|
160
|
+
from_range = np.asarray(from_range, dtype=float)
|
|
161
|
+
to = np.asarray(to, dtype=float)
|
|
162
|
+
|
|
163
|
+
if zero_range(from_range):
|
|
164
|
+
return np.full_like(x, (to[0] + to[1]) / 2.0)
|
|
165
|
+
|
|
166
|
+
return (x - from_range[0]) / (from_range[1] - from_range[0]) * (
|
|
167
|
+
to[1] - to[0]
|
|
168
|
+
) + to[0]
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def recycle_common(
|
|
172
|
+
*args: ArrayLike, size: Optional[int] = None
|
|
173
|
+
) -> list[np.ndarray]:
|
|
174
|
+
"""
|
|
175
|
+
Recycle arrays to a common length.
|
|
176
|
+
|
|
177
|
+
Each argument must be either length 1 (scalar) or length ``size``.
|
|
178
|
+
Scalars are broadcast to length ``size``. If *size* is ``None`` it is
|
|
179
|
+
inferred as the length of the longest non-scalar argument.
|
|
180
|
+
|
|
181
|
+
Parameters
|
|
182
|
+
----------
|
|
183
|
+
*args : array-like
|
|
184
|
+
One or more arrays to recycle.
|
|
185
|
+
size : int, optional
|
|
186
|
+
Target length. Inferred from the arguments when ``None``.
|
|
187
|
+
|
|
188
|
+
Returns
|
|
189
|
+
-------
|
|
190
|
+
list of numpy.ndarray
|
|
191
|
+
Recycled arrays, all of length *size*.
|
|
192
|
+
|
|
193
|
+
Raises
|
|
194
|
+
------
|
|
195
|
+
ValueError
|
|
196
|
+
If arguments have incompatible lengths (not 1 and not *size*).
|
|
197
|
+
"""
|
|
198
|
+
arrays = [np.atleast_1d(np.asarray(a)) for a in args]
|
|
199
|
+
|
|
200
|
+
if size is None:
|
|
201
|
+
lengths = [len(a) for a in arrays]
|
|
202
|
+
non_scalar = [l for l in lengths if l != 1]
|
|
203
|
+
if not non_scalar:
|
|
204
|
+
size = 1
|
|
205
|
+
else:
|
|
206
|
+
size = max(non_scalar)
|
|
207
|
+
|
|
208
|
+
result: list[np.ndarray] = []
|
|
209
|
+
for i, a in enumerate(arrays):
|
|
210
|
+
n = len(a)
|
|
211
|
+
if n == size:
|
|
212
|
+
result.append(a)
|
|
213
|
+
elif n == 1:
|
|
214
|
+
result.append(np.repeat(a, size))
|
|
215
|
+
else:
|
|
216
|
+
raise ValueError(
|
|
217
|
+
f"Argument {i} has length {n}, which is not 1 or {size}"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
return result
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def fullseq(
|
|
224
|
+
range: ArrayLike,
|
|
225
|
+
size: float,
|
|
226
|
+
pad: bool = False,
|
|
227
|
+
) -> np.ndarray:
|
|
228
|
+
"""
|
|
229
|
+
Generate a sequence of fixed-size intervals that covers *range*.
|
|
230
|
+
|
|
231
|
+
Parameters
|
|
232
|
+
----------
|
|
233
|
+
range : array-like of length 2
|
|
234
|
+
``[min, max]`` of the data range.
|
|
235
|
+
size : float
|
|
236
|
+
Step size for the sequence.
|
|
237
|
+
pad : bool, optional
|
|
238
|
+
If ``True``, extend the sequence by one *size* on each side
|
|
239
|
+
(default ``False``).
|
|
240
|
+
|
|
241
|
+
Returns
|
|
242
|
+
-------
|
|
243
|
+
numpy.ndarray
|
|
244
|
+
A regular numeric sequence from (at most) ``min`` to (at least)
|
|
245
|
+
``max``, with spacing *size*.
|
|
246
|
+
"""
|
|
247
|
+
range = np.asarray(range, dtype=float)
|
|
248
|
+
if range.shape != (2,):
|
|
249
|
+
raise ValueError("range must have exactly 2 elements")
|
|
250
|
+
|
|
251
|
+
if not np.isfinite(size) or size <= 0:
|
|
252
|
+
raise ValueError("size must be a positive finite number")
|
|
253
|
+
|
|
254
|
+
lo = np.floor(range[0] / size) * size
|
|
255
|
+
hi = np.ceil(range[1] / size) * size
|
|
256
|
+
|
|
257
|
+
if pad:
|
|
258
|
+
lo -= size
|
|
259
|
+
hi += size
|
|
260
|
+
|
|
261
|
+
# Use round_any to avoid floating-point fuzz at boundaries
|
|
262
|
+
return np.arange(lo, hi + size / 2, size)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def round_any(
|
|
266
|
+
x: ArrayLike,
|
|
267
|
+
accuracy: float,
|
|
268
|
+
f: Callable = np.round,
|
|
269
|
+
) -> np.ndarray:
|
|
270
|
+
"""
|
|
271
|
+
Round values to the nearest multiple of *accuracy*.
|
|
272
|
+
|
|
273
|
+
Parameters
|
|
274
|
+
----------
|
|
275
|
+
x : array-like
|
|
276
|
+
Numeric values to round.
|
|
277
|
+
accuracy : float
|
|
278
|
+
Rounding unit; values are rounded to the nearest multiple of
|
|
279
|
+
this number.
|
|
280
|
+
f : callable, optional
|
|
281
|
+
Rounding function, by default :func:`numpy.round`. Other useful
|
|
282
|
+
choices include :func:`numpy.floor` and :func:`numpy.ceil`.
|
|
283
|
+
|
|
284
|
+
Returns
|
|
285
|
+
-------
|
|
286
|
+
numpy.ndarray
|
|
287
|
+
Rounded values.
|
|
288
|
+
"""
|
|
289
|
+
x = np.asarray(x, dtype=float)
|
|
290
|
+
return f(x / accuracy) * accuracy
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def offset_by(
|
|
294
|
+
x: Union[float, np.datetime64, Any],
|
|
295
|
+
size: Union[float, timedelta, np.timedelta64, Any],
|
|
296
|
+
) -> Any:
|
|
297
|
+
"""
|
|
298
|
+
Offset a value by *size*.
|
|
299
|
+
|
|
300
|
+
For plain numerics, this is simple addition. For datetime-like objects
|
|
301
|
+
the *size* should be an appropriate timedelta.
|
|
302
|
+
|
|
303
|
+
Parameters
|
|
304
|
+
----------
|
|
305
|
+
x : float or datetime-like
|
|
306
|
+
Starting value.
|
|
307
|
+
size : float, timedelta, or numpy.timedelta64
|
|
308
|
+
Amount to offset by.
|
|
309
|
+
|
|
310
|
+
Returns
|
|
311
|
+
-------
|
|
312
|
+
float or datetime-like
|
|
313
|
+
``x + size``.
|
|
314
|
+
"""
|
|
315
|
+
return x + size
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def precision(x: ArrayLike) -> float:
|
|
319
|
+
"""
|
|
320
|
+
Detect the precision of a numeric vector.
|
|
321
|
+
|
|
322
|
+
The precision is the smallest power of 10 that captures the spacing
|
|
323
|
+
between unique, finite, non-NaN values.
|
|
324
|
+
|
|
325
|
+
Parameters
|
|
326
|
+
----------
|
|
327
|
+
x : array-like
|
|
328
|
+
Numeric values.
|
|
329
|
+
|
|
330
|
+
Returns
|
|
331
|
+
-------
|
|
332
|
+
float
|
|
333
|
+
Precision as a power of 10 (e.g. ``0.01`` for data with two
|
|
334
|
+
decimal places of resolution).
|
|
335
|
+
|
|
336
|
+
Notes
|
|
337
|
+
-----
|
|
338
|
+
If *x* has fewer than 2 unique finite values, returns ``1``.
|
|
339
|
+
"""
|
|
340
|
+
x = np.asarray(x, dtype=float)
|
|
341
|
+
x = x[np.isfinite(x)]
|
|
342
|
+
x = np.unique(x)
|
|
343
|
+
|
|
344
|
+
if len(x) <= 1:
|
|
345
|
+
return 1.0
|
|
346
|
+
|
|
347
|
+
diffs = np.diff(np.sort(x))
|
|
348
|
+
diffs = diffs[diffs > 0]
|
|
349
|
+
|
|
350
|
+
if len(diffs) == 0:
|
|
351
|
+
return 1.0
|
|
352
|
+
|
|
353
|
+
smallest = np.min(diffs)
|
|
354
|
+
|
|
355
|
+
if smallest == 0:
|
|
356
|
+
return 1.0
|
|
357
|
+
|
|
358
|
+
# Smallest power of 10 <= smallest diff.
|
|
359
|
+
# Round the log10 to avoid floating-point fuzz (e.g. log10(0.1) ≈ -1.0000000000000004).
|
|
360
|
+
log_val = np.log10(smallest)
|
|
361
|
+
rounded = np.round(log_val)
|
|
362
|
+
if np.abs(log_val - rounded) < 1e-6:
|
|
363
|
+
log_val = rounded
|
|
364
|
+
return float(10 ** np.floor(log_val))
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
# ---------------------------------------------------------------------------
|
|
368
|
+
# Demo functions (R source: utils.R)
|
|
369
|
+
# ---------------------------------------------------------------------------
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def demo_continuous(
|
|
373
|
+
x: ArrayLike,
|
|
374
|
+
*,
|
|
375
|
+
labels: object = None,
|
|
376
|
+
breaks: object = None,
|
|
377
|
+
trans: object = None,
|
|
378
|
+
**kwargs: object,
|
|
379
|
+
) -> None:
|
|
380
|
+
"""Show a continuous scale demo using matplotlib.
|
|
381
|
+
|
|
382
|
+
Creates a simple plot that demonstrates how breaks and labels
|
|
383
|
+
render for the supplied data range. The R version delegates to
|
|
384
|
+
ggplot2; this Python version uses matplotlib.
|
|
385
|
+
|
|
386
|
+
Parameters
|
|
387
|
+
----------
|
|
388
|
+
x : array-like
|
|
389
|
+
Data range (used as x-limits).
|
|
390
|
+
labels : callable or None
|
|
391
|
+
Label formatter (e.g. ``label_comma()``).
|
|
392
|
+
breaks : callable or None
|
|
393
|
+
Break generator (e.g. ``breaks_extended(n=5)``).
|
|
394
|
+
trans : Transform or None
|
|
395
|
+
Transformation object.
|
|
396
|
+
**kwargs
|
|
397
|
+
Ignored (accepted for forward-compatibility).
|
|
398
|
+
"""
|
|
399
|
+
import matplotlib.pyplot as plt
|
|
400
|
+
|
|
401
|
+
x = np.asarray(x, dtype=float)
|
|
402
|
+
xmin, xmax = float(np.nanmin(x)), float(np.nanmax(x))
|
|
403
|
+
|
|
404
|
+
fig, ax = plt.subplots(figsize=(6, 1))
|
|
405
|
+
|
|
406
|
+
# Generate breaks
|
|
407
|
+
if breaks is not None:
|
|
408
|
+
ticks = np.asarray(breaks(x))
|
|
409
|
+
else:
|
|
410
|
+
ticks = np.linspace(xmin, xmax, 6)
|
|
411
|
+
|
|
412
|
+
# Generate labels
|
|
413
|
+
if labels is not None:
|
|
414
|
+
tick_labels = labels(ticks)
|
|
415
|
+
else:
|
|
416
|
+
tick_labels = [str(v) for v in ticks]
|
|
417
|
+
|
|
418
|
+
ax.set_xlim(xmin, xmax)
|
|
419
|
+
ax.set_xticks(ticks)
|
|
420
|
+
ax.set_xticklabels(tick_labels)
|
|
421
|
+
ax.set_yticks([])
|
|
422
|
+
ax.set_title("Continuous scale demo")
|
|
423
|
+
plt.tight_layout()
|
|
424
|
+
plt.show()
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def demo_log10(
|
|
428
|
+
x: ArrayLike,
|
|
429
|
+
*,
|
|
430
|
+
labels: object = None,
|
|
431
|
+
breaks: object = None,
|
|
432
|
+
**kwargs: object,
|
|
433
|
+
) -> None:
|
|
434
|
+
"""Show a log-10 scale demo using matplotlib.
|
|
435
|
+
|
|
436
|
+
Parameters
|
|
437
|
+
----------
|
|
438
|
+
x : array-like
|
|
439
|
+
Data range.
|
|
440
|
+
labels : callable or None
|
|
441
|
+
Label formatter.
|
|
442
|
+
breaks : callable or None
|
|
443
|
+
Break generator.
|
|
444
|
+
**kwargs
|
|
445
|
+
Ignored.
|
|
446
|
+
"""
|
|
447
|
+
import matplotlib.pyplot as plt
|
|
448
|
+
|
|
449
|
+
x = np.asarray(x, dtype=float)
|
|
450
|
+
xmin, xmax = float(np.nanmin(x)), float(np.nanmax(x))
|
|
451
|
+
|
|
452
|
+
fig, ax = plt.subplots(figsize=(6, 1))
|
|
453
|
+
ax.set_xscale("log")
|
|
454
|
+
ax.set_xlim(max(xmin, 1e-10), xmax)
|
|
455
|
+
|
|
456
|
+
if breaks is not None:
|
|
457
|
+
ticks = np.asarray(breaks(x))
|
|
458
|
+
ax.set_xticks(ticks)
|
|
459
|
+
if labels is not None:
|
|
460
|
+
ax.set_xticklabels(labels(ax.get_xticks()))
|
|
461
|
+
|
|
462
|
+
ax.set_yticks([])
|
|
463
|
+
ax.set_title("Log-10 scale demo")
|
|
464
|
+
plt.tight_layout()
|
|
465
|
+
plt.show()
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def demo_discrete(
|
|
469
|
+
x: ArrayLike,
|
|
470
|
+
*,
|
|
471
|
+
labels: object = None,
|
|
472
|
+
**kwargs: object,
|
|
473
|
+
) -> None:
|
|
474
|
+
"""Show a discrete scale demo using matplotlib.
|
|
475
|
+
|
|
476
|
+
Parameters
|
|
477
|
+
----------
|
|
478
|
+
x : array-like
|
|
479
|
+
Categorical values.
|
|
480
|
+
labels : callable or None
|
|
481
|
+
Label formatter.
|
|
482
|
+
**kwargs
|
|
483
|
+
Ignored.
|
|
484
|
+
"""
|
|
485
|
+
import matplotlib.pyplot as plt
|
|
486
|
+
|
|
487
|
+
x = list(x) if not isinstance(x, (list, np.ndarray)) else list(x)
|
|
488
|
+
positions = list(range(len(x)))
|
|
489
|
+
|
|
490
|
+
fig, ax = plt.subplots(figsize=(6, 1))
|
|
491
|
+
ax.set_xlim(-0.5, len(x) - 0.5)
|
|
492
|
+
ax.set_xticks(positions)
|
|
493
|
+
|
|
494
|
+
if labels is not None:
|
|
495
|
+
tick_labels = labels(x)
|
|
496
|
+
else:
|
|
497
|
+
tick_labels = [str(v) for v in x]
|
|
498
|
+
|
|
499
|
+
ax.set_xticklabels(tick_labels)
|
|
500
|
+
ax.set_yticks([])
|
|
501
|
+
ax.set_title("Discrete scale demo")
|
|
502
|
+
plt.tight_layout()
|
|
503
|
+
plt.show()
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def demo_datetime(
|
|
507
|
+
x: ArrayLike,
|
|
508
|
+
*,
|
|
509
|
+
labels: object = None,
|
|
510
|
+
breaks: object = None,
|
|
511
|
+
**kwargs: object,
|
|
512
|
+
) -> None:
|
|
513
|
+
"""Show a datetime scale demo using matplotlib.
|
|
514
|
+
|
|
515
|
+
Parameters
|
|
516
|
+
----------
|
|
517
|
+
x : array-like
|
|
518
|
+
Datetime values.
|
|
519
|
+
labels : callable or None
|
|
520
|
+
Label formatter.
|
|
521
|
+
breaks : callable or None
|
|
522
|
+
Break generator.
|
|
523
|
+
**kwargs
|
|
524
|
+
Ignored.
|
|
525
|
+
"""
|
|
526
|
+
import matplotlib.pyplot as plt
|
|
527
|
+
|
|
528
|
+
fig, ax = plt.subplots(figsize=(8, 2), layout="constrained")
|
|
529
|
+
ax.set_xlim(min(x), max(x))
|
|
530
|
+
fig.autofmt_xdate()
|
|
531
|
+
ax.set_yticks([])
|
|
532
|
+
ax.set_title("Datetime scale demo")
|
|
533
|
+
plt.show()
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
def demo_time(
|
|
537
|
+
x: ArrayLike,
|
|
538
|
+
*,
|
|
539
|
+
labels: object = None,
|
|
540
|
+
breaks: object = None,
|
|
541
|
+
**kwargs: object,
|
|
542
|
+
) -> None:
|
|
543
|
+
"""Show a time scale demo using matplotlib.
|
|
544
|
+
|
|
545
|
+
Parameters
|
|
546
|
+
----------
|
|
547
|
+
x : array-like
|
|
548
|
+
Time/numeric values representing seconds.
|
|
549
|
+
labels : callable or None
|
|
550
|
+
Label formatter.
|
|
551
|
+
breaks : callable or None
|
|
552
|
+
Break generator.
|
|
553
|
+
**kwargs
|
|
554
|
+
Ignored.
|
|
555
|
+
"""
|
|
556
|
+
demo_continuous(x, labels=labels, breaks=breaks, **kwargs)
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
def demo_timespan(
|
|
560
|
+
x: ArrayLike,
|
|
561
|
+
*,
|
|
562
|
+
labels: object = None,
|
|
563
|
+
breaks: object = None,
|
|
564
|
+
**kwargs: object,
|
|
565
|
+
) -> None:
|
|
566
|
+
"""Show a timespan scale demo using matplotlib.
|
|
567
|
+
|
|
568
|
+
Parameters
|
|
569
|
+
----------
|
|
570
|
+
x : array-like
|
|
571
|
+
Timespan data (numeric seconds).
|
|
572
|
+
labels : callable or None
|
|
573
|
+
Label formatter.
|
|
574
|
+
breaks : callable or None
|
|
575
|
+
Break generator.
|
|
576
|
+
**kwargs
|
|
577
|
+
Ignored.
|
|
578
|
+
"""
|
|
579
|
+
demo_continuous(x, labels=labels, breaks=breaks, **kwargs)
|