Segni di spunta e rotazione della data in matplotlib


175

Sto riscontrando un problema durante il tentativo di ottenere la rotazione dei segni di spunta della data in matplotlib. Di seguito è riportato un piccolo programma di esempio. Se provo a ruotare le zecche alla fine, le zecche non vengono ruotate. Se provo a ruotare i segni di spunta come mostrato sotto il commento 'crash', allora matplot lib crash.

Questo succede solo se i valori x sono date. Se sostituisco la variabile datescon la variabile tnella chiamata a avail_plot, la xticks(rotation=70)chiamata funziona perfettamente all'interno avail_plot.

Qualche idea?

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
Creare un grafico con le date sull'asse x è un compito così comune - un peccato che non ci siano esempi più completi là fuori.
alexw,

Mi chiedo se questo non è un duplicato di stackoverflow.com/questions/10998621/…
ImportanceOfBeingErnest

Risposte:


249

Se si preferisce un approccio non orientato agli oggetti, spostarsi plt.xticks(rotation=70)a destra prima delle due avail_plotchiamate, ad es

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

Questo imposta la proprietà di rotazione prima di impostare le etichette. Dato che hai due assi qui, plt.xticksti confondi dopo aver fatto i due grafici. Nel momento in cui plt.xticksnon fa nulla, plt.gca()non ti dà gli assi che vuoi modificare, e quindi plt.xticks, che agisce sugli assi attuali, non funzionerà.

Per un approccio orientato agli oggetti che non utilizza plt.xticks, è possibile utilizzare

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

dopo le due avail_plotchiamate. Ciò imposta specificamente la rotazione sugli assi corretti.


11
Un'altra cosa utile: quando chiami plt.setppuoi impostare più parametri specificandoli come argomenti di parole chiave aggiuntivi. il horizontalalignmentkwarg è particolarmente utile quando si ruotano le etichette dei tick:plt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70, horizontalalignment='right' )
8one6

32
Non mi piace questa soluzione in quanto mescola il pyplot e gli approcci orientati agli oggetti. Puoi semplicemente chiamare ax.tick_params(axis='x', rotation=70)in qualsiasi luogo.
Ted Petrou,

3
@TedPetrou Cosa intendi per "miscelazione" qui? la plt.setpsoluzione è completamente orientata agli oggetti. Se non ti piace il fatto che ce ne siano alcuni plt, usa from matplotlib.artist import setp; setp(ax.get_xticklabels(), rotation=90)invece.
ImportanceOfBeingErnest

@ImportanceOfBeingErnest La prima riga di codice utilizza plt.xticksche è pyplot. La stessa documentazione dice di non mescolare gli stili. plt.setpè più dettagliato ma non è nemmeno il tipico stile orientato agli oggetti. La tua risposta è sicuramente la migliore qui. Fondamentalmente, qualsiasi cosa con una funzione non è orientata agli oggetti. Non importa se si importa setpo no.
Ted Petrou,

Gran parte del codice della domanda usa pyplot, come yticks, e il codice non ha un'ascia definita affatto al di fuori di avail_plot...
cge

114

La soluzione funziona con matplotlib 2.1+

Esiste un metodo assi tick_paramsche può modificare le proprietà del tick. Esiste anche come metodo dell'asse comeset_tick_params

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

O

ax.xaxis.set_tick_params(rotation=45)

Come nota a margine, la soluzione corrente mescola l'interfaccia stateful (usando pyplot) con l'interfaccia orientata agli oggetti usando il comando plt.xticks(rotation=70). Poiché il codice nella domanda utilizza l'approccio orientato agli oggetti, è meglio attenersi a tale approccio in tutto. La soluzione offre una buona soluzione esplicita conplt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70 )


È possibile modificare la rotazione predefinita per tutti i grafici? Penso di dover cambiare questo su ogni trama che faccio.
Chogg,

42

Una soluzione semplice che evita il looping sopra i ticklabes è semplicemente usare

fig.autofmt_xdate()

Questo comando ruota automaticamente le etichette degli assi x e ne regola la posizione. I valori predefiniti sono un angolo di rotazione di 30 ° e un allineamento orizzontale "a destra". Ma possono essere modificati nella chiamata di funzione

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

L'ulteriore bottom argomento equivale all'impostazione plt.subplots_adjust(bottom=bottom), che consente di impostare il riempimento degli assi inferiori su un valore maggiore per ospitare i ticklabel ruotati.

Quindi sostanzialmente qui hai tutte le impostazioni che ti servono per avere un buon asse data in un singolo comando.

Un buon esempio può essere trovato nella pagina matplotlib.


Bella risposta! Grazie!
Abramodj,

puoi controllare il parametro 'rotation_mode' tramite questa funzione?
TheoryX

1
@TheoryX No. Se hai bisogno di rotation_mode, devi utilizzare l' plt.setpapproccio o passare in rassegna i tick (che è essenzialmente lo stesso).
ImportanceOfBeingErnest

Sì, è una soluzione semplice solo quando l'asse x è presente nella parte inferiore del grafico. Tuttavia, quando l'asse x si trova nella parte superiore del grafico o quando twinxsono presenti due assi x ( ), si annulla l'allineamento tra tick ed etichette tick. In tal caso, la soluzione di Ted Petrou è migliore.
Śubham,

@ Śubham Correct. Questo è principalmente un collegamento a tutte le altre soluzioni per il caso più comune.
ImportanceOfBeingErnest

18

Un altro modo per applicare horizontalalignmente rotationper ciascuna etichetta tick è fare un forciclo sopra le etichette tick che si desidera modificare:

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

inserisci qui la descrizione dell'immagine

Ed ecco un esempio se vuoi controllare la posizione delle zecche maggiori e minori:

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

inserisci qui la descrizione dell'immagine


1
Questo ha funzionato per me su matplotlib v1.5.1 (sono bloccato su una versione legacy di matplotlib al lavoro, non chiedermi perché)
Eddy

Ho appena provato con python3.6 e matplotlib v2.2.2 e funziona anche.
Pablo Reyes,

2

Basta usare

ax.set_xticklabels(label_list, rotation=45)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.