Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 80dca54

Browse filesBrowse files
committed
ticker.EngFormatter: base upon ScalarFormatter
Allows us to use many order of magnitude and offset related routines from ScalarFormatter, and removes a bit usetex related duplicated code. Solves #28463.
1 parent f7a991e commit 80dca54
Copy full SHA for 80dca54

File tree

3 files changed

+98
-37
lines changed
Filter options

3 files changed

+98
-37
lines changed
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
ticker.EngFormatter now computes offset by default
2+
-------------------------------------------
3+
4+
`ticker.EngFormatter` was modified to act very similar to
5+
`ticker.ScalarFormatter`, such that it computes the best offset of the axis
6+
data, and shows the offset with the known SI quantity prefixes. To disable this
7+
new behavior, simply pass `useOffset=False` when you instantiate it. If offsets
8+
are disabled, or if there is no particular offset that fits your axis data, the
9+
formatter will reside to the old behavior.

‎lib/matplotlib/tests/test_ticker.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_ticker.py
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,6 +1594,18 @@ def test_engformatter_usetex_useMathText():
15941594
assert x_tick_label_text == ['$0$', '$500$', '$1$ k']
15951595

15961596

1597+
def test_engformatter_useOffset():
1598+
fig, ax = plt.subplots()
1599+
offset = int(1e7)
1600+
ydata = range(offset, offset+5)
1601+
ax.plot(ydata)
1602+
ax.set_yticks(ydata)
1603+
ax.yaxis.set_major_formatter(mticker.EngFormatter(useOffset=True, unit="Hz"))
1604+
fig.canvas.draw()
1605+
y_tick_label_text = [labl.get_text() for labl in ax.get_yticklabels()]
1606+
assert y_tick_label_text == (np.array(ydata)-offset).astype(str).tolist()
1607+
1608+
15971609
class TestPercentFormatter:
15981610
percent_data = [
15991611
# Check explicitly set decimals over different intervals and values

‎lib/matplotlib/ticker.py

Copy file name to clipboardExpand all lines: lib/matplotlib/ticker.py
+77-37Lines changed: 77 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,7 +1338,7 @@ def format_data_short(self, value):
13381338
return f"1-{1 - value:e}"
13391339

13401340

1341-
class EngFormatter(Formatter):
1341+
class EngFormatter(ScalarFormatter):
13421342
"""
13431343
Format axis values using engineering prefixes to represent powers
13441344
of 1000, plus a specified unit, e.g., 10 MHz instead of 1e7.
@@ -1370,7 +1370,7 @@ class EngFormatter(Formatter):
13701370
}
13711371

13721372
def __init__(self, unit="", places=None, sep=" ", *, usetex=None,
1373-
useMathText=None):
1373+
useMathText=None, useOffset=None):
13741374
r"""
13751375
Parameters
13761376
----------
@@ -1404,55 +1404,93 @@ def __init__(self, unit="", places=None, sep=" ", *, usetex=None,
14041404
useMathText : bool, default: :rc:`axes.formatter.use_mathtext`
14051405
To enable/disable the use mathtext for rendering the numbers in
14061406
the formatter.
1407+
useOffset : bool or float, default: :rc:`axes.formatter.useoffset`
1408+
Whether to use offset notation. See `.set_useOffset`.
14071409
"""
14081410
self.unit = unit
14091411
self.places = places
14101412
self.sep = sep
1411-
self.set_usetex(usetex)
1412-
self.set_useMathText(useMathText)
1413-
1414-
def get_usetex(self):
1415-
return self._usetex
1416-
1417-
def set_usetex(self, val):
1418-
if val is None:
1419-
self._usetex = mpl.rcParams['text.usetex']
1420-
else:
1421-
self._usetex = val
1422-
1423-
usetex = property(fget=get_usetex, fset=set_usetex)
1424-
1425-
def get_useMathText(self):
1426-
return self._useMathText
1413+
super().__init__(
1414+
useOffset=useOffset,
1415+
useMathText=useMathText,
1416+
useLocale=False,
1417+
usetex=usetex,
1418+
)
14271419

1428-
def set_useMathText(self, val):
1429-
if val is None:
1430-
self._useMathText = mpl.rcParams['axes.formatter.use_mathtext']
1420+
def __call__(self, x, pos=None):
1421+
"""
1422+
Return the format for tick value *x* at position *pos*. If there is no
1423+
currently offset in the data, it returns the best engineering formatting
1424+
that fits the given argument, independently.
1425+
"""
1426+
if len(self.locs) == 0 or self.offset == 0:
1427+
return self.fix_minus(self.format_data(x))
14311428
else:
1432-
self._useMathText = val
1429+
xp = (x - self.offset) / (10. ** self.orderOfMagnitude)
1430+
if abs(xp) < 1e-8:
1431+
xp = 0
1432+
return self._format_maybe_minus_and_locale(self.format, xp)
14331433

1434-
useMathText = property(fget=get_useMathText, fset=set_useMathText)
1434+
def set_locs(self, locs):
1435+
# docstring inherited
1436+
self.locs = locs
1437+
if len(self.locs) > 0:
1438+
if self._useOffset:
1439+
self._compute_offset()
1440+
self._set_order_of_magnitude()
1441+
# This is what's different from ScalarFormatter: We search among
1442+
# the engineers' standard orders of magnitudes (0, -3, 3, -6, 6,
1443+
# -9, 9 etc) the oom closest to our self.orderOfMagnitude. Then we
1444+
# set our self.orderOfMagnitude to it.
1445+
c = abs(self.orderOfMagnitude)
1446+
for sciOom in itertools.count(0, 3):
1447+
if c <= sciOom:
1448+
self.orderOfMagnitude = math.copysign(sciOom, self.orderOfMagnitude)
1449+
break
1450+
self._set_format()
14351451

1436-
def __call__(self, x, pos=None):
1437-
s = f"{self.format_eng(x)}{self.unit}"
1438-
# Remove the trailing separator when there is neither prefix nor unit
1439-
if self.sep and s.endswith(self.sep):
1440-
s = s[:-len(self.sep)]
1441-
return self.fix_minus(s)
1452+
# Simplify a bit ScalarFormatter.get_offset: We always want to use
1453+
# self.format_data. We insert here the surrounding $...$ here, if tex /
1454+
# mathtext is set.
1455+
def get_offset(self):
1456+
# docstring inherited
1457+
if len(self.locs) == 0:
1458+
return ''
1459+
if self.orderOfMagnitude or self.offset:
1460+
offsetStr = ''
1461+
sciNotStr = ''
1462+
if self.offset:
1463+
offsetStr = self.format_data(self.offset)
1464+
if self.offset > 0:
1465+
offsetStr = '+' + offsetStr
1466+
if self.orderOfMagnitude:
1467+
sciNotStr = self.format_data(10 ** self.orderOfMagnitude)
1468+
if self._useMathText or self._usetex:
1469+
if sciNotStr != '':
1470+
sciNotStr = r'\times%s' % sciNotStr
1471+
s = fr'${sciNotStr}{offsetStr}$'
1472+
else:
1473+
s = ''.join((sciNotStr, offsetStr))
1474+
return self.fix_minus(s)
1475+
return ''
14421476

14431477
def format_eng(self, num):
1478+
"""Alias to EngFormatter.format_data"""
1479+
return self.format_data(num)
1480+
1481+
def format_data(self, num):
14441482
"""
14451483
Format a number in engineering notation, appending a letter
14461484
representing the power of 1000 of the original number.
14471485
Some examples:
14481486
1449-
>>> format_eng(0) # for self.places = 0
1487+
>>> format_data(0) # for self.places = 0
14501488
'0'
14511489
1452-
>>> format_eng(1000000) # for self.places = 1
1490+
>>> format_data(1000000) # for self.places = 1
14531491
'1.0 M'
14541492
1455-
>>> format_eng(-1e-6) # for self.places = 2
1493+
>>> format_data(-1e-6) # for self.places = 2
14561494
'-1.00 \N{MICRO SIGN}'
14571495
"""
14581496
sign = 1
@@ -1482,13 +1520,15 @@ def format_eng(self, num):
14821520
mant /= 1000
14831521
pow10 += 3
14841522

1485-
prefix = self.ENG_PREFIXES[int(pow10)]
1523+
unitPrefix = self.ENG_PREFIXES[int(pow10)]
1524+
if self.unit or unitPrefix:
1525+
suffix = f"{self.sep}{unitPrefix}{self.unit}"
1526+
else:
1527+
suffix = ""
14861528
if self._usetex or self._useMathText:
1487-
formatted = f"${mant:{fmt}}${self.sep}{prefix}"
1529+
return rf"${mant:{fmt}}${suffix}"
14881530
else:
1489-
formatted = f"{mant:{fmt}}{self.sep}{prefix}"
1490-
1491-
return formatted
1531+
return rf"{mant:{fmt}}{suffix}"
14921532

14931533

14941534
class PercentFormatter(Formatter):

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.