Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix FixedFormatter #4302

Closed
wants to merge 1 commit into from
Closed

Conversation

mdboom
Copy link
Member

@mdboom mdboom commented Mar 31, 2015

In investigating #4301, I discovered that when you set explicit tick labels, the value display in the toolbar doesn't work. Strangely, it seems to have been this way at least since 1.1 (before that, I can't compile due to libpng API changes).

To reproduce:

import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

f, ax = plt.subplots(figsize=(9, 9))
plt.plot(np.arange(100))
plt.gca().set_xticklabels(['foo %s' % i for i in range(100)])
plt.show()

Looking at the FixedFormatter code, it seems that it's supposed to be keying the values from a kwarg pos, but I can't see anywhere in the code where pos is being passed...

Does this change make sense? I think probably not, but it's broken as it stands in any event...

Cc: @efiring

@WeatherGod
Copy link
Member

hmm, dug a bit deeper into the format_coord() method. There is a failback mechanism if calling fmt_xdata() raises a TypeError that uses the axis' major formatter. That might explain this a bit.

@mdboom
Copy link
Member Author

mdboom commented Mar 31, 2015

@WeatherGod: How so?

@WeatherGod
Copy link
Member

Because the default formatter is ScalarFormatter, but after explicitly setting the tick labels, it becomes a FixedFormatter:

>>> f, ax = plt.subplots()
>>> import numpy as np
>>> plt.plot(np.arange(100))
[<matplotlib.lines.Line2D object at 0x2cee250>]
>>> ax.xaxis.get_major_formatter()
<matplotlib.ticker.ScalarFormatter object at 0x2cdf490>
>>> ax.set_xticklabels(['foo %s' % i for i in range(100)])
[<matplotlib.text.Text object at 0x2cd9990>, <matplotlib.text.Text object at 0x2cdf5d0>, <matplotlib.text.Text object at 0x2ceee10>, <matplotlib.text.Text object at 0x2cf3590>, <matplotlib.text.Text object at 0x2cf3cd0>, <matplotlib.text.Text object at 0x2cf6450>]
>>> ax.xaxis.get_major_formatter()
<matplotlib.ticker.FixedFormatter object at 0x2cee410>
>>> 

@WeatherGod
Copy link
Member

FixedFormatter.format_data_short() falls back to Formatter.format_data_short(), which utilizes __call__(). For a FixedFormatter, __call__() is defined as:

    def __call__(self, x, pos=None):
        'Return the format for tick val *x* at position *pos*'
        if pos is None or pos >= len(self.seq):
            return ''
        else:
            return self.seq[pos]

So, the position is assumed to be an index into the sequence, but the data coordinate system isn't necessarially set up that way, I think.

@WeatherGod
Copy link
Member

ah, it is even easier than that! pos is None!

return ''
else:
return self.seq[pos]
return self.seq[int(np.round(x)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax error. Missing parens.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks the use case where you do

from matplotlib.ticker import FixedLocator, FixedFormatter

fig, ax = plt.subplots()
ax.xaxis.set_major_locator(FixedLocator([10, 20, 30]))
ax.xaxis.set_major_formatter(FixedFormatter(['a', 'b', 'c']))
ax.set_xlim([5, 35])

@WeatherGod
Copy link
Member

ah, and I took a look at your changes and I see you arrived at the same spot in the code in a different way. So, what was "pos" ever for?

@tacaswell
Copy link
Member

I came across this a while ago and thought it was a feature not a bug ;). If you are using a fixed formatter, then you have de-coupled the tick labels from the data so there is no sensible way we can guess at what the values between the ticks are.

I think pos is passed is as part of the call to the formatter from the Axis code.

@tacaswell
Copy link
Member

See https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/axis.py#L873 for where the formatter is called as part of the Axis draw process (which led me down the rabbit hole of how Axis objects draw them selves.... They seem to have a pool of Tick objects and then update and draw just enough of them. The Tick objects wrap up both tick labels, the tick mark, and the grid line.).

@mdboom
Copy link
Member Author

mdboom commented Apr 1, 2015

Ah, of course. If the labels are not 1-to-1 with values this isn't going to work. Thanks.

@mdboom mdboom deleted the fixed-formatter-fix branch November 10, 2015 02:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants