Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Setting xticklabels causes warning related to FixedFormatter #18848

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

Closed
igurin-invn opened this issue Oct 30, 2020 · 24 comments · Fixed by #20047
Closed

Setting xticklabels causes warning related to FixedFormatter #18848

igurin-invn opened this issue Oct 30, 2020 · 24 comments · Fixed by #20047

Comments

@igurin-invn
Copy link
Contributor

igurin-invn commented Oct 30, 2020

Bug report

Bug summary

I get a warning related to FixedFormatter even though I am not setting a formatter.

Thanks to user Rational-IM on StackOverflow and the good folks at pandas for isolating and reproducing the bug.

Code for reproduction

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.scatter(0.5, 0.5)
ax.set_xticklabels(['0', r'$T/2$', r'$T$'])

# Optional line
ax.xaxis.set_major_locator(FixedLocator([0, 0.5, 1]))

None

Actual outcome

<ipython-input-3-52a3699e4278>:5: UserWarning: FixedFormatter should only be used together with FixedLocator
  ax.set_xticklabels(['0', r'$T/2$', r'$T$'])

Expected outcome

Works on 3.2.2 with no warning.

Matplotlib version

  • Operating system: Win 10
  • Matplotlib version: 3.3.2
  • Matplotlib backend: module://ipykernel.pylab.backend_inline
  • Python version: 3.8.5
  • Jupyter version (if applicable): 6.1.4
  • Other libraries: none

Installed from conda

@jklymak
Copy link
Member

jklymak commented Oct 30, 2020

That's on purpose. You should also explicitly specify the three ticks to go with the label and then this warning should go away.

OTOH I agree the warning is pretty mysterious if u are just using set_xticklabels.

@igurin-invn
Copy link
Contributor Author

I see. It would be good to fix the message.

Also, If you do set the tick locations, you have to do it first (I revised my code sample to show this). Yes, it's cleaner to set the locations first and then the labels, but I don't think it should be a warning. As long as the locator and labels have both been set by the time the plot is drawn, it should be OK.

@jklymak
Copy link
Member

jklymak commented Oct 30, 2020

The problem is when people zoom or otherwise change the view, and we continue to use an AutoLocator, the labels will often end up applied to the wrong ticks. By encouraging you to manually specify the ticks we help you avoid that possibility.

@timhoffm
Copy link
Member

Possibly adding a warning a

if isinstance(locator, mticker.FixedLocator):

makes sense.

@Rational-IM
Copy link

Rational-IM commented Nov 1, 2020

Thanks, @jklymak: now I understand why the warning was included. However, I'm assuming there is a lot of code out there - the one I use the most on a day-to-day basis included - that only plots "fixed" charts. Therefore, having to add a line of code using FixedLocator (if what I want is just to format the existing labels), would make the conde unnecessarily polluted, no? Maybe the potential issue you describe should be included somewhere on matplotlib's documentation?

@jklymak
Copy link
Member

jklymak commented Nov 1, 2020

Its a warning that you are doing something poorly defined. If you want to suppress it either specify the ticks

ax.set_xticks([1, 2, 3])
ax.set_xticklabels(['$\pi$', '$2\pi$', '$3\pi$']) 

filter the Warning, or just ignore it.

@timhoffm
Copy link
Member

timhoffm commented Nov 1, 2020

Side note: I'm tempted to modify set_ticks()
from set_ticks(ticks, *, minor=False)
to set_ticks(ticks, *, labels=None, minor=False).

That would allow to set_ticks([1, 2, 3], labels=['$\pi$', '$2\pi$', '$3\pi$']) and would exactly prevent this problem. A bit hesitant because that'd break the symmetry between get/set. OTOH it does not make sense to set labels without controlling positions, and that's most of the time best done by setting both simultaneously.

