Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions autoarray/plot/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1143,20 +1143,32 @@ def _inward_ticks(lo: float, hi: float, factor: float, n: int) -> np.ndarray:
)


def _round_ticks(values: np.ndarray, sig: int = 2) -> np.ndarray:
"""Round *values* to *sig* significant figures.
def _round_ticks(values: np.ndarray, sig: int = 2, max_sig: int = 10) -> np.ndarray:
"""Round *values* to *sig* significant figures, adding figures until distinct ticks stay distinct.

Two significant figures suit an extent that spans its own magnitude (e.g. -2" to 2"), but on a
narrow extent far from zero (a few milli-arcseconds at 1.2") every tick rounds to the same value
and the labels collapse to one repeated number. The significant figures are therefore raised, up
to *max_sig*, until the rounded ticks are as distinct as the input ticks.

After rounding, values smaller than 1e-10 of the overall tick scale are
clamped to zero so that floating-point noise (e.g. 1e-16 centre ticks on
symmetric extents) does not appear as scientific notation in labels.
"""
with np.errstate(divide="ignore", invalid="ignore"):
nonzero = np.where(values != 0, np.abs(values), 1.0)
mags = np.where(values != 0, 10 ** (sig - 1 - np.floor(np.log10(nonzero))), 1.0)
rounded = np.round(values * mags) / mags
scale = float(np.max(np.abs(rounded))) if len(rounded) > 0 else 1.0
if scale > 0:
rounded[np.abs(rounded) < scale * 1e-10] = 0.0
values = np.asarray(values, dtype=float)
n_distinct = len(np.unique(values))
for n_sig in range(sig, max(sig, max_sig) + 1):
with np.errstate(divide="ignore", invalid="ignore"):
nonzero = np.where(values != 0, np.abs(values), 1.0)
mags = np.where(
values != 0, 10 ** (n_sig - 1 - np.floor(np.log10(nonzero))), 1.0
)
rounded = np.round(values * mags) / mags
scale = float(np.max(np.abs(rounded))) if len(rounded) > 0 else 1.0
if scale > 0:
rounded[np.abs(rounded) < scale * 1e-10] = 0.0
if len(np.unique(rounded)) == n_distinct:
break
return rounded


Expand Down
32 changes: 32 additions & 0 deletions test_autoarray/plot/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,3 +619,35 @@ def test__plot_regions__single_polygon_region_is_labelled_once():
)
finally:
plt.close(figure)


def test__round_ticks__two_significant_figures_by_default():
from autoarray.plot.utils import _round_ticks

assert list(_round_ticks(np.array([-1.875, 0.0, 1.875]))) == [-1.9, 0.0, 1.9]


def test__round_ticks__adds_significant_figures_until_ticks_are_distinct():
from autoarray.plot.utils import _round_ticks

# A few milli-arcseconds at 1.2": two (and three) significant figures collapse every tick to 1.2.
ticks = _round_ticks(np.array([1.19655, 1.2, 1.20345]))

assert len(np.unique(ticks)) == 3
assert list(ticks) == [1.197, 1.2, 1.203]


def test__apply_extent__narrow_extent_far_from_zero_has_distinct_tick_labels():
import matplotlib.pyplot as plt

from autoarray.plot.utils import apply_extent

fig, ax = plt.subplots()
apply_extent(ax, [1.1954, 1.2046, -0.8046, -0.7954])

xlabels = [label.get_text() for label in ax.get_xticklabels()]
ylabels = [label.get_text() for label in ax.get_yticklabels()]
plt.close(fig)

assert len(set(xlabels)) == len(xlabels) == 3
assert len(set(ylabels)) == len(ylabels) == 3
Loading