ticker.EngFormatter: allow offset by doronbehar · Pull Request #28495 · matplotlib/matplotlib · GitHub
Skip to content
Merged
13 changes: 13 additions & 0 deletions doc/users/next_whats_new/engformatter_offset.rst
4 changes: 4 additions & 0 deletions doc/users/next_whats_new/update_features.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Miscellaneous Changes
---------------------

- The `matplotlib.ticker.ScalarFormatter` class has gained a new instantiating parameter ``usetex``.
33 changes: 33 additions & 0 deletions galleries/examples/ticks/engformatter_offset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""
===================================================
SI prefixed offsets and natural order of magnitudes
===================================================

`matplotlib.ticker.EngFormatter` is capable of computing a natural
offset for your axis data, and presenting it with a standard SI prefix
automatically calculated.

Below is an examples of such a plot:

"""

import matplotlib.pyplot as plt
import numpy as np

import matplotlib.ticker as mticker

# Fixing random state for reproducibility
np.random.seed(19680801)
Comment thread
doronbehar marked this conversation as resolved.

UNIT = "Hz"

fig, ax = plt.subplots()
ax.yaxis.set_major_formatter(mticker.EngFormatter(
useOffset=True,
unit=UNIT
))
size = 100
measurement = np.full(size, 1e9)
noise = np.random.uniform(low=-2e3, high=2e3, size=size)
ax.plot(measurement + noise)
plt.show()
67 changes: 67 additions & 0 deletions lib/matplotlib/tests/test_ticker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1591,6 +1591,73 @@ def test_engformatter_usetex_useMathText():
assert x_tick_label_text == ['$0$', '$500$', '$1$ k']


@pytest.mark.parametrize(
'data_offset, noise, oom_center_desired, oom_noise_desired', [
(271_490_000_000.0, 10, 9, 0),
(27_149_000_000_000.0, 10_000_000, 12, 6),
(27.149, 0.01, 0, -3),
(2_714.9, 0.01, 3, -3),
(271_490.0, 0.001, 3, -3),
(271.49, 0.001, 0, -3),
# The following sets of parameters demonstrates that when
# oom(data_offset)-1 and oom(noise)-2 equal a standard 3*N oom, we get
# that oom_noise_desired < oom(noise)
(27_149_000_000.0, 100, 9, +3),
(27.149, 1e-07, 0, -6),
(271.49, 0.0001, 0, -3),
(27.149, 0.0001, 0, -3),
# Tests where oom(data_offset) <= oom(noise), those are probably
# covered by the part where formatter.offset != 0
(27_149.0, 10_000, 0, 3),
(27.149, 10_000, 0, 3),
(27.149, 1_000, 0, 3),
(27.149, 100, 0, 0),
(27.149, 10, 0, 0),
]
)
def test_engformatter_offset_oom(
data_offset,
noise,
oom_center_desired,
oom_noise_desired
):
UNIT = "eV"
fig, ax = plt.subplots()
ydata = data_offset + np.arange(-5, 7, dtype=float)*noise
ax.plot(ydata)
formatter = mticker.EngFormatter(useOffset=True, unit=UNIT)
# So that offset strings will always have the same size
formatter.ENG_PREFIXES[0] = "_"
ax.yaxis.set_major_formatter(formatter)
fig.canvas.draw()
offset_got = formatter.get_offset()
ticks_got = [labl.get_text() for labl in ax.get_yticklabels()]
# Predicting whether offset should be 0 or not is essentially testing
# ScalarFormatter._compute_offset . This function is pretty complex and it
# would be nice to test it, but this is out of scope for this test which
# only makes sure that offset text and the ticks gets the correct unit
# prefixes and the ticks.
if formatter.offset:
prefix_noise_got = offset_got[2]
prefix_noise_desired = formatter.ENG_PREFIXES[oom_noise_desired]
prefix_center_got = offset_got[-1-len(UNIT)]
prefix_center_desired = formatter.ENG_PREFIXES[oom_center_desired]
assert prefix_noise_desired == prefix_noise_got
assert prefix_center_desired == prefix_center_got
# Make sure the ticks didn't get the UNIT
for tick in ticks_got:
assert UNIT not in tick
else:
assert oom_center_desired == 0
assert offset_got == ""
# Make sure the ticks contain now the prefixes
for tick in ticks_got:
# 0 is zero on all orders of magnitudes, no matter what is
# oom_noise_desired
prefix_idx = 0 if tick[0] == "0" else oom_noise_desired
assert tick.endswith(formatter.ENG_PREFIXES[prefix_idx] + UNIT)


class TestPercentFormatter:
percent_data = [
# Check explicitly set decimals over different intervals and values
Expand Down
154 changes: 109 additions & 45 deletions lib/matplotlib/ticker.py
Loading