Kutu dan rotasi tanggal di matplotlib

175

Saya mengalami masalah saat mencoba agar kutu tanggal saya diputar di matplotlib. Contoh program kecil ada di bawah ini. Jika saya mencoba memutar kutu di bagian akhir, kutu tidak akan diputar. Jika saya mencoba memutar tanda centang seperti yang ditunjukkan di bawah komentar 'crash', maka matplot lib crash.

Ini hanya terjadi jika nilai-x adalah tanggal. Jika saya mengganti variabel datesdengan variabel tdalam panggilan ke avail_plot, xticks(rotation=70)panggilan berfungsi dengan baik di dalamnya avail_plot.

Ada ide?

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
sumber
3
Membuat plot dengan tanggal pada sumbu x adalah tugas yang umum - memalukan tidak ada contoh yang lebih lengkap di luar sana.
alexw
Saya bertanya-tanya apakah ini bukan duplikat dari stackoverflow.com/questions/10998621/…
ImportanceOfBeingErnest

Jawaban:

249

Jika Anda lebih suka pendekatan yang tidak berorientasi objek, pindah plt.xticks(rotation=70)ke kanan sebelum dua avail_plotpanggilan, mis

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

Ini mengatur properti rotasi sebelum mengatur label. Karena Anda memiliki dua sumbu di sini, plt.xticksmenjadi bingung setelah Anda membuat dua plot. Pada titik ketika plt.xtickstidak melakukan apa-apa, plt.gca()tidak tidak memberikan sumbu Anda ingin memodifikasi, dan sebagainya plt.xticks, yang bekerja pada sumbu saat ini, tidak akan bekerja.

Untuk pendekatan berorientasi objek yang tidak menggunakan plt.xticks, Anda dapat menggunakan

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

setelah dua avail_plotpanggilan. Ini menetapkan rotasi pada sumbu yang benar secara khusus.

cge
sumber
11
Satu hal praktis lainnya: saat Anda menelepon, plt.setpAnda dapat mengatur beberapa parameter dengan menetapkannya sebagai argumen kata kunci tambahan. yang horizontalalignmentkwarg sangat berguna ketika Anda memutar label centang:plt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70, horizontalalignment='right' )
8one6
32
Saya tidak suka solusi ini karena menggabungkan pendekatan pyplot dan berorientasi objek. Anda bisa menelepon ax.tick_params(axis='x', rotation=70)di mana saja.
Ted Petrou
3
@TedPetrou Apa yang Anda maksud dengan "mencampur" di sini? yang plt.setpsolusi benar-benar berorientasi objek. Jika Anda tidak menyukai kenyataan bahwa ada beberapa pltdi dalamnya, gunakan from matplotlib.artist import setp; setp(ax.get_xticklabels(), rotation=90)saja.
ImportanceOfBeingErnest
@ImportanceOfBeingErnest Baris pertama menggunakan kode plt.xticksyang pyplot. Dokumentasi itu sendiri mengatakan untuk tidak mencampur gaya. plt.setplebih verbose tetapi bukan gaya berorientasi objek yang khas baik. Jawaban Anda pasti yang terbaik di sini. Pada dasarnya, apa pun dengan fungsi tidak berorientasi objek. Tidak masalah apakah Anda mengimpor setpatau tidak.
Ted Petrou
Sebagian besar kode pertanyaan menggunakan pyplot, seperti yticks, dan kode tersebut tidak memiliki kapak yang didefinisikan di luar avail_plot...
cge
114

Solusi berfungsi untuk matplotlib 2.1+

Ada metode sumbu tick_paramsyang dapat mengubah properti centang. Itu juga ada sebagai metode poros sebagaiset_tick_params

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

Atau

ax.xaxis.set_tick_params(rotation=45)

Sebagai catatan, solusi saat ini mencampur antarmuka stateful (menggunakan pyplot) dengan antarmuka berorientasi objek dengan menggunakan perintah plt.xticks(rotation=70). Karena kode dalam pertanyaan menggunakan pendekatan berorientasi objek, yang terbaik adalah tetap menggunakan pendekatan itu secara keseluruhan. Solusi ini memang memberikan solusi eksplisit yang baikplt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70 )

Ted Petrou
sumber
Apakah mungkin untuk mengubah rotasi default untuk semua plot? Saya pikir saya perlu mengubah ini pada setiap plot yang saya buat.
Chogg
42

Solusi mudah yang menghindari perulangan di atas ticklabes adalah dengan hanya menggunakan

fig.autofmt_xdate()

Perintah ini secara otomatis memutar label xaxis dan menyesuaikan posisinya. Nilai standarnya adalah sudut rotasi 30 ° dan perataan horizontal "kanan". Tetapi mereka dapat diubah dalam pemanggilan fungsi

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

bottomArgumen tambahan setara dengan pengaturan plt.subplots_adjust(bottom=bottom), yang memungkinkan untuk mengatur sumbu sumbu bawah ke nilai yang lebih besar untuk meng-host label centang yang diputar.

Jadi pada dasarnya di sini Anda memiliki semua pengaturan yang Anda butuhkan untuk memiliki sumbu tanggal yang bagus dalam satu perintah.

Contoh yang baik dapat ditemukan di halaman matplotlib.

ImportanceOfBeingErnest
sumber
Jawaban bagus! Terima kasih!
Abramodj
dapatkah Anda mengontrol parameter 'rotasi_mode' melalui fungsi ini?
TheoryX
1
@TheoryX No Jika Anda perlu rotation_mode, Anda perlu baik menggunakan plt.setppendekatan, atau loop atas kutu (yang pada dasarnya sama).
ImportanceOfBeingErnest
Ya itu adalah solusi yang mudah hanya ketika sumbu x hadir di bagian bawah grafik. Namun, ketika sumbu x ada di bagian atas grafik atau ketika ada dua sumbu x ( twinx), itu mengacaukan keselarasan antara kutu dan label kutu. Dalam hal ini, solusi oleh Ted Petrou lebih baik.
Śubham
@ Śubham Benar. Ini terutama merupakan jalan pintas ke semua solusi lain untuk kasus yang paling umum.
ImportanceOfBeingErnest
18

Cara lain untuk menerapkan horizontalalignmentdan rotationuntuk setiap label centang adalah melakukan forperulangan pada label centang yang ingin Anda ubah:

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

masukkan deskripsi gambar di sini

Dan di sini adalah contoh jika Anda ingin mengontrol lokasi kutu besar dan kecil:

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

masukkan deskripsi gambar di sini

Pablo Reyes
sumber
1
Ini bekerja untuk saya di matplotlib v1.5.1 (saya terjebak pada versi lama matplotlib di tempat kerja, jangan tanya kenapa)
Eddy
Saya baru saja diuji dengan python3.6 dan matplotlib v2.2.2 dan itu berfungsi juga.
Pablo Reyes
2

Cukup gunakan

ax.set_xticklabels(label_list, rotation=45)
gizzmole
sumber