python - xlabel - ¿Cómo mover la etiqueta de una garrapata en matplotlib?
set xlabel matplotlib (4)
Me gustaría mover las etiquetas de algunos ticks horizontalmente a lo largo del eje x, sin mover los ticks correspondientes.
Más específicamente, al rotar etiquetas con plt.setp
, los centros del texto de las etiquetas permanecen alineados con las marcas. Me gustaría mover esas etiquetas a la derecha, para que los extremos cercanos de las etiquetas se alineen como se sugiere en la imagen de abajo.
Estoy al tanto de esta publicación y esta , sin embargo, las respuestas son más interesantes que las respuestas estrictas a la pregunta.
mi código:
import matplotlib.pyplot as plt
import numpy as np
import datetime
# my fake data
dates = np.array([datetime.datetime(2000,1,1) + datetime.timedelta(days=i) for i in range(365*5)])
data = np.sin(np.arange(365*5)/365.0*2*np.pi - 0.25*np.pi) + np.random.rand(365*5) /3
# creates fig with 2 subplots
fig = plt.figure(figsize=(10.0, 6.0))
ax = plt.subplot2grid((2,1), (0, 0))
ax2 = plt.subplot2grid((2,1), (1, 0))
## plot dates
ax2.plot_date( dates, data )
# rotates labels
plt.setp( ax2.xaxis.get_majorticklabels(), rotation=-45 )
# try to shift labels to the right
ax2.xaxis.get_majorticklabels()[2].set_y(-.1)
ax2.xaxis.get_majorticklabels()[2].set_x(10**99)
plt.show()
Por extraño que parezca, set_y
comporta como se esperaba, pero incluso si configuro x
en un fantasillion, las etiquetas no se moverían en una iota. (El uso de plot_date
puede introducir confusión adicional, pero lo mismo sucede con plot
).
En lugar de
ax2.xaxis.get_majorticklabels()[2].set_y(-.1)
ax2.xaxis.get_majorticklabels()[2].set_x(10**99)
use set_horizontalalignment()
para cada tick en el eje:
for tick in ax2.xaxis.get_majorticklabels():
tick.set_horizontalalignment("left")
Resultando en:
En primer lugar, usemos un mcve para mostrar el problema.
import numpy as np
import datetime
import matplotlib.pyplot as plt
plt.rcParams["date.autoformatter.month"] = "%b %Y"
# my fake data
dates = np.array([datetime.datetime(2000,1,1) + datetime.timedelta(days=i) for i in range(365)])
data = np.sin(np.arange(365)/365.0*2*np.pi - 0.25*np.pi) + np.random.rand(365) /3
# creates fig with 2 subplots
fig, ax = plt.subplots(figsize=(6,2))
## plot dates
ax.plot_date( dates, data )
# rotates labels
plt.setp( ax.xaxis.get_majorticklabels(), rotation=-45 )
plt.tight_layout()
plt.show()
Ahora, como ya han indicado otras respuestas, puede utilizar la alineación horizontal del texto.
# rotates labels and aligns them horizontally to left
plt.setp( ax.xaxis.get_majorticklabels(), rotation=-45, ha="left" )
Puede usar el argumento rotation_mode
para permitir que se produzca la rotación en el punto superior izquierdo del texto, dando un resultado ligeramente mejor en este caso.
# rotates labels and aligns them horizontally to left
plt.setp( ax.xaxis.get_majorticklabels(), rotation=-45, ha="left", rotation_mode="anchor")
En caso de que esas opciones no sean lo suficientemente detalladas, es decir, si desea ubicar las etiquetas con mayor precisión, por ejemplo, desplazándolas hacia un lado en algunos puntos, puede usar una transformación. Lo siguiente compensaría la etiqueta en 5 puntos en dirección horizontal, utilizando un matplotlib.transforms.ScaledTranslation
.
import matplotlib.transforms
plt.setp( ax.xaxis.get_majorticklabels(), rotation=-45)
# Create offset transform by 5 points in x direction
dx = 5/72.; dy = 0/72.
offset = matplotlib.transforms.ScaledTranslation(dx, dy, fig.dpi_scale_trans)
# apply offset transform to all x ticklabels.
for label in ax.xaxis.get_majorticklabels():
label.set_transform(label.get_transform() + offset)
La ventaja de esto, en comparación con, por ejemplo, la solución provista por @explorerDude es que el desplazamiento es independiente de los datos en el gráfico, de modo que generalmente es aplicable a cualquier gráfico y se vería igual para un tamaño de fuente determinado.
Encontré una manera de cambiar las etiquetas de garrapatas del eje x en una cantidad arbitraria y exacta, pero de esta manera corre peligrosamente cerca de los acantilados escarpados y resbaladizos que se elevan sobre el mar de la locura. Así que solo los muy valientes o desesperados deberían seguir leyendo ...
Dicho esto, el problema es que la posición x de las etiquetas se establece cuando se representa el dibujo (no he buscado en esa parte del código, pero es mi entendimiento). Así que todo lo que haces con set_x () se anula más tarde. Sin embargo, hay una forma de evitarlo: puede crear un parche set_x para ciertos ticks para que las etiquetas no se dibujen donde el renderizador quiera dibujarlos:
import types
SHIFT = 10. # Data coordinates
for label in ax2.xaxis.get_majorticklabels():
label.customShiftValue = SHIFT
label.set_x = types.MethodType( lambda self, x: matplotlib.text.Text.set_x(self, x-self.customShiftValue ),
label, matplotlib.text.Text )
Puede hacerlo de forma selectiva solo para las etiquetas que desea cambiar y, por supuesto, también puede utilizar un turno diferente para cada etiqueta.
Si alguien sabe cómo hacer esto en un nivel de locura inferior, estaría muy interesado ...
Otra forma de hacer una alineación horizontal:
plt.xticks(ha=''left'')