เห็บวันที่และการหมุนใน matplotlib


175

ฉันมีปัญหาในการพยายามทำเครื่องหมายวันที่ของฉันหมุนใน matplotlib โปรแกรมตัวอย่างขนาดเล็กอยู่ด้านล่าง ถ้าฉันพยายามหมุนเห็บในตอนท้ายเห็บจะไม่หมุน หากฉันพยายามหมุนเห็บตามที่แสดงใต้ความคิดเห็น 'ขัดข้อง' แล้ว matplot lib หยุดทำงาน

สิ่งนี้จะเกิดขึ้นหากค่า x เป็นวันที่เท่านั้น ถ้าฉันแทนที่ตัวแปรdatesกับตัวแปรtในการเรียกร้องให้avail_plotการโทรที่ทำงานอยู่ภายในเพียงแค่ปรับxticks(rotation=70)avail_plot

ความคิดใด ๆ

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
การสร้างพล็อตที่มีวันที่บนแกน x นั้นเป็นงานที่เหมือนกัน - ความอัปยศที่มีไม่สมบูรณ์มากขึ้น
alexw

ฉันสงสัยว่านี่ไม่ใช่ไฟล์ซ้ำซ้อนของstackoverflow.com/questions/10998621/ อย่างไร
ImportanceOfBeingErnest

คำตอบ:


249

หากคุณต้องการวิธีการที่ไม่ใช่เชิงวัตถุให้เลื่อนplt.xticks(rotation=70)ไปทางขวาก่อนการavail_plotโทรสองครั้งเช่น

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

การตั้งค่าคุณสมบัติการหมุนก่อนที่จะตั้งค่าป้ายชื่อ เนื่องจากคุณมีสองแกนที่นี่plt.xticksทำให้สับสนหลังจากที่คุณแปลงทั้งสองแล้ว เมื่อมาถึงจุดเมื่อplt.xticksไม่ได้ทำอะไรplt.gca()ไม่ได้ทำให้คุณแกนที่คุณต้องการที่จะแก้ไขและอื่น ๆplt.xticksซึ่งทำหน้าที่ในแกนปัจจุบันไม่ได้ไปทำงาน

สำหรับวิธีการเชิงวัตถุที่ไม่ได้ใช้plt.xticksคุณสามารถใช้

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

หลังจากทั้งสองavail_plotสาย ชุดนี้จะทำการหมุนบนแกนที่ถูกต้องโดยเฉพาะ


11
สิ่งที่มีประโยชน์อีกอย่างหนึ่ง: เมื่อคุณโทรplt.setpคุณสามารถตั้งค่าพารามิเตอร์หลายรายการโดยระบุให้เป็นอาร์กิวเมนต์คำหลักเพิ่มเติม horizontalalignmentkwarg เป็นประโยชน์อย่างยิ่งเมื่อคุณหมุนป้ายเห็บ:plt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70, horizontalalignment='right' )
8one6

32
ฉันไม่ชอบวิธีแก้ปัญหานี้เนื่องจากเป็นการผสมผสานแนวทางของ pyplot และ object-oriented คุณสามารถโทรหาax.tick_params(axis='x', rotation=70)ได้ทุกที่
Ted Petrou

3
@TedPetrou คุณหมายถึงอะไรโดย "ผสม" ที่นี่? การplt.setpแก้ปัญหาเป็นเชิงวัตถุอย่างสมบูรณ์ หากคุณไม่ชอบความจริงที่ว่ามีบางอย่างpltอยู่ให้ใช้ from matplotlib.artist import setp; setp(ax.get_xticklabels(), rotation=90)แทน
ImportanceOfBeingErnest

@ImportanceOfBeingErnest บรรทัดแรกของโค้ดที่ใช้plt.xticksคือ pyplot เอกสารเองบอกว่าจะไม่ผสมผสานสไตล์ plt.setpมีความละเอียดมากกว่า แต่ไม่ใช่สไตล์เชิงวัตถุทั่วไปเช่นกัน คำตอบของคุณดีที่สุดแน่นอน โดยพื้นฐานแล้วสิ่งใดก็ตามที่มีฟังก์ชั่นนั้นไม่ได้เป็นวัตถุ ไม่สำคัญว่าคุณจะนำเข้าsetpหรือไม่
Ted Petrou

มากของคำถามที่ใช้รหัส pyplot เช่นyticksและรหัสไม่ได้มีขวานกำหนดไว้ที่ด้านนอกทั้งหมดของavail_plot...
CGE

