'UserWarning: FixedFormatter should only be used together with FixedLocator
I have used for a long time small subroutines to format axes of charts I'm plotting. A couple of examples:
def format_y_label_thousands(): # format y-axis tick labels formats
ax = plt.gca()
label_format = '{:,.0f}'
ax.set_yticklabels([label_format.format(x) for x in ax.get_yticks().tolist()])
def format_y_label_percent(): # format y-axis tick labels formats
ax = plt.gca()
label_format = '{:.1%}'
ax.set_yticklabels([label_format.format(x) for x in ax.get_yticks().tolist()])
However, after an update to matplotlib yesterday, I get the following warning when calling any of these two functions:
UserWarning: FixedFormatter should only be used together with FixedLocator
ax.set_yticklabels([label_format.format(x) for x in ax.get_yticks().tolist()])
What is the reason for such a warning? I couldn't figure it out looking into matplotlib's documentation.
Solution 1:[1]
WORKAROUND:
The way to avoid the warning is to use FixedLocator (that is part of matplotlib.ticker). Below I show a code to plot three charts. I format their axes in different ways. Note that the "set_ticks" silence the warning, but it changes the actual ticks locations/labels (it took me some time to figure out that FixedLocator uses the same info but keeps the ticks locations intact). You can play with the x/y's to see how each solution might affect the output.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as mticker
mpl.rcParams['font.size'] = 6.5
x = np.array(range(1000, 5000, 500))
y = 37*x
fig, [ax1, ax2, ax3] = plt.subplots(1,3)
ax1.plot(x,y, linewidth=5, color='green')
ax2.plot(x,y, linewidth=5, color='red')
ax3.plot(x,y, linewidth=5, color='blue')
label_format = '{:,.0f}'
# nothing done to ax1 as it is a "control chart."
# fixing yticks with "set_yticks"
ticks_loc = ax2.get_yticks().tolist()
ax2.set_yticks(ax1.get_yticks().tolist())
ax2.set_yticklabels([label_format.format(x) for x in ticks_loc])
# fixing yticks with matplotlib.ticker "FixedLocator"
ticks_loc = ax3.get_yticks().tolist()
ax3.yaxis.set_major_locator(mticker.FixedLocator(ticks_loc))
ax3.set_yticklabels([label_format.format(x) for x in ticks_loc])
# fixing xticks with FixedLocator but also using MaxNLocator to avoid cramped x-labels
ax3.xaxis.set_major_locator(mticker.MaxNLocator(3))
ticks_loc = ax3.get_xticks().tolist()
ax3.xaxis.set_major_locator(mticker.FixedLocator(ticks_loc))
ax3.set_xticklabels([label_format.format(x) for x in ticks_loc])
fig.tight_layout()
plt.show()
OUTPUT CHARTS:
Obviously, having a couple of idle lines of code like the one above (I'm basically getting the yticks or xticks and setting them again) only adds noise to my program. I would prefer that the warning was removed. However, look into some of the "bug reports" (from links on the comments above/below; the issue is not actually a bug: it is an update that is generating some issues), and the contributors that manage matplotlib have their reasons to keep the warning.
OLDER VERSION OF MATPLOTLIB: If you use your Console to control critical outputs of your code (as I do), the warning messages might be problematic. Therefore, a way to delay having to deal with the issue is to downgrade matplotlib to version 3.2.2. I use Anaconda to manage my Python packages, and here is the command used to downgrade matplotlib:
conda install matplotlib=3.2.2
Not all listed versions might be available. For instance, couldn't install matplotlib 3.3.0 although it is listed on matplotlib's releases page: https://github.com/matplotlib/matplotlib/releases
Solution 2:[2]
If someone comes here using the function axes.xaxis.set_ticklabels()
(or yaxis equivalent), you don't need to use FixedLocator, you can avoid this warning using axes.xaxis.set_ticks(values_list)
BEFORE axes.xaxis.set_ticklabels(labels_list)
.
Solution 3:[3]
According to this matplotlib page
# FixedFormatter should only be used together with FixedLocator.
# Otherwise, one cannot be sure where the labels will end up.
This means one should do
positions = [0, 1, 2, 3, 4, 5]
labels = ['A', 'B', 'C', 'D', 'E', 'F']
ax.xaxis.set_major_locator(ticker.FixedLocator(positions))
ax.xaxis.set_major_formatter(ticker.FixedFormatter(labels))
But the issue also persisted with the ticker.LogLocator
even if the labels were passed to ticker.FixedFormatter
.
So the solution in this case was
Define a formatter function
# FuncFormatter can be used as a decorator @ticker.FuncFormatter def major_formatter(x, pos): return f'{x:.2f}'
and pass the formatter function to the FixedFormatter
ax.xaxis.set_major_locator(ticker.LogLocator(base=10, numticks=5)) ax.xaxis.set_major_formatter(major_formatter)
See the above link for details.
Solution 4:[4]
Simplest workaround is to suppress warnings (this includes UserWarning):
import warnings
warnings.filterwarnings("ignore")
The use-case would be if you don't want your jupyter notebook on github to look trashed with warning messages. Unlike most warnings, this warning keeps repeating if you're in a loop (python 3.7).
Solution 5:[5]
I had the same problem as I tried to rotate the tick labels on the X-axis with located date ticks:
ax.set_xticklabels(ax.get_xticklabels(), rotation=45)
ax.xaxis.set_major_locator(dates.DayLocator())
It worked out using the 'tick_params()' method:
ax.tick_params(axis='x', labelrotation = 45)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | dansarmo |
Solution 3 | Tom M. |
Solution 4 | |
Solution 5 | Rami |