matplotlib: formatea los valores de desplazamiento del eje a números enteros o números específicos

92

Tengo una figura de matplotlib que estoy trazando datos que siempre se conocen como nanosegundos (1e-9). En el eje y, si tengo datos que son decenas de nanosegundos, es decir. 44e-9, el valor en el eje se muestra como 4.4 con un + 1e-8 como compensación. ¿Hay alguna forma de forzar que el eje muestre 44 con un desplazamiento + 1e-9?

Lo mismo ocurre con mi eje x donde el eje muestra + 5.54478e4, donde preferiría que mostrara un desplazamiento de +55447 (número entero, sin decimal; el valor aquí está en días).

He intentado un par de cosas como esta:

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

para el eje x, pero esto no funciona, aunque probablemente lo esté usando incorrectamente o malinterpretando algo de los documentos, ¿alguien puede señalarme en la dirección correcta?

Gracias Jonathan

Ilustración del problema


Intenté hacer algo con formateadores pero aún no he encontrado ninguna solución ...:

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

y

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

En una nota al margen, estoy realmente confundido en cuanto a dónde reside realmente el objeto 'número de compensación' ... ¿es parte de los ticks mayores / menores?

Jonathan
fuente
1
¿Usted ha intentado set_units? matplotlib.sourceforge.net/api/… (No puedo probarlo porque no tengo matplotlib aquí.)
Katriel
1
Revisé la función set_units y parece mucho más complicado de lo necesario (¿tengo que escribir / agregar un módulo adicional? - basic_units?). Tiene que haber una forma de simplemente editar el formato de la marca. La función units / set_unit parece estar más en la línea de conversión de unidades. Sin embargo, gracias por el consejo, ¡me ha llevado a otras soluciones que estoy buscando ahora!
Jonathan
1
por favor considere rcParamsapagar si está apagado por defecto: rcParams["axes.formatter.useoffset"] = Falsecomo aquí: stackoverflow.com/questions/24171064/…
Ruggero Turra

Respuestas:

100

Tuve exactamente el mismo problema, y ​​estas líneas solucionaron el problema:

from matplotlib.ticker import ScalarFormatter

y_formatter = ScalarFormatter(useOffset=False)
ax.yaxis.set_major_formatter(y_formatter)
Gonzalo
fuente
1
Esta es la respuesta rápida y sencilla. Gracias.
maxm
6
Una sola línea sería:ax.get_yaxis().get_major_formatter().set_useOffset(False)
Dataman
3
para novatos como yo, no olviden que el from matplotlib.ticker import ScalarFormattercódigo de @Gonzalo funcione o simplemente use la solución de
@Dataman
36

Una solución mucho más sencilla es simplemente personalizar las etiquetas de las marcas. Toma este ejemplo:

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()

texto alternativo

Observe cómo en el caso del eje y, multipliqué los valores y 1e9luego mencioné esa constante en la etiqueta y


EDITAR

Otra opción es falsificar el multiplicador de exponentes agregando manualmente su texto en la parte superior de la gráfica:

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

También puede formatear el valor de desplazamiento del eje x de la misma manera:

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)

texto alternativo

Amro
fuente
Eso fue exactamente lo que hice, al principio. Desafortunadamente, no pude encontrar una manera fácil de establecer / mostrar el multiplicador del eje (aparte de ponerlo explícitamente en la etiqueta del eje y, como lo hizo). Si no le importa no tener la etiqueta del multiplicador de ejes, esta es la forma más sencilla. De cualquier manera, +1 de mí.
Joe Kington
1
@Joe Kington: puede agregarlo manualmente como texto ... ver la edición anterior :)
Amro
¡Excelente! Voy a probar tu enfoque con las etiquetas del eje x. Tomaré el piso del primer valor de x, luego lo eliminaré de cada valor de x y agregaré un "+ minxval" como etiqueta. No puedo averiguar de qué otra manera formatear el desplazamiento x-tick. Estoy bien con la magnitud del desplazamiento, solo necesito que se muestre como un valor no exponencial.
Jonathan
Guau. Gran trabajo al mostrar cómo realmente puedes tomar el control de matplotlib y ajustarlo a tus necesidades y realmente y algo de pizazz a tus tramas.
physicsmichael
¿Cómo se cambia el tamaño de fuente de 1e-9 en la figura?
una oferta no se puede rechazar
30

Tienes que crear una subclase ScalarFormatterpara hacer lo que necesitas ... _set_offsetsolo agrega una constante, quieres establecer ScalarFormatter.orderOfMagnitude. Desafortunadamente, la configuración manual orderOfMagnitudeno hará nada, ya que se restablece cuando ScalarFormatterse llama a la instancia para formatear las etiquetas de marca del eje. No debería ser tan complicado, pero no puedo encontrar una manera más fácil de hacer exactamente lo que quieres ... Aquí tienes un ejemplo:

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()

Lo que produce algo como: texto alternativo

Considerando que, el formato predeterminado se vería así: texto alternativo

¡Espero que esto ayude un poco!

Editar: Por lo que vale, tampoco sé dónde reside la etiqueta de compensación ... Sería un poco más fácil configurarla manualmente, pero no pude averiguar cómo hacerlo ... Tengo la sensación que tiene que haber una manera más fácil que todo esto. ¡Aunque funciona!

Joe Kington
fuente
¡Gracias! ¡Subclasificar ScalarFormatter funciona muy bien! Pero supongo que no dije claramente lo que quería para el eje x. Me gustaría mantener el desplazamiento para el eje x, pero formatee el valor del desplazamiento para que no se muestre como un exponente.
Jonathan
¡Este es el único método que funcionó para mí! Gracias :)
Ocean Scientist
11

Similar a la respuesta de Amro, puede usar 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()
idrinkpabst
fuente
5

La solución de Gonzalo comenzó a funcionar para mí después de haber agregado set_scientific(False):

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

Como se ha señalado en los comentarios y en esta respuesta , el desplazamiento puede desactivarse globalmente, haciendo lo siguiente:

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

Creo que una forma más elegante es utilizar el formateador de ticker. Aquí hay un ejemplo para xaxis y 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)
Bogdan
fuente
1
Esto no responde a la pregunta, que es cómo especificar el desplazamiento y / o el factor utilizado en la notación científica .
sodd
@nordev Incluso si mi respuesta no responde específicamente a la pregunta, todavía da una pista. El mensaje es que puede elegir otro formateador y obtener lo que desee en lugar de una fecha de mi ejemplo. En el mundo científico, el día juliano es la norma, o puede usar la fecha como en mi ejemplo. Lo que estaba tratando de sugerir es que se puede adoptar un enfoque diferente. A veces, se puede hacer una pregunta porque la persona no tiene una idea mejor en este momento. Las soluciones alternativas no deben descartarse ni tratarse con falta de respeto. Con todo, no merecía el voto -1.
Bogdan
2

Para la segunda parte, sin restablecer manualmente todos los ticks nuevamente, esta fue mi solución:

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)

obviamente, puede configurar la cadena de formato a lo que desee.

astrojuanlu
fuente
Desafortunadamente, no he investigado cómo establecer el multiplicador como dice la primera parte de su pregunta.
astrojuanlu