J'ai remarqué que toutes les solutions postées ici qui utilisent des set_xticklabels()
ne préservent pas les compensation qui est un facteur d'échelle appliqué aux valeurs des ticks pour créer des étiquettes de ticks plus esthétiques. Par exemple, si les ticks sont de l'ordre de 0.00001 (1e-5), matplotlib ajoutera automatiquement un facteur d'échelle (ou offset
) de 1e-5
Les étiquettes de coche résultantes peuvent donc prendre la forme suivante 1 2 3 4
plutôt que 1e-5 2e-5 3e-5 4e-5
.
Voici un exemple :
En x
est np.array([1, 2, 3, 4])/1e6
et y
est y=x**2
. Il s'agit donc dans les deux cas de valeurs très faibles.
Colonne de gauche : changer manuellement les 1ère et 3ème étiquettes, comme suggéré par @Joe Kington. Notez que le décalage est perdu.
Milieu de colonne : similaire à ce que @iipr a suggéré, en utilisant un FuncFormatter
.
Colonne de droite : Ma proposition de solution pour préserver l'offset.
Figure ici :
Code complet ici :
import matplotlib.pyplot as plt
import numpy as np
# create some *small* data to plot
x = np.arange(5)/1e6
y = x**2
fig, axes = plt.subplots(1, 3, figsize=(10,6))
#------------------The set_xticklabels() solution------------------
ax1 = axes[0]
ax1.plot(x, y)
fig.canvas.draw()
labels = [item.get_text() for item in ax1.get_xticklabels()]
# Modify specific labels
labels[1] = 'Testing'
labels[3] = 'Testing2'
ax1.set_xticklabels(labels)
ax1.set_title('set_xticklabels()')
#--------------FuncFormatter solution--------------
import matplotlib.ticker as mticker
def update_ticks(x, pos):
if pos==1:
return 'testing'
elif pos==3:
return 'testing2'
else:
return x
ax2=axes[1]
ax2.plot(x,y)
ax2.xaxis.set_major_formatter(mticker.FuncFormatter(update_ticks))
ax2.set_title('Func Formatter')
#-------------------My solution-------------------
def changeLabels(axis, pos, newlabels):
'''Change specific x/y tick labels
Args:
axis (Axis): .xaxis or .yaxis obj.
pos (list): indices for labels to change.
newlabels (list): new labels corresponding to indices in <pos>.
'''
if len(pos) != len(newlabels):
raise Exception("Length of <pos> doesn't equal that of <newlabels>.")
ticks = axis.get_majorticklocs()
# get the default tick formatter
formatter = axis.get_major_formatter()
# format the ticks into strings
labels = formatter.format_ticks(ticks)
# Modify specific labels
for pii, lii in zip(pos, newlabels):
labels[pii] = lii
# Update the ticks and ticklabels. Order is important here.
# Need to first get the offset (1e-6 in this case):
offset = formatter.get_offset()
# Then set the modified labels:
axis.set_ticklabels(labels)
# In doing so, matplotlib creates a new FixedFormatter and sets it to the xaxis
# and the new FixedFormatter has no offset. So we need to query the
# formatter again and re-assign the offset:
axis.get_major_formatter().set_offset_string(offset)
return
ax3 = axes[2]
ax3.plot(x, y)
changeLabels(ax3.xaxis, [1, 3], ['Testing', 'Testing2'])
ax3.set_title('With offset')
fig.show()
plt.savefig('tick_labels.png')
Mise en garde Il apparaît que les solutions qui utilisent les set_xticklabels()
y compris la mienne, s'appuie sur FixedFormatter
qui est statique et ne réagit pas au redimensionnement de la figure. Pour observer l'effet, changez la figure pour une taille plus petite, par exemple fig, axes = plt.subplots(1, 3, figsize=(6,6))
et agrandir la fenêtre de la figure. Vous remarquerez que seule la colonne centrale réagit au redimensionnement et ajoute des points au fur et à mesure que la figure s'agrandit. Les colonnes de gauche et de droite ont des étiquettes vides (voir figure ci-dessous).
Deuxième mise en garde : J'ai également remarqué que si vos valeurs de tic-tac sont des flottants, l'appel à set_xticklabels(ticks)
peut directement vous donner des chaînes de caractères peu esthétiques, telles que 1.499999999998
au lieu de 1.5
.