Automatically create tick formatters for str and callable inputs. · matplotlib/matplotlib@b438cdd · GitHub
Skip to content

Commit b438cdd

Browse files
committed
Automatically create tick formatters for str and callable inputs.
1 parent 6113bbc commit b438cdd

6 files changed

Lines changed: 139 additions & 36 deletions

File tree

Lines changed: 7 additions & 0 deletions

examples/ticks_and_spines/tick-formatters.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def setup(ax, title):
3434
fontsize=14, fontname='Monospace', color='tab:blue')
3535

3636

37-
fig, axs = plt.subplots(7, 1, figsize=(8, 6))
37+
fig, axs = plt.subplots(9, 1, figsize=(8, 8))
3838

3939
# Null formatter
4040
setup(axs[0], title="NullFormatter()")
@@ -59,21 +59,30 @@ def major_formatter(x, pos):
5959
setup(axs[2], title='FuncFormatter(lambda x, pos: "[%.2f]" % x)')
6060
axs[2].xaxis.set_major_formatter(major_formatter)
6161

62-
# FormatStr formatter
63-
setup(axs[3], title="FormatStrFormatter('#%d')")
64-
axs[3].xaxis.set_major_formatter(ticker.FormatStrFormatter("#%d"))
62+
# FuncFormatter can be automatically created directly with a function input
63+
# without needing to manually create the formatter
64+
setup(axs[3], title="lambda x, pos: str(x-5)")
65+
axs[3].xaxis.set_major_formatter(lambda x, pos: str(x-5))
66+
67+
# StrMethod formatter
68+
setup(axs[4], title="StrMethodFormatter('{x:.3f}')")
69+
axs[4].xaxis.set_major_formatter(ticker.StrMethodFormatter("{x:.3f}"))
70+
71+
# StrMethodFormatter can also be created directly with a string input.
72+
setup(axs[5], title="'{x} km'")
73+
axs[5].xaxis.set_major_formatter('{x} km')
6574

6675
# Scalar formatter
67-
setup(axs[4], title="ScalarFormatter()")
68-
axs[4].xaxis.set_major_formatter(ticker.ScalarFormatter(useMathText=True))
76+
setup(axs[6], title="ScalarFormatter()")
77+
axs[6].xaxis.set_major_formatter(ticker.ScalarFormatter(useMathText=True))
6978

70-
# StrMethod formatter
71-
setup(axs[5], title="StrMethodFormatter('{x:.3f}')")
72-
axs[5].xaxis.set_major_formatter(ticker.StrMethodFormatter("{x:.3f}"))
79+
# FormatStr formatter
80+
setup(axs[7], title="FormatStrFormatter('#%d')")
81+
axs[7].xaxis.set_major_formatter(ticker.FormatStrFormatter("#%d"))
7382

7483
# Percent formatter
75-
setup(axs[6], title="PercentFormatter(xmax=5)")
76-
axs[6].xaxis.set_major_formatter(ticker.PercentFormatter(xmax=5))
84+
setup(axs[8], title="PercentFormatter(xmax=5)")
85+
axs[8].xaxis.set_major_formatter(ticker.PercentFormatter(xmax=5))
7786

7887
plt.tight_layout()
7988
plt.show()

lib/matplotlib/axis.py

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,37 +1529,93 @@ def set_major_formatter(self, formatter):
15291529
"""
15301530
Set the formatter of the major ticker.
15311531
1532+
In addition to a `~matplotlib.ticker.Formatter` instance,
1533+
this also accepts a `str` or function.
1534+
1535+
For a `str` a `~matplotlib.ticker.StrMethodFormatter` is used.
1536+
The field used for the value must be labeled *x* and the field used
1537+
for the position must be labeled *pos*.
1538+
1539+
For a function a `~matplotlib.ticker.FuncFormatter` is used.
1540+
The function should take in two inputs (a tick value ``x`` and a
1541+
position ``pos``), and return a string containing the corresponding
1542+
tick label.
1543+
15321544
Parameters
15331545
----------
1534-
formatter : `~matplotlib.ticker.Formatter`
1546+
formatter : `~matplotlib.ticker.Formatter`, str, or function
15351547
"""
1536-
cbook._check_isinstance(mticker.Formatter, formatter=formatter)
1537-
if (isinstance(formatter, mticker.FixedFormatter)
1538-
and len(formatter.seq) > 0
1539-
and not isinstance(self.major.locator, mticker.FixedLocator)):
1540-
cbook._warn_external('FixedFormatter should only be used together '
1541-
'with FixedLocator')
1542-
self.isDefault_majfmt = False
1543-
self.major.formatter = formatter
1544-
formatter.set_axis(self)
1545-
self.stale = True
1548+
self._set_formatter(formatter, self.major)
15461549

15471550
def set_minor_formatter(self, formatter):
15481551
"""
15491552
Set the formatter of the minor ticker.
15501553
1554+
In addition to a `~matplotlib.ticker.Formatter` instance,
1555+
this also accepts a `str` or function.
1556+
1557+
For a `str` a `~matplotlib.ticker.StrMethodFormatter` is used.
1558+
The field used for the tick value must be labeled *x* and the field
1559+
used for the tick position must be labeled *pos*.
1560+
See the `~matplotlib.ticker.StrMethodFormatter` documentation for
1561+
more information.
1562+
1563+
For a function a `~matplotlib.ticker.FuncFormatter` is used.
1564+
The function should take in two inputs (a tick value ``x`` and a
1565+
position ``pos``), and return a string containing the corresponding
1566+
tick label.
1567+
See the `~matplotlib.ticker.FuncFormatter` documentation for
1568+
more information.
1569+
15511570
Parameters
15521571
----------
1553-
formatter : `~matplotlib.ticker.Formatter`
1572+
formatter : `~matplotlib.ticker.Formatter`, str, or function
15541573
"""
1555-
cbook._check_isinstance(mticker.Formatter, formatter=formatter)
1574+
self._set_formatter(formatter, self.minor)
1575+
1576+
def _set_formatter(self, formatter, level):
1577+
"""
1578+
Set the formatter of the major or minor ticker.
1579+
1580+
In addition to a `~matplotlib.ticker.Formatter` instance,
1581+
this also accepts a `str` or function.
1582+
1583+
For a `str` a `~matplotlib.ticker.StrMethodFormatter` is used.
1584+
The field used for the value must be labeled *x* and the field used
1585+
for the position must be labeled *pos*.
1586+
1587+
For a function a `~matplotlib.ticker.FuncFormatter` is used.
1588+
The function should take in two inputs (a tick value ``x`` and a
1589+
position ``pos``), and return a string containing the corresponding
1590+
tick label.
1591+
1592+
Parameters
1593+
----------
1594+
formatter : `~matplotlib.ticker.Formatter`, str, or function
1595+
level : `self.major` or `self.minor`
1596+
"""
1597+
if isinstance(formatter, str):
1598+
formatter = mticker.StrMethodFormatter(formatter)
1599+
# Don't allow any other TickHelper to avoid easy-to-make errors,
1600+
# like using a Locator instead of a Formatter.
1601+
elif (callable(formatter) and
1602+
not isinstance(formatter, mticker.TickHelper)):
1603+
formatter = mticker.FuncFormatter(formatter)
1604+
else:
1605+
cbook._check_isinstance(mticker.Formatter, formatter=formatter)
1606+
15561607
if (isinstance(formatter, mticker.FixedFormatter)
15571608
and len(formatter.seq) > 0
1558-
and not isinstance(self.minor.locator, mticker.FixedLocator)):
1609+
and not isinstance(level.locator, mticker.FixedLocator)):
15591610
cbook._warn_external('FixedFormatter should only be used together '
15601611
'with FixedLocator')
1561-
self.isDefault_minfmt = False
1562-
self.minor.formatter = formatter
1612+
1613+
if level == self.major:
1614+
self.isDefault_majfmt = False
1615+
else:
1616+
self.isDefault_minfmt = False
1617+
1618+
level.formatter = formatter
15631619
formatter.set_axis(self)
15641620
self.stale = True
15651621

lib/matplotlib/tests/test_axes.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,28 @@ def test_formatter_ticker():
203203
ax.autoscale_view()
204204

205205

206+
def test_auto_formatter():
207+
def _formfunc(x, pos):
208+
return ''
209+
210+
formstr = '{x}_{pos}'
211+
212+
ax = plt.figure().subplots()
213+
ax.xaxis.set_major_formatter(_formfunc)
214+
ax.yaxis.set_minor_formatter(formstr)
215+
216+
targ_funcformatter = mticker.FuncFormatter(_formfunc)
217+
targ_strformatter = mticker.StrMethodFormatter(formstr)
218+
219+
assert isinstance(ax.xaxis.get_major_formatter(),
220+
mticker.FuncFormatter)
221+
assert isinstance(ax.yaxis.get_minor_formatter(),
222+
mticker.StrMethodFormatter)
223+
224+
assert ax.xaxis.get_major_formatter().func == targ_funcformatter.func
225+
assert ax.yaxis.get_minor_formatter().fmt == targ_strformatter.fmt
226+
227+
206228
@image_comparison(["twin_axis_locators_formatters"])
207229
def test_twin_axis_locators_formatters():
208230
vals = np.linspace(0, 1, num=5, endpoint=True)

lib/matplotlib/ticker.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@
159159
ax.yaxis.set_major_formatter(ymajor_formatter)
160160
ax.yaxis.set_minor_formatter(yminor_formatter)
161161
162+
For `.StrMethodFormatter`, `~.Axis.set_major_formatter` and
163+
`~.Axis.set_minor_formatter` accept a ``str``. In that case a
164+
`.StrMethodFormatter` will be automatically created and applied based on
165+
the given ``str``. Similalry, a `.FuncFormatter` will be automatically
166+
created and applied if a function is provided.
167+
162168
See :doc:`/gallery/ticks_and_spines/major_minor_demo` for an
163169
example of setting major and minor ticks. See the :mod:`matplotlib.dates`
164170
module for more information and examples of using date locators and formatters.
@@ -418,8 +424,8 @@ class StrMethodFormatter(Formatter):
418424
"""
419425
Use a new-style format string (as used by `str.format`) to format the tick.
420426
421-
The field used for the value must be labeled *x* and the field used
422-
for the position must be labeled *pos*.
427+
The field used for the tick value must be labeled *x* and the field used
428+
for the tick position must be labeled *pos*.
423429
"""
424430
def __init__(self, fmt):
425431
self.fmt = fmt

tutorials/text/text_intro.py

Lines changed: 10 additions & 7 deletions

0 commit comments

Comments
 (0)