Datumstricks und Rotation in matplotlib

175

Ich habe ein Problem beim Versuch, meine Date-Ticks in matplotlib zu drehen. Ein kleines Beispielprogramm finden Sie weiter unten. Wenn ich versuche, die Zecken am Ende zu drehen, werden die Zecken nicht gedreht. Wenn ich versuche, die Häkchen wie unter dem Kommentar 'Abstürze' gezeigt zu drehen, stürzt matplot lib ab.

Dies geschieht nur, wenn die x-Werte Datumsangaben sind. Wenn ich die Variable datesdurch die Variable tim Aufruf von ersetze avail_plot, xticks(rotation=70)funktioniert der Aufruf im Inneren einwandfreiavail_plot .

Irgendwelche Ideen?

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()
Neal
quelle
3
Das Erstellen eines Diagramms mit Datumsangaben auf der x-Achse ist eine so häufige Aufgabe - schade, dass es keine vollständigeren Beispiele gibt.
Alexw
Ich frage mich, ob dies kein Duplikat von stackoverflow.com/questions/10998621/… ist
ImportanceOfBeingErnest

Antworten:

249

Wenn Sie einen nicht objektorientierten Ansatz bevorzugen, gehen Sie vor den beiden Aufrufen plt.xticks(rotation=70)nach rechts , zavail_plot

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

Dadurch wird die Rotationseigenschaft festgelegt, bevor die Beschriftungen eingerichtet werden. Da Sie hier zwei Achsen haben, plt.xtickswird es verwirrt, nachdem Sie die beiden Diagramme erstellt haben. An dem Punkt , wenn plt.xticksnichts tut, plt.gca()hat nicht geben Sie die Achsen Sie ändern möchten, und soplt.xticks , die auf den gegenwärtigen Achsen wirkt, nicht zur Arbeit gehen.

Für einen objektorientierten Ansatz, der nicht verwendet wird plt.xticks, können Sie verwenden

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

nach den beiden avail_plotanrufen. Dadurch wird die Drehung speziell auf die richtigen Achsen eingestellt.

cge
quelle
11
Eine weitere nützliche Sache: Wenn Sie aufrufen plt.setp, können Sie mehrere Parameter festlegen, indem Sie sie als zusätzliche Schlüsselwortargumente angeben. Das horizontalalignmentKwarg ist besonders nützlich, wenn Sie die Häkchen drehen:plt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70, horizontalalignment='right' )
8one6
31
Ich mag diese Lösung nicht, da sie Pyplot- und objektorientierte Ansätze mischt. Sie können einfach ax.tick_params(axis='x', rotation=70)an jedem Ort anrufen .
Ted Petrou
3
@ TedPetrou Was meinst du hier mit "Mischen"? Die plt.setpLösung ist vollständig objektorientiert. Wenn Ihnen die Tatsache nicht gefällt, dass einige pltdavon enthalten sind, verwenden Sie from matplotlib.artist import setp; setp(ax.get_xticklabels(), rotation=90)stattdessen.
ImportanceOfBeingErnest
@ImportanceOfBeingErnest Die erste verwendete Codezeile plt.xticksist Pyplot. Die Dokumentation selbst sagt, dass Stile nicht gemischt werden sollen. plt.setpist ausführlicher, aber auch nicht der typische objektorientierte Stil. Ihre Antwort ist hier definitiv die beste. Grundsätzlich ist alles mit einer Funktion nicht objektorientiert. Es spielt keine Rolle, ob Sie importieren setpoder nicht.
Ted Petrou
Ein Großteil des Codes der Frage verwendet Pyplot, yticksund der Code hat überhaupt keine Axt außerhalb von avail_plot...
cge
113

Die Lösung funktioniert für matplotlib 2.1+

Es gibt eine Achsenmethode tick_params, mit der die Tick-Eigenschaften geändert werden können. Es existiert auch als Achsenmethode alsset_tick_params

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

Oder

ax.xaxis.set_tick_params(rotation=45)

Nebenbei bemerkt, die aktuelle Lösung mischt die zustandsbehaftete Schnittstelle (mithilfe von Pyplot) mit der objektorientierten Schnittstelle mithilfe des Befehls plt.xticks(rotation=70). Da der Code in der Frage den objektorientierten Ansatz verwendet, ist es am besten, diesen Ansatz durchgehend beizubehalten. Die Lösung gibt eine gute explizite Lösung mitplt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70 )

Ted Petrou
quelle
Ist es möglich, die Standardrotation für alle Diagramme zu ändern? Ich denke, ich muss das bei jeder Handlung ändern, die ich mache.
Chogg
42

Eine einfache Lösung, die ein Schleifen über die Ticklabes vermeidet, ist die einfache Verwendung

fig.autofmt_xdate()

Dieser Befehl dreht die xaxis-Beschriftungen automatisch und passt ihre Position an. Die Standardwerte sind ein Drehwinkel von 30 ° und die horizontale Ausrichtung "rechts". Sie können aber im Funktionsaufruf geändert werden

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

Das zusätzliche bottomArgument entspricht der Einstellung plt.subplots_adjust(bottom=bottom), mit der die Polsterung der unteren Achsen auf einen größeren Wert gesetzt werden kann, um die gedrehten Ticklabels aufzunehmen.

Im Grunde haben Sie hier alle Einstellungen, die Sie benötigen, um eine schöne Datumsachse in einem einzigen Befehl zu haben.

Ein gutes Beispiel finden Sie auf der matplotlib-Seite.

Wichtigkeit von BeErnest
quelle
Gute Antwort! Vielen Dank!
Abramodj
Können Sie den Parameter 'rotationsmodus' über diese Funktion steuern?
TheoryX
1
@TheoryX Nein. Wenn Sie einen Rotationsmodus benötigen, müssen Sie entweder den plt.setpAnsatz verwenden oder die Ticks durchlaufen (was im Wesentlichen derselbe ist).
ImportanceOfBeingErnest
Ja, es ist nur dann eine einfache Lösung, wenn die x-Achse am unteren Rand des Diagramms vorhanden ist. twinxBefindet sich die x-Achse jedoch oben im Diagramm oder sind zwei x-Achsen ( ) vorhanden, wird die Ausrichtung zwischen Ticks und Tick-Beschriftungen verzerrt. In diesem Fall ist die Lösung von Ted Petrou besser.
Śubham
@ Śubham Richtig. Dies ist hauptsächlich eine Abkürzung zu allen anderen Lösungen für den häufigsten Fall.
ImportanceOfBeingErnest
18

Eine weitere Möglichkeit , anzuwenden horizontalalignmentund rotationzu jedem Tick Label macht eine forSchleife über die Strichbeschriftungen Sie ändern möchten:

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

Geben Sie hier die Bildbeschreibung ein

Und hier ist ein Beispiel, wenn Sie die Position von Haupt- und Nebenzecken steuern möchten:

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

Geben Sie hier die Bildbeschreibung ein

Pablo Reyes
quelle
1
Dies funktionierte für mich auf matplotlib v1.5.1 (ich bin bei der Arbeit auf einer Legacy-Version von matplotlib festgefahren, frage nicht warum)
Eddy
Ich habe gerade mit Python3.6 und Matplotlib v2.2.2 getestet und es funktioniert auch.
Pablo Reyes
2

Einfach benutzen

ax.set_xticklabels(label_list, rotation=45)
Gizzmole
quelle