Note also that pyplot.xticks() does something siimilar, though it fully merges set_ticks and set_ticklabels`; and I don't think we can mimic this on the Axes due to backward-compatibility (whether that'd be a good idea would need to be evaluated separately..

@hrr2020
Copy link

hrr2020 commented Apr 10, 2021

This worked fine in a similar situation:

ax.set_xticks(ax.get_xticks())  # just get and reset whatever you already have
ax.set_xticklabels(xtick_labels)  # set the new/modified labels

@anntzer
Copy link
Contributor

anntzer commented Apr 14, 2021

fwiw I agree with @timhoffm's proposal at #18848 (comment), although a variant (that I think I suggested somewhere else) may be to support ax.set_xticks({pos: label, pos: label, ...}).

@jklymak
Copy link
Member

jklymak commented Apr 14, 2021

I would find a dict hard to remember and assemble. But two matching lists seems fine to me...

@timhoffm
Copy link
Member

A single parameter would work relatively well also if you start with two lists, either as dict

set_xticks(dict(zip(positions, labels)))

and/or as list of tuples

set_xticks(zip(positions, labels))

My concern is however, that we basically don't have such compound parameters in our API and mostly use separate parameters.

@igurin-invn
Copy link
Contributor Author

I would probably assemble such a dict using a dict comprehension.

@timhoffm
Copy link
Member

timhoffm commented Apr 15, 2021

In case we should accept a dict, you are free to create that any way you want.

On a side node, note however, that for a dict comprehension {k: v for k, v in zip(positions, labels)}, you'll need to create the k, v pairs anyway using zip(...). dict(zip(...)) is shorter.

@anntzer
Copy link
Contributor

anntzer commented Apr 15, 2021

I'm not overly wedded to the dict idea either, but it's likely that the absence of such APIs in our codebase just reflects our matlab heritage.

@igurin-invn
Copy link
Contributor Author

@timhoffm , I disagree. {tick: f(tick) for tick in positions} would be very convenient.

Boo Matlab!

@jklymak
Copy link
Member

jklymak commented Apr 15, 2021

... but f(tick) is just a FuncFormatter. You would be better off setting that rather than the labels: https://matplotlib.org/stable/api/ticker_api.html#matplotlib.ticker.FuncFormatter (or StrFormatter) https://matplotlib.org/stable/gallery/ticks_and_spines/tick-formatters.html

@amueller
Copy link
Contributor

Hm can someone summarize the suggested solution after #20047?
Basically I want to do string formatting of the form ax.set_xticklabels([f(t) for t in ax.get_xticklabels()]). I tried using a FuncFormatter, but couldn't get it to work, presumably because I'm using pandas which does something under the hood.

Example:

import pandas as pd
from matplotlib.ticker import FuncFormatter

pd.Series({'a': 10, 'b': 20, 'c': 30}).plot.bar()
ax = plt.gca()
formatter = FuncFormatter(lambda x, pos: f"{x}, {pos}")
ax.xaxis.set_major_formatter(formatter)

image
I would have expected x to be a, b and c but it's not.

What I had before was something like

pd.Series({'a': 10, 'b': 20, 'c': 30}).plot.bar()
ax = plt.gca()
ax.set_xticklabels([f"{x.get_text()}_is_great" for x in ax.get_xticklabels()])

image

amueller added a commit to amueller/dabl that referenced this issue May 22, 2021
@QuLogic
Copy link
Member

QuLogic commented May 22, 2021

Those are categorical plots; the axis is not the categories, but 0, 1, 2, 3... which is transformed by StrCategoryFormatter into the correct labels. You will probably want to grab the existing formatter, wrap its result, and return that back. Something like:

import matplotlib.ticker as mticker


class SpecialFormatter(mticker.Formatter):
    def __init__(self, original_formatter):
        self.original_formatter = original_formatter

    def __call__(self, x, pos=None):
        x = self.original_formatter(x, pos=pos)
        return f"{x}, {pos}"


ax.xaxis.set_major_formatter(SpecialFormatter(ax.xaxis.get_major_formatter()))

@amueller
Copy link
Contributor

amueller commented May 22, 2021

Thanks, I figured that the category-indexes were transformed by a formatter.

I feel that requires a lot more knowledge of the internals than what I was doing before, and it's quite a common operation.
Basically if you want to apply a function to the ticks in an existing plot, this is what you have to do. Was that the suggested solution before as well?

Basically this is a DerivedFuncFormatter that allows me to apply a function to the existing formatter, so it's a bit more complicated than what @jklymak suggested above. For a library this works as a recipe, for a notebook it's not very convenient.

@jklymak
Copy link
Member

jklymak commented May 22, 2021

Sorry if I'm not following, what is wrong with the following? It seems to work fine with categoricals?

ax.set_xticklabels([f"{x.get_text()}_is_great" for x in ax.get_xticklabels()])

@Olshansk
Copy link

If you're like me and were simply trying to rotate the axis labels without any other changes, the following answer on stack overflow avoids the warning: https://stackoverflow.com/a/52461208/768439.

@tdpetrou
Copy link
Contributor

tdpetrou commented Dec 7, 2021

I ran into the same issue as @amueller - attempting to use FuncFormatter on tick labels that were internally stored as categories. You have to have deep knowledge of Matplotlib to understand what is going on as the labels appear as strings in the plot but are stored as numeric internally.

The easiest solution seems to be:

ax.set_xticklabels([my_func(x.get_text()) for x in ax.get_xticklabels()])

@jklymak
Copy link
Member

jklymak commented Dec 7, 2021

That seems orthogonal to this issue, and has always been the case - FuncFormatter always worked on the tick values, not the label text.

@story645
Copy link
Member

story645 commented Dec 7, 2021

FuncFormatter always worked on the tick values, not the label text.

The values for categorical should be the strings, but I can't think of a good way to do implement that given the internal mpl architecture needs it to be numeric.

PaulJonasJost added a commit to ICB-DCM/pyPESTO that referenced this issue Jan 11, 2022
* removed automatically reading the problem in each Optimize-/Profile-/Sample-reader. Set default argument for reading problem to false as the problem can in general not be stored completely.

* fix matplotlib warning. More on this here: matplotlib/matplotlib#18848

* fixed warning for missing burn in by performing geweke test within test pipeline.

* fixed warning for usage of distplot, as this will be removed in a future seaborn version.

* added probability as stat to normalize.

* added comment regarding axis argument error in seaborn. Not sure how to fix.

* changed `bw` to `bw_method`.

* changed `bw` to `bw_method`. Fixed a typehint.

* reworked FixedLocator warning.

* remove DeprecationWarning: Flags not at the start of the expression '^(?i)(ls_)'.

* fixed problem with MPI pool engine test.

* fixed problem in set_yticks. Missing parentheses.

* fixed ValueError in set_yticklabels.

* increased possible runtime on tests.

* fixed notebook `hdf5_storage.ipynb`.

* put timeout minutes back to 10min again

Co-authored-by: Yannik Schälte <[email protected]>
ndevenish pushed a commit to dials/dials that referenced this issue Jan 14, 2022
Directly setting axis labels raises a warning from matplotlib that
results may not be correct in every display scenario. We don't currently
hit those cases here, but this resolves the warnings in a more general
way.

See: matplotlib/matplotlib#18848

Co-authored-by: Ben Williams <[email protected]>
jboynyc added a commit to jboynyc/shifterator that referenced this issue Oct 21, 2022
matplotlib 3.3 and above gives a warning when calling `get_shift_graph`:

    UserWarning: FixedFormatter should only be used together with FixedLocator

This issue explains why the warning occurs and suggests setting xticks first before setting xticklabels: matplotlib/matplotlib#18848
jboynyc added a commit to jboynyc/shifterator that referenced this issue Feb 24, 2023
matplotlib 3.3 and above gives a warning when calling `get_shift_graph`:

    UserWarning: FixedFormatter should only be used together with FixedLocator

This issue explains why the warning occurs and suggests setting `xticks`
first before setting `xticklabels`:
matplotlib/matplotlib#18848
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 a pull request may close this issue.