Comment définir l'échelle de graduation comme puissance de 2 dans matplotlib? [dupliquer]
Je veux tracer une figure dont les valeurs xtick augmentent à la puissance de 2.
Par exemple,
import pandas as pd
data = pd.DataFrame({
'x': [2, 4, 8, 16, 32, 64],
'y': [1, 2, 3, 4, 5, 6]
})
Ce que j'attends, c'est un chiffre comme celui-ci,

Pour des données ordonnées comme l'exemple ci-dessus, je peux simplement créer une colonne auxiliaire x2
de 1 ~ 6 dans ce dataframe, puis définir les xticklabels de la figure sur 2 ^ 1 ~ 2 ^ 6. Cependant, cette solution de contournement ne s'applique pas aux situations avec d'autres valeurs telles que 3, 7 ou 30.
Il semble que matplotlib ne supporte qu'une échelle logarithmique. Comment puis-je obtenir une graduation avec une échelle de puissance de 2?
Réponses
C'est une question particulièrement piège (que je ne m'attendais pas à être ^^).
OK, commençons par quelques conseils de lecture: Vous voulez définir l'échelle x / y: .matplotlib.axes.Axes.set_yscale () . Bien qu'il existe quelques échelles standard (la valeur par défaut est évidemment 'liner'
, on peut définir une échelle personnalisée. Voici quelques exemples intéressants.
En gros, vous définissez deux fonctions avec la transformation avant et avec l' inverse de celle-ci. Ensuite, vous devez définir correctement les graduations (parce que vous appliquez la transformation après le tracé, les graduations restent les mêmes (mais pas à la même position en raison de la transformation). On a deux options pour cela:
- définir les graduations manuellement
matplotlib.axes.Axes.set_xticks()
, ou - en réglant le positionnement de l'axe:
matplotlib.axes.Axes.xaxis.set_major_locator()
. Ceci est recommandé si vous utilisez des grilles. Mais comme mes connaissances sont limitées, j'apprécie une explication plus détaillée (car maintenant je suis aussi curieuse de cette fonctionnalité ^^)
Et maintenant vient la partie délicate: formater les étiquettes de graduation pour représenter un fichier '2^x'
. Je n'ai pas trouvé de meilleure idée que de les définir explicitement comme des chaînes. Il semble que l'on ne puisse changer le format général que dans des limites restreintes, voir matplotlib.pyplot.ticklabel_format(), où l'on peut choisir si et quand une notation scientifique doit être utilisée (c'est-à-dire en affichant un '10^x'
en bas à droite). Faites-moi savoir s'il existe une solution plus générique pour cela.
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.ticker import FixedLocator
# create dummy data
df = pd.DataFrame({
'x': [2**x for x in range(1,10)],
'y': list(range(1,10))
})
def forward(x):
return np.log2(x)
def inverse(x):
return 2**x
# open figure
fig, ax = plt.subplots(2,2)
axs = ax.flatten()
for i in range(0,4):
# plot data
axs[i].plot(df['x'],df['y'])
if i > 0:
# set scale function
axs[i].set_xscale('function', functions=(forward,inverse))
if i > 1:
# set ticks
# - OPTION 1
axs[i].set_xticks(df['x'])
# - OPTION 2
axs[i].xaxis.set_major_locator(FixedLocator(2**np.arange(1,10)))
if i > 2:
# est tick labels
axs[i].set_xticklabels( [f"2^{j:.0f}" for j in np.log2(df['x'])] )
plt.show()