113

โซลูชันใช้งานได้กับ matplotlib 2.1+

มีวิธีแกนtick_paramsที่สามารถเปลี่ยนคุณสมบัติเห็บได้ มันยังมีอยู่เป็นวิธีการแกนเป็นset_tick_params

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

หรือ

ax.xaxis.set_tick_params(rotation=45)

ขณะที่ทราบด้านการแก้ปัญหาในปัจจุบันผสมอินเตอร์เฟซ stateful (ใช้ pyplot) plt.xticks(rotation=70)กับอินเตอร์เฟซเชิงวัตถุโดยใช้คำสั่ง เนื่องจากรหัสในคำถามใช้วิธีการเชิงวัตถุจึงควรยึดแนวทางดังกล่าวตลอด การแก้ปัญหาจะให้ทางออกที่ชัดเจนกับplt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70 )


เป็นไปได้หรือไม่ที่จะเปลี่ยนการหมุนเริ่มต้นสำหรับแปลงทั้งหมด? ฉันคิดว่าฉันต้องเปลี่ยนสิ่งนี้ในทุกพล็อตที่ฉันทำ
Chogg

42

ทางออกที่ง่ายซึ่งหลีกเลี่ยงการวนซ้ำ ticklabes คือการใช้งาน

fig.autofmt_xdate()

คำสั่งนี้หมุนฉลาก xaxis โดยอัตโนมัติและปรับตำแหน่ง ค่าเริ่มต้นคือมุมการหมุน 30 °และการจัดแนวแนวนอน "ขวา" แต่สามารถเปลี่ยนแปลงได้ในการเรียกใช้ฟังก์ชัน

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

bottomอาร์กิวเมนต์เพิ่มเติมนั้นเทียบเท่ากับการตั้งค่าplt.subplots_adjust(bottom=bottom)ซึ่งช่วยให้สามารถตั้งแกนล่างด้านล่างให้เป็นค่าที่ใหญ่กว่าเพื่อโฮสต์ ticklablab ที่หมุนได้

โดยพื้นฐานแล้วที่นี่คุณมีการตั้งค่าทั้งหมดที่คุณต้องมีแกนวันที่ดีในคำสั่งเดียว

ตัวอย่างที่ดีสามารถพบได้บนหน้า matplotlib


คำตอบที่ดี! ขอบคุณ!
Abramodj

คุณสามารถควบคุมพารามิเตอร์ 'rotation_mode' ผ่านฟังก์ชั่นนี้ได้หรือไม่?
TheoryX

1
@TheoryX ไม่หากคุณต้องการ rotation_mode คุณจำเป็นต้องใช้plt.setpวิธีการหรือวนซ้ำไปตามเห็บ (ซึ่งเป็นหลักเดียวกัน)
ImportanceOfBeingErnest

ใช่มันเป็นทางออกที่ง่ายเฉพาะเมื่อแกน x อยู่ที่ด้านล่างของกราฟ อย่างไรก็ตามเมื่อแกน x อยู่ที่ด้านบนของกราฟหรือเมื่อมีแกน x สองตัว ( twinx) ปรากฏขึ้นจะทำให้เกิดการจัดแนวระหว่างเห็บและเลเบลเห็บ ในกรณีนั้นทางออกของ Ted Petrou ดีกว่า
Śubham

@ Śubhamถูกต้อง นี่เป็นทางลัดส่วนใหญ่สำหรับโซลูชันอื่น ๆ ทั้งหมดสำหรับกรณีที่พบบ่อยที่สุด
ความสำคัญของการ

18

วิธีการที่จะนำไปใช้อีกhorizontalalignmentและrotationแต่ละป้ายเห็บจะทำforห่วงฉลากเห็บที่คุณต้องการเปลี่ยนแปลง:

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

ป้อนคำอธิบายรูปภาพที่นี่

และนี่คือตัวอย่างถ้าคุณต้องการควบคุมตำแหน่งของเห็บหลักและรอง:

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

ป้อนคำอธิบายรูปภาพที่นี่


1
สิ่งนี้ใช้ได้กับฉันใน matplotlib v1.5.1 (ฉันติดอยู่กับรุ่นเก่า ๆ ของ matplotlib ในที่ทำงานอย่าถามว่าทำไม)
Eddy

ฉันเพิ่งทดสอบด้วย python3.6 และ matplotlib v2.2.2 และใช้งานได้เช่นกัน
Pablo Reyes

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.