Marcas de fecha y rotación [duplicate]

8 minutos de lectura

Avatar de usuario de Neal
Neal

Tengo un problema al tratar de rotar mis marcas de fecha en matplotlib. A continuación se muestra un pequeño programa de muestra. Si trato de rotar las marcas al final, las marcas no se rotan. Si trato de rotar las marcas como se muestra debajo del comentario ‘falla’, entonces matplot lib falla.

Esto solo sucede si los valores de x son fechas. Si reemplazo la variable dates con la variable t en la llamada a avail_plotel xticks(rotation=70) la 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
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()

  • Crear un gráfico con fechas en el eje x es una tarea muy común; es una pena que no haya ejemplos más completos.

    – alexw

    13/03/2016 a las 20:29

avatar de usuario de cge
cge

Si prefiere un enfoque no orientado a objetos, muévase plt.xticks(rotation=70) a derecha antes los dos avail_plot llamadas, por ejemplo

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. Ya que tienes dos hachas aquí, plt.xticks se confunde después de haber hecho las dos tramas. en el momento en que plt.xticks no hace nada, plt.gca() hace no darle los ejes que desea modificar, y así plt.xticksque actúa sobre los ejes actuales, no va a funcionar.

Para un enfoque orientado a objetos que no utilice plt.xtickspuedes usar

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

después los dos avail_plot llamadas Esto establece la rotación en los ejes correctos específicamente.

  • Otra cosa útil: cuando llamas plt.setp puede establecer varios parámetros especificándolos como argumentos de palabras clave adicionales. el horizontalalignment kwarg es particularmente útil cuando gira las etiquetas de marca: plt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70, horizontalalignment='right' )

    – 8uno6

    4 de agosto de 2015 a las 12:54


  • No me gusta esta solución ya que mezcla los enfoques de pyplot y orientado a objetos. solo puedes llamar ax.tick_params(axis='x', rotation=70) en cualquier lugar

    –Ted Petrou

    9 oct 2017 a las 17:41

  • @TedPetrou ¿Qué quiere decir con “mezclar” aquí? el plt.setp La solución está completamente orientada a objetos. Si no le gusta el hecho de que hay algunos plt en ella, utiliza from matplotlib.artist import setp; setp(ax.get_xticklabels(), rotation=90) en cambio.

    – La Importancia De Ser Ernesto

    9 oct 2017 a las 19:29

  • @ImportanceOfBeingErnest La primera línea de código utiliza plt.xticks que es pyplot. La documentación en sí dice que no se mezclen estilos. plt.setp es más detallado pero tampoco tiene el típico estilo orientado a objetos. Tu respuesta es definitivamente la mejor aquí. Básicamente, cualquier cosa con una función no está orientada a objetos. No importa si importas setp O no.

    –Ted Petrou

    9 oct 2017 a las 19:37


  • Gran parte del código de la pregunta usa pyplot, como yticksy el código no tiene ax definido en absoluto fuera de avail_plot

    – cge

    9 oct 2017 a las 19:43

Avatar de usuario de Ted Petrou
Ted Petrou

La solución funciona para matplotlib 2.1+

Existe un método de ejes. tick_params que puede cambiar las propiedades de las marcas. También existe como un método de eje como set_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 combina la interfaz con estado (usando pyplot) con la interfaz orientada a objetos usando el comando plt.xticks(rotation=70). Dado que el código de la pregunta utiliza el enfoque orientado a objetos, es mejor ceñirse a ese enfoque en todo momento. La solución da una buena solución explícita con plt.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 parcela que hago.

    – Chogg

    17 de junio de 2020 a las 18:48


  • Parece el método más OOP aquí.

    – minutos

    9 de noviembre de 2020 a las 16:26

Avatar de usuario de ImportanceOfBeingErnest
La Importancia De SerErnest

Una solución fácil que evita pasar por encima de los ticklabes es simplemente usar

fig.autofmt_xdate()

Este comando gira automáticamente las etiquetas del eje x 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 adicional bottom argumento es equivalente a establecer plt.subplots_adjust(bottom=bottom)que permite establecer el relleno de los ejes inferiores en un valor mayor para alojar las etiquetas rotadas.

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

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

  • ¡Gran respuesta! ¡Gracias!

    – Abramodj

    7 julio 2017 a las 11:25

  • ¿Puedes controlar el parámetro ‘modo_rotación’ a través de esta función?

    – TeoríaX

    24 de febrero de 2020 a las 14:54

  • @TheoryX No. Si necesita el modo de rotación, debe usar el plt.setp enfoque, o bucle sobre las marcas (que es esencialmente lo mismo).

    – La Importancia De Ser Ernesto

    24 de febrero de 2020 a las 14:57

  • 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 dos ejes x (twinx) están presentes, estropea la alineación entre las marcas y las etiquetas de las marcas. En ese caso, la solución de Ted Petrou es mejor.

    – Ombrófilo

    15 mayo 2020 a las 16:43


  • @Śubham Correcto. Esto es principalmente un atajo a todas las demás soluciones para el caso más común.

    – La Importancia De Ser Ernesto

    15 mayo 2020 a las 18:32

Avatar de usuario de Pablo Reyes
pablo reyes

Otra forma de aplicarhorizontalalignment y rotation a cada etiqueta de marca está haciendo un for Recorra 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 los ticks 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í

Claramente llegué tarde, pero hay un ejemplo oficial que usa

plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor")

para rotar las etiquetas manteniéndolas correctamente alineadas con las marcas, lo cual es limpio y fácil.

Árbitro: https://matplotlib.org/stable/gallery/images_contours_and_fields/image_annotated_heatmap.html

avatar de usuario de gizzmole
molleja

Simplemente use

ax.set_xticklabels(label_list, rotation=45)

¿Ha sido útil esta solución?