添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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

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]))

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

    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.

    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.

    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?

    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.

    VongCanhChi, FanchenBao, mahdiaslanimk, wenyuan-wu, wilswer, Shelly-Lee, BitnaKeum, L-Marriott, M4thinking, Sheshadri-1992, and mandresyandri reacted with thumbs up emoji IcyW and jorgeUnas reacted with laugh emoji All reactions

    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..

    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
              

    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.

    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)
    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()])
              

    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()))
              

    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.

    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()])
              

    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()])
    * 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]>
    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
    See: matplotlib/matplotlib#18848
    Co-authored-by: Ben Williams <[email protected]>
    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
    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