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 8d33af7

Browse filesBrowse files
authored
Merge pull request #17266 from efiring/set_ticklabels
FIX: Keep explicit ticklabels in sync with ticks from FixedLocator
2 parents b5d4a6c + 3eb471d commit 8d33af7
Copy full SHA for 8d33af7

File tree

Expand file treeCollapse file tree

7 files changed

+80
-14
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+80
-14
lines changed

‎doc/api/api_changes_3.3/behaviour.rst

Copy file name to clipboardExpand all lines: doc/api/api_changes_3.3/behaviour.rst
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ did nothing, when passed an unsupported value. It now raises a ``ValueError``.
3737
`.pyplot.tick_params`) used to accept any value for ``which`` and silently
3838
did nothing, when passed an unsupported value. It now raises a ``ValueError``.
3939

40+
``Axis.set_ticklabels()`` must match ``FixedLocator.locs``
41+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42+
If an axis is using a `.ticker.FixedLocator`, typically set by a call to
43+
`.Axis.set_ticks`, then the number of ticklabels supplied must match the
44+
number of locations available (``FixedFormattor.locs``). If not, a
45+
``ValueError`` is raised.
46+
4047
``backend_pgf.LatexManager.latex``
4148
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4249
``backend_pgf.LatexManager.latex`` is now created with ``encoding="utf-8"``, so

‎doc/api/axis_api.rst

Copy file name to clipboardExpand all lines: doc/api/axis_api.rst
+7-4Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -228,17 +228,20 @@ Other
228228
Discouraged
229229
-----------
230230

231-
These methods implicitly use `~matplotlib.ticker.FixedLocator` and
232-
`~matplotlib.ticker.FixedFormatter`. They can be convenient, but if
233-
not used together may de-couple your tick labels from your data.
231+
These methods should be used together with care, calling ``set_ticks``
232+
to specify the desired tick locations **before** calling ``set_ticklabels`` to
233+
specify a matching series of labels. Calling ``set_ticks`` makes a
234+
`~matplotlib.ticker.FixedLocator`; it's list of locations is then used by
235+
``set_ticklabels`` to make an appropriate
236+
`~matplotlib.ticker.FuncFormatter`.
234237

235238
.. autosummary::
236239
:toctree: _as_gen
237240
:template: autosummary.rst
238241
:nosignatures:
239242

240-
Axis.set_ticklabels
241243
Axis.set_ticks
244+
Axis.set_ticklabels
242245

243246

244247

‎examples/images_contours_and_fields/image_annotated_heatmap.py

Copy file name to clipboardExpand all lines: examples/images_contours_and_fields/image_annotated_heatmap.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ def annotate_heatmap(im, data=None, valfmt="{x:.2f}",
288288
# the diagonal elements (which are all 1) by using a
289289
# `matplotlib.ticker.FuncFormatter`.
290290

291-
corr_matrix = np.corrcoef(np.random.rand(6, 5))
291+
corr_matrix = np.corrcoef(harvest)
292292
im, _ = heatmap(corr_matrix, vegetables, vegetables, ax=ax4,
293293
cmap="PuOr", vmin=-1, vmax=1,
294294
cbarlabel="correlation coeff.")

‎lib/matplotlib/axes/_base.py

Copy file name to clipboardExpand all lines: lib/matplotlib/axes/_base.py
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3010,6 +3010,7 @@ def locator_params(self, axis='both', tight=None, **kwargs):
30103010
self.yaxis.get_major_locator().set_params(**kwargs)
30113011
self._request_autoscale_view(tight=tight,
30123012
scalex=update_x, scaley=update_y)
3013+
self.stale = True
30133014

30143015
def tick_params(self, axis='both', **kwargs):
30153016
"""

‎lib/matplotlib/axis.py

Copy file name to clipboardExpand all lines: lib/matplotlib/axis.py
+34-7Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
import datetime
6+
import functools
67
import logging
78

89
import numpy as np
@@ -1646,6 +1647,11 @@ def set_pickradius(self, pickradius):
16461647
"""
16471648
self.pickradius = pickradius
16481649

1650+
# Helper for set_ticklabels. Defining it here makes it pickleable.
1651+
@staticmethod
1652+
def _format_with_dict(tickd, x, pos):
1653+
return tickd.get(x, "")
1654+
16491655
def set_ticklabels(self, ticklabels, *, minor=False, **kwargs):
16501656
r"""
16511657
Set the text values of the tick labels.
@@ -1658,8 +1664,9 @@ def set_ticklabels(self, ticklabels, *, minor=False, **kwargs):
16581664
Parameters
16591665
----------
16601666
ticklabels : sequence of str or of `.Text`\s
1661-
List of texts for tick labels; must include values for non-visible
1662-
labels.
1667+
Texts for labeling each tick location in the sequence set by
1668+
`.Axis.set_ticks`; the number of labels must match the number of
1669+
locations.
16631670
minor : bool
16641671
If True, set minor ticks instead of major ticks.
16651672
**kwargs
@@ -1673,14 +1680,34 @@ def set_ticklabels(self, ticklabels, *, minor=False, **kwargs):
16731680
"""
16741681
ticklabels = [t.get_text() if hasattr(t, 'get_text') else t
16751682
for t in ticklabels]
1683+
locator = (self.get_minor_locator() if minor
1684+
else self.get_major_locator())
1685+
if isinstance(locator, mticker.FixedLocator):
1686+
if len(locator.locs) != len(ticklabels):
1687+
raise ValueError(
1688+
"The number of FixedLocator locations"
1689+
f" ({len(locator.locs)}), usually from a call to"
1690+
" set_ticks, does not match"
1691+
f" the number of ticklabels ({len(ticklabels)}).")
1692+
tickd = {loc: lab for loc, lab in zip(locator.locs, ticklabels)}
1693+
func = functools.partial(self._format_with_dict, tickd)
1694+
formatter = mticker.FuncFormatter(func)
1695+
else:
1696+
formatter = mticker.FixedFormatter(ticklabels)
1697+
16761698
if minor:
1677-
self.set_minor_formatter(mticker.FixedFormatter(ticklabels))
1678-
ticks = self.get_minor_ticks()
1699+
self.set_minor_formatter(formatter)
1700+
locs = self.get_minorticklocs()
1701+
ticks = self.get_minor_ticks(len(locs))
16791702
else:
1680-
self.set_major_formatter(mticker.FixedFormatter(ticklabels))
1681-
ticks = self.get_major_ticks()
1703+
self.set_major_formatter(formatter)
1704+
locs = self.get_majorticklocs()
1705+
ticks = self.get_major_ticks(len(locs))
1706+
16821707
ret = []
1683-
for tick_label, tick in zip(ticklabels, ticks):
1708+
for pos, (loc, tick) in enumerate(zip(locs, ticks)):
1709+
tick.update_position(loc)
1710+
tick_label = formatter(loc, pos)
16841711
# deal with label1
16851712
tick.label1.set_text(tick_label)
16861713
tick.label1.update(kwargs)

‎lib/matplotlib/tests/test_axes.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_axes.py
+22-2Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4497,8 +4497,8 @@ def test_set_get_ticklabels():
44974497
# set ticklabel to 1 plot in normal way
44984498
ax[0].set_xticks(range(10))
44994499
ax[0].set_yticks(range(10))
4500-
ax[0].set_xticklabels(['a', 'b', 'c', 'd'])
4501-
ax[0].set_yticklabels(['11', '12', '13', '14'])
4500+
ax[0].set_xticklabels(['a', 'b', 'c', 'd'] + 6 * [''])
4501+
ax[0].set_yticklabels(['11', '12', '13', '14'] + 6 * [''])
45024502

45034503
# set ticklabel to the other plot, expect the 2 plots have same label
45044504
# setting pass get_ticklabels return value as ticklabels argument
@@ -4508,6 +4508,26 @@ def test_set_get_ticklabels():
45084508
ax[1].set_yticklabels(ax[0].get_yticklabels())
45094509

45104510

4511+
def test_subsampled_ticklabels():
4512+
# test issue 11937
4513+
fig, ax = plt.subplots()
4514+
ax.plot(np.arange(10))
4515+
ax.xaxis.set_ticks(np.arange(10) + 0.1)
4516+
ax.locator_params(nbins=5)
4517+
ax.xaxis.set_ticklabels([c for c in "bcdefghijk"])
4518+
plt.draw()
4519+
labels = [t.get_text() for t in ax.xaxis.get_ticklabels()]
4520+
assert labels == ['b', 'd', 'f', 'h', 'j']
4521+
4522+
4523+
def test_mismatched_ticklabels():
4524+
fig, ax = plt.subplots()
4525+
ax.plot(np.arange(10))
4526+
ax.xaxis.set_ticks([1.5, 2.5])
4527+
with pytest.raises(ValueError):
4528+
ax.xaxis.set_ticklabels(['a', 'b', 'c'])
4529+
4530+
45114531
@image_comparison(['retain_tick_visibility.png'])
45124532
def test_retain_tick_visibility():
45134533
fig, ax = plt.subplots()

‎lib/matplotlib/ticker.py

Copy file name to clipboardExpand all lines: lib/matplotlib/ticker.py
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,10 @@ class FuncFormatter(Formatter):
387387
position ``pos``), and return a string containing the corresponding
388388
tick label.
389389
"""
390+
390391
def __init__(self, func):
391392
self.func = func
393+
self.offset_string = ""
392394

393395
def __call__(self, x, pos=None):
394396
"""
@@ -398,6 +400,12 @@ def __call__(self, x, pos=None):
398400
"""
399401
return self.func(x, pos)
400402

403+
def get_offset(self):
404+
return self.offset_string
405+
406+
def set_offset_string(self, ofs):
407+
self.offset_string = ofs
408+
401409

402410
class FormatStrFormatter(Formatter):
403411
"""

0 commit comments

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