Scales¶
Axis scales: the data-space → transformed-space mapping that sits between raw
data and the device transform. Pass a name or an instance to
Axes.set(xscale=..., yscale=...). See the
scales & ticks guide.
scales
¶
Axis scales: the data-space -> transformed-space mapping that sits between raw data and the device transform.
A Scale owns three things an axis needs: a monotonic transform (and
its inverse) used to position data, a tick locator+formatter (ticks),
and an optional set of minor_ticks. The figure draws marks by composing the
scale transform with an affine device map, so the Rust fast paths stay valid:
they only ever see an affine map over transformed space (see
Axes._draw/_draw_mark in pyplotrs._figure).
LinearScale is the default and is bit-for-bit identical to the previous
linear-only behavior (its transform is the identity and its ticks defer
to the Rust nice_ticks locator). Nonlinear scales (log, symlog, ...) override
transform/inverse/ticks and set is_identity = False so the figure
knows to pre-transform mark coordinates before the affine fast path.
Scale
¶
Base axis scale. Subclasses override transform/inverse/ticks.
is_identity is a fast-path flag: when True the figure may skip the
per-point transform pass entirely (the device map is already affine in
data space), preserving the Rust polyline/marker fast paths. code names
the transform for the Rust fast paths (add_line_xform/
add_markers_xform), which apply it per point in Rust — see
apply_scale in crates/pyplotrs-py/src/lib.rs.
fixed_range
¶
A view range this scale pins regardless of the data, or None.
Only a categorical axis has one: its window is a property of the category list, not of the values plotted against it.
Source code in python/pyplotrs/scales.py
clip_bounds
¶
Restrict raw (lo, hi) data bounds to this scale's domain.
Returns unpadded bounds in data space, or None when the data holds
nothing this scale can show. arrays is every contributed coordinate
array, for the domains that can't be decided from the bounds alone (a
log axis needs the smallest positive value, which the overall minimum
does not give it).
Source code in python/pyplotrs/scales.py
empty_range
¶
ticks
¶
LogScale
¶
Bases: Scale
Base-10 logarithmic scale. Non-positive data is dropped (becomes a gap), matching matplotlib; the per-point transform runs in Rust.
SymlogScale
¶
Bases: Scale
Symmetric log: linear within [-linthresh, linthresh] and logarithmic
beyond, so zero and negative values are representable.
linthresh is where the axis stops being linear. It has to match the
data: the default of 1.0 is right for a signal measured in ones and wrong
for one measured in microvolts, where it puts the entire dataset inside the
linear region and produces a plain linear axis wearing a symlog label.
Setting it is the whole point of the scale, so it is a constructor
argument::
ax.set(yscale=pp.scales.SymlogScale(linthresh=1e-6))
The threshold travels to the Rust fast paths inside the scale code
("symlog:1e-06"), which is what lets the per-point transform stay in
Rust while still being this axes' transform rather than a global constant.
Source code in python/pyplotrs/scales.py
ticks
¶
Decades outside the linear region, plus the threshold and zero.
The old version emitted decades and nothing else, so a view like
[-3, 3] at the default threshold got ticks at only -1, 0 and 1 -
every one of them inside the middle fifth of the axis, with the outer
80% carrying no label at all. The threshold itself was never marked
either, so nothing on the axis told a reader where the scale stops
being logarithmic. Both are fixed here: ±linthresh is always a tick
when it is in view, and an axis too narrow to hold two decades falls
back to plain nice numbers, which is what such an axis actually is.
Source code in python/pyplotrs/scales.py
minor_ticks
¶
The 2..9 subdivisions of each decade, as on a log axis.
A symlog axis had none at all, so between two labeled decades there was no way to read off where a point sat - the one thing minor ticks are for on a logarithmic axis.
Source code in python/pyplotrs/scales.py
CategoricalScale
¶
Bases: Scale
A discrete axis: string categories occupy integer positions 0..n-1.
Data are mapped to their category index before plotting (see
Axes._categorize); the transform is the identity in that index space, so
the Rust affine fast paths are untouched (code = "linear"). Ticks are one
per category, centered on its position; the view spans -0.5 .. n-0.5.
Source code in python/pyplotrs/scales.py
DateScale
¶
Bases: Scale
A time axis over float day numbers (date2num, days since
1970-01-01). Datetime inputs are converted on the way in; ticks fall on
calendar boundaries (year/month/day/hour) chosen from the visible span.
get
¶
Resolve scale (a Scale, a name string, or None) to a
concrete Scale. None/"linear" -> LinearScale.
Source code in python/pyplotrs/scales.py
nice_ticks
¶
The Rust "nice numbers" auto-locator, signed for display.
Every caller of the locator goes through here rather than
_core.nice_ticks: the Rust side formats with an ASCII hyphen (it is a
pure function with no view of the display setting) and this is where that
becomes a real MINUS. Doing it before the labels
reach the layout engine keeps the pre-measured extents honest - a minus is
nearly twice the width of a hyphen.
Source code in python/pyplotrs/scales.py
date2num
¶
Convert a datetime-like value to float days since 1970-01-01.
Source code in python/pyplotrs/scales.py
num2date
¶
is_datetime_like
¶
Whether v is a datetime we can place on a DateScale
(datetime/date, pandas Timestamp, or NumPy datetime64).