Keywords: Matplotlib | FixedFormatter | FixedLocator | Axis Formatting | Python Data Visualization
Abstract: This article provides a comprehensive examination of the 'FixedFormatter should only be used together with FixedLocator' warning that emerged after recent Matplotlib updates. By analyzing changes in the axis formatting mechanism, it explains the collaborative workflow between FixedFormatter and FixedLocator in detail. Three practical solutions are presented: using the set_ticks method, combining with the FixedLocator class, and employing the alternative tick_params method. The article includes complete code examples and visual comparisons to help developers understand how to safely customize tick label formats without altering tick positions.
Problem Background and Warning Analysis
In recent Matplotlib updates, many developers encountered the following warning when customizing axis tick labels using set_xticklabels() or set_yticklabels() methods:
UserWarning: FixedFormatter should only be used together with FixedLocator
ax.set_yticklabels([label_format.format(x) for x in ax.get_yticks().tolist()])
This warning is not an error but a check mechanism introduced by the Matplotlib development team to enhance code robustness. The fundamental issue is that when developers directly set tick labels without corresponding fixed locators, it may cause mismatches between labels and tick positions if the axis range changes.
Core Concept Explanation
To understand this warning, two key Matplotlib components must be understood:
- FixedFormatter: A fixed formatter that converts numerical values to specific label text formats. When
set_xticklabels()orset_yticklabels()is called, Matplotlib internally creates a FixedFormatter instance. - FixedLocator: A fixed locator that determines exact tick positions on the axis. It ensures tick positions remain unchanged even when the axis range is modified.
The core logic of the warning is: when using FixedFormatter, it must be paired with FixedLocator; otherwise, if the axis range changes, fixed-format labels may not correctly correspond to new tick positions.
Solution 1: Using the set_ticks Method
The simplest solution is to explicitly set tick positions before setting labels:
# Get current tick positions
current_ticks = ax.get_yticks().tolist()
# First set tick positions
ax.set_yticks(current_ticks)
# Then set label format
ax.set_yticklabels([label_format.format(x) for x in current_ticks])
This approach uses set_yticks() to explicitly set tick positions, which implicitly employs FixedLocator and thus eliminates the warning. However, note that this changes the tick positioning behavior—tick positions will no longer adjust automatically with axis range changes.
Solution 2: Combining with FixedLocator Class
A more standardized solution is to explicitly use FixedLocator, which is the recommended practice by Matplotlib:
import matplotlib.ticker as mticker
# Get current tick positions
ticks_loc = ax.get_yticks().tolist()
# Set fixed locator
ax.yaxis.set_major_locator(mticker.FixedLocator(ticks_loc))
# Set label format
ax.set_yticklabels([label_format.format(x) for x in ticks_loc])
This method clearly expresses the developer's intent: using fixed tick positions with fixed label formats. It maintains code clarity and aligns with Matplotlib's design philosophy.
Complete Example and Visual Comparison
The following code demonstrates a comparison of three different approaches:
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}'
# Control group: no processing
# ax1 maintains default behavior
# Method 1: Using set_yticks
ax2.set_yticks(ax1.get_yticks().tolist())
ax2.set_yticklabels([label_format.format(x) for x in ax1.get_yticks().tolist()])
# Method 2: Using FixedLocator
ax3.yaxis.set_major_locator(mticker.FixedLocator(ax1.get_yticks().tolist()))
ax3.set_yticklabels([label_format.format(x) for x in ax1.get_yticks().tolist()])
fig.tight_layout()
plt.show()
Visual comparison reveals that all three methods produce identical chart appearances, but the latter two avoid generating warning messages.
Alternative Solution: Using tick_params Method
For specific formatting needs like rotating tick labels, the tick_params() method can serve as an alternative:
# Not recommended (generates warning)
ax.set_xticklabels(ax.get_xticklabels(), rotation=45)
# Recommended approach
ax.tick_params(axis='x', labelrotation=45)
This method is suitable for simple label property adjustments and avoids potential issues from directly manipulating label arrays.
Version Compatibility and Downgrade Options
If warning messages interfere with development workflow, consider temporarily downgrading Matplotlib to version 3.2.2:
# Using conda package manager
conda install matplotlib=3.2.2
# Using pip package manager
pip install matplotlib==3.2.2
However, note that downgrading is only a temporary solution. In the long term, developers should adapt to new API changes. The Matplotlib team introduced this warning to prevent common misuse patterns and enhance code reliability.
Best Practice Recommendations
- Always consider using FixedLocator when customizing tick labels to explicitly define tick positions
- For simple label property adjustments, prioritize the
tick_params()method - In team projects, standardize warning handling approaches to avoid mixing different solutions
- Regularly update Matplotlib knowledge and stay informed about API changes and best practices
By understanding the collaborative mechanism between FixedFormatter and FixedLocator, developers can write more robust and maintainable visualization code, fully leveraging Matplotlib's powerful capabilities.