Marca de fecha y rotación en matplotlib


175

Tengo un problema al intentar rotar mis tics de fecha en matplotlib. Un pequeño programa de muestra está debajo. Si trato de rotar las garrapatas al final, las garrapatas no se rotan. Si intento rotar los ticks como se muestra debajo del comentario 'se bloquea', entonces matplot lib se bloquea.

Esto solo sucede si los valores de x son fechas. Si reemplazo la variable datescon la variable ten la llamada a avail_plot, la xticks(rotation=70)llamada funciona bien por dentro avail_plot.

¿Algunas ideas?

import numpy as np
import matplotlib.pyplot as plt
import datetime as dt

def avail_plot(ax, x, y, label, lcolor):
    ax.plot(x,y,'b')
    ax.set_ylabel(label, rotation='horizontal', color=lcolor)
    ax.get_yaxis().set_ticks([])

    #crashes
    #plt.xticks(rotation=70)

    ax2 = ax.twinx()
    ax2.plot(x, [1 for a in y], 'b')
    ax2.get_yaxis().set_ticks([])
    ax2.set_ylabel('testing')

f, axs = plt.subplots(2, sharex=True, sharey=True)
t = np.arange(0.01, 5, 1)
s1 = np.exp(t)
start = dt.datetime.now()
dates=[]
for val in t:
    next_val = start + dt.timedelta(0,val)
    dates.append(next_val)
    start = next_val

avail_plot(axs[0], dates, s1, 'testing', 'green')
avail_plot(axs[1], dates, s1, 'testing2', 'red')
plt.subplots_adjust(hspace=0, bottom=0.3)
plt.yticks([0.5,],("",""))
#doesn't crash, but does not rotate the xticks
#plt.xticks(rotation=70)
plt.show()

3
Crear una gráfica con fechas en el eje x es una tarea tan común: una pena que no haya ejemplos más completos.
alexw

Me pregunto si esto no es un duplicado de stackoverflow.com/questions/10998621/…
ImportanceOfBeingErnest

Respuestas:


249

Si prefiere un enfoque no orientado a objetos, muévase plt.xticks(rotation=70)a la derecha antes de las dos avail_plotllamadas, p. Ej.

plt.xticks(rotation=70)
avail_plot(axs[0], dates, s1, 'testing', 'green')
avail_plot(axs[1], dates, s1, 'testing2', 'red')

Esto establece la propiedad de rotación antes de configurar las etiquetas. Como tienes dos ejes aquí, plt.xticksse confunde después de haber hecho las dos parcelas. En el momento en plt.xticksque no hace nada, plt.gca()no le da los ejes que desea modificar y plt.xticks, por lo tanto , lo que actúa sobre los ejes actuales no va a funcionar.

Para un enfoque orientado a objetos que no use plt.xticks, puede usar

plt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70 )

después de las dos avail_plotllamadas. Esto establece la rotación en los ejes correctos específicamente.


11
Otra cosa útil: cuando llama plt.setp, puede establecer múltiples parámetros al especificarlos como argumentos de palabras clave adicionales. el horizontalalignmentkwarg es particularmente útil cuando gira las etiquetas de marca:plt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70, horizontalalignment='right' )
8one6

32
No me gusta esta solución, ya que combina los enfoques orientados a objetos y pyplot. Puedes llamar ax.tick_params(axis='x', rotation=70)en cualquier lugar.
Ted Petrou

3
@TedPetrou ¿Qué quieres decir con "mezclar" aquí? La plt.setpsolución está completamente orientada a objetos. Si no le gusta el hecho de que haya algo plten él, from matplotlib.artist import setp; setp(ax.get_xticklabels(), rotation=90)úselo.
ImportanceOfBeingErnest

@ImportanceOfBeingErnest La primera línea de código utiliza plt.xticksque es pyplot. La documentación en sí dice no mezclar estilos. plt.setpes más detallado pero tampoco el típico estilo orientado a objetos. Su respuesta es definitivamente la mejor aquí. Básicamente, cualquier cosa con una función no está orientada a objetos. No importa si importas setpo no.
Ted Petrou

Gran parte del código de la pregunta usa pyplot, como yticks, y el código no tiene un hacha definida en absoluto fuera de avail_plot...
cge

115

La solución funciona para matplotlib 2.1+

Existe un método de ejes tick_paramsque puede cambiar las propiedades de la marca. También existe como un método de eje comoset_tick_params

ax.tick_params(axis='x', rotation=45)

O

ax.xaxis.set_tick_params(rotation=45)

Como nota al margen, la solución actual mezcla la interfaz con estado (usando pyplot) con la interfaz orientada a objetos mediante el comando plt.xticks(rotation=70). Dado que el código en la pregunta usa el enfoque orientado a objetos, es mejor apegarse a ese enfoque en todo momento. La solución da una buena solución explícita conplt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70 )


¿Es posible cambiar la rotación predeterminada para todas las parcelas? Creo que necesito cambiar esto en cada trama que hago.
Chogg

42

Una solución fácil que evita el bucle sobre los ticklabes es simplemente usar

fig.autofmt_xdate()

Este comando gira automáticamente las etiquetas xaxis y ajusta su posición. Los valores predeterminados son un ángulo de rotación de 30 ° y una alineación horizontal "derecha". Pero se pueden cambiar en la llamada de función

fig.autofmt_xdate(bottom=0.2, rotation=30, ha='right')

El bottomargumento adicional es equivalente a la configuración plt.subplots_adjust(bottom=bottom), lo que permite establecer el relleno de los ejes inferiores en un valor mayor para alojar las etiquetas rotatorias.

Entonces, básicamente, aquí tiene todas las configuraciones que necesita para tener un eje de fecha agradable en un solo comando.

Un buen ejemplo se puede encontrar en la página matplotlib.


¡Gran respuesta! ¡Gracias!
Abramodj

¿Puedes controlar el parámetro 'rotacion_modo' a través de esta función?
TheoryX

1
@TheoryX No. Si necesita rotación_modo, debe usar el plt.setpenfoque o recorrer los tics (que es esencialmente lo mismo).
ImportanceOfBeingErnest

Sí, es una solución fácil solo cuando el eje x está presente en la parte inferior del gráfico. Sin embargo, cuando el eje x está en la parte superior del gráfico o cuando hay dos ejes x ( twinx), arruina la alineación entre las marcas y las etiquetas de las marcas. En ese caso, la solución de Ted Petrou es mejor.
Śubham

@ Śubham Correcto. Esto es principalmente un acceso directo a todas las otras soluciones para el caso más común.
ImportanceOfBeingErnest

18

Otra forma de aplicar horizontalalignmenty rotationpara cada etiqueta de marca es hacer un forbucle sobre las etiquetas de marca que desea cambiar:

import numpy as np
import matplotlib.pyplot as plt
import datetime as dt

now = dt.datetime.now()
hours = [now + dt.timedelta(minutes=x) for x in range(0,24*60,10)]
days = [now + dt.timedelta(days=x) for x in np.arange(0,30,1/4.)]
hours_value = np.random.random(len(hours))
days_value = np.random.random(len(days))

fig, axs = plt.subplots(2)
fig.subplots_adjust(hspace=0.75)
axs[0].plot(hours,hours_value)
axs[1].plot(days,days_value)

for label in axs[0].get_xmajorticklabels() + axs[1].get_xmajorticklabels():
    label.set_rotation(30)
    label.set_horizontalalignment("right")

ingrese la descripción de la imagen aquí

Y aquí hay un ejemplo si desea controlar la ubicación de las garrapatas mayores y menores:

import numpy as np
import matplotlib.pyplot as plt
import datetime as dt

fig, axs = plt.subplots(2)
fig.subplots_adjust(hspace=0.75)
now = dt.datetime.now()
hours = [now + dt.timedelta(minutes=x) for x in range(0,24*60,10)]
days = [now + dt.timedelta(days=x) for x in np.arange(0,30,1/4.)]

axs[0].plot(hours,np.random.random(len(hours)))
x_major_lct = mpl.dates.AutoDateLocator(minticks=2,maxticks=10, interval_multiples=True)
x_minor_lct = matplotlib.dates.HourLocator(byhour = range(0,25,1))
x_fmt = matplotlib.dates.AutoDateFormatter(x_major_lct)
axs[0].xaxis.set_major_locator(x_major_lct)
axs[0].xaxis.set_minor_locator(x_minor_lct)
axs[0].xaxis.set_major_formatter(x_fmt)
axs[0].set_xlabel("minor ticks set to every hour, major ticks start with 00:00")

axs[1].plot(days,np.random.random(len(days)))
x_major_lct = mpl.dates.AutoDateLocator(minticks=2,maxticks=10, interval_multiples=True)
x_minor_lct = matplotlib.dates.DayLocator(bymonthday = range(0,32,1))
x_fmt = matplotlib.dates.AutoDateFormatter(x_major_lct)
axs[1].xaxis.set_major_locator(x_major_lct)
axs[1].xaxis.set_minor_locator(x_minor_lct)
axs[1].xaxis.set_major_formatter(x_fmt)
axs[1].set_xlabel("minor ticks set to every day, major ticks show first day of month")
for label in axs[0].get_xmajorticklabels() + axs[1].get_xmajorticklabels():
    label.set_rotation(30)
    label.set_horizontalalignment("right")

ingrese la descripción de la imagen aquí


1
Esto funcionó para mí en matplotlib v1.5.1 (estoy atascado en una versión heredada de matplotlib en el trabajo, no pregunte por qué)
Eddy

Acabo de probar con python3.6 y matplotlib v2.2.2 y también funciona.
Pablo Reyes

3

Simplemente use

ax.set_xticklabels(label_list, rotation=45)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.