web-dev-qa-db-fra.com

matplotlib: met en forme les valeurs de décalage d'axe en nombres entiers ou en nombres spécifiques

J'ai un chiffre matplotlib sur lequel je trace des données toujours appelées nanosecondes (1e-9). Sur l’axe des y, si j’ai des données de dizaines de nanosecondes, c’est-à-dire. 44e-9, la valeur sur l’axe indique 4,4 avec un décalage + 1e-8. Est-il possible de forcer l'axe à afficher 44 avec un décalage de + 1e-9?

Il en va de même pour mon axe x où l'axe affiche + 5.54478e4, où je préférerais afficher un décalage de +55447 (nombre entier, pas de décimale - la valeur ici est en jours).

J'ai essayé quelques choses comme ça:

p = axes.plot(x,y)
p.ticklabel_format(style='plain')

pour l'axe des x, mais cela ne fonctionne pas, bien que je l'utilise probablement de manière incorrecte ou que j'interprète mal quelque chose dans la documentation, quelqu'un peut-il m'indiquer dans la bonne direction?

Merci Jonathan

Problem illustration


J'ai essayé de faire quelque chose avec les formateurs mais je n'ai pas encore trouvé de solution ...:

myyfmt = ScalarFormatter(useOffset=True)
myyfmt._set_offset(1e9)
axes.get_yaxis().set_major_formatter(myyfmt)

et

myxfmt = ScalarFormatter(useOffset=True)
myxfmt.set_portlimits((-9,5))
axes.get_xaxis().set_major_formatter(myxfmt)

Sur une note de côté, je suis vraiment confus quant à l'endroit où l'objet "numéro de décalage" réside réellement ... fait-il partie des ticks majeurs/mineurs?

89
Jonathan

J'ai eu exactement le même problème, et ces deux lignes ont résolu le problème:

y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
ax.yaxis.set_major_formatter(y_formatter)
97
Gonzalo

Une solution beaucoup plus simple consiste à personnaliser les libellés de ticks. Prenons cet exemple:

from pylab import *

# Generate some random data...
x = linspace(55478, 55486, 100)
y = random(100) - 0.5
y = cumsum(y)
y -= y.min()
y *= 1e-8

# plot
plot(x,y)

# xticks
locs,labels = xticks()
xticks(locs, map(lambda x: "%g" % x, locs))

# ytikcs
locs,labels = yticks()
yticks(locs, map(lambda x: "%.1f" % x, locs*1e9))
ylabel('microseconds (1E-9)')

show()

alt text

Remarquez comment dans le cas de l'axe des y, j'ai multiplié les valeurs par 1e9 puis mentionné cette constante dans l'étiquette y


EDIT

Une autre option consiste à simuler le multiplicateur d'exposant en ajoutant manuellement son texte en haut du graphique:

locs,labels = yticks()
yticks(locs, map(lambda x: "%.1f" % x, locs*1e9))
text(0.0, 1.01, '1e-9', fontsize=10, transform = gca().transAxes)

EDIT2

Vous pouvez également formater la valeur de décalage de l’axe des x de la même manière:

locs,labels = xticks()
xticks(locs, map(lambda x: "%g" % x, locs-min(locs)))
text(0.92, -0.07, "+%g" % min(locs), fontsize=10, transform = gca().transAxes)

alt text

34
Amro

Vous devez sous-classer ScalarFormatter pour faire ce dont vous avez besoin ... _set_offset ajoute simplement une constante, vous voulez définir ScalarFormatter.orderOfMagnitude. Malheureusement, définir manuellement orderOfMagnitude ne fera rien, car il est réinitialisé lorsque l'instance ScalarFormatter est appelée pour formater les étiquettes de ticks d'axe. Cela ne devrait pas être aussi compliqué, mais je ne trouve pas de moyen plus facile de faire exactement ce que vous voulez ... Voici un exemple:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter, FormatStrFormatter

class FixedOrderFormatter(ScalarFormatter):
    """Formats axis ticks using scientific notation with a constant order of 
    magnitude"""
    def __init__(self, order_of_mag=0, useOffset=True, useMathText=False):
        self._order_of_mag = order_of_mag
        ScalarFormatter.__init__(self, useOffset=useOffset, 
                                 useMathText=useMathText)
    def _set_orderOfMagnitude(self, range):
        """Over-riding this to avoid having orderOfMagnitude reset elsewhere"""
        self.orderOfMagnitude = self._order_of_mag

# Generate some random data...
x = np.linspace(55478, 55486, 100) 
y = np.random.random(100) - 0.5
y = np.cumsum(y)
y -= y.min()
y *= 1e-8

# Plot the data...
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, 'b-')

# Force the y-axis ticks to use 1e-9 as a base exponent 
ax.yaxis.set_major_formatter(FixedOrderFormatter(-9))

# Make the x-axis ticks formatted to 0 decimal places
ax.xaxis.set_major_formatter(FormatStrFormatter('%0.0f'))
plt.show()

Ce qui donne quelque chose comme: alt text

Considérant que, le formatage par défaut ressemblerait à ceci: alt text

J'espère que ça aidera un peu!

Edit: Pour ce que ça vaut, je ne sais pas non plus où se trouve l’étiquette offset ... Ce serait un peu plus facile de la définir manuellement, mais je ne savais pas comment le faire. qu'il doit y avoir un moyen plus facile que tout cela. Cela fonctionne, cependant!

28
Joe Kington

Semblable à la réponse d'Amro, vous pouvez utiliser FuncFormatter

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter

# Generate some random data...
x = np.linspace(55478, 55486, 100) 
y = np.random.random(100) - 0.5
y = np.cumsum(y)
y -= y.min()
y *= 1e-8

# Plot the data...
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, 'b-')

# Force the y-axis ticks to use 1e-9 as a base exponent 
ax.yaxis.set_major_formatter(FuncFormatter(lambda x, pos: ('%.1f')%(x*1e9)))
ax.set_ylabel('microseconds (1E-9)')

# Make the x-axis ticks formatted to 0 decimal places
ax.xaxis.set_major_formatter(FuncFormatter(lambda x, pos: '%.0f'%x))
plt.show()
11
idrinkpabst

La solution de Gonzalo a commencé à fonctionner pour moi après avoir ajouté set_scientific(False):

ax=gca()
fmt=matplotlib.ticker.ScalarFormatter(useOffset=False)
fmt.set_scientific(False)
ax.xaxis.set_major_formatter(fmt)
5
ha7ilm

Je pense qu'un moyen plus élégant consiste à utiliser le formateur de ticker. Voici un exemple pour xaxis et yaxis:

from pylab import *
from matplotlib.ticker import MultipleLocator, FormatStrFormatter

majorLocator   = MultipleLocator(20)
xFormatter = FormatStrFormatter('%d')
yFormatter = FormatStrFormatter('%.2f')
minorLocator   = MultipleLocator(5)


t = arange(0.0, 100.0, 0.1)
s = sin(0.1*pi*t)*exp(-t*0.01)

ax = subplot(111)
plot(t,s)

ax.xaxis.set_major_locator(majorLocator)
ax.xaxis.set_major_formatter(xFormatter)
ax.yaxis.set_major_formatter(yFormatter)

#for the minor ticks, use no labels; default NullFormatter
ax.xaxis.set_minor_locator(minorLocator)
5
Bogdan

Comme indiqué dans les commentaires et dans cette réponse , le décalage peut être désactivé de manière globale en procédant comme suit:

matplotlib.rcParams['axes.formatter.useoffset'] = False
4
Evgeni Sergeev

Pour la deuxième partie, sans réinitialiser manuellement tous les ticks, voici ma solution:

class CustomScalarFormatter(ScalarFormatter):
    def format_data(self, value):
        if self._useLocale:
            s = locale.format_string('%1.2g', (value,))
        else:
            s = '%1.2g' % value
        s = self._formatSciNotation(s)
        return self.fix_minus(s)
xmajorformatter = CustomScalarFormatter()  # default useOffset=True
axes.get_xaxis().set_major_formatter(xmajorformatter)

évidemment, vous pouvez définir la chaîne de formatage comme vous le souhaitez.

1
astrojuanlu