φυβλαςのβλογ
phyblasのブログ



numpy & matplotlib เบื้องต้น บทที่ ๓๘: การใช้วันและเวลาเป็นค่าในกราฟ
เขียนเมื่อ 2016/06/26 00:44
แก้ไขล่าสุด 2021/09/28 16:42
วันเดือนปีและเวลาถือเป็นปริมาณอย่างหนึ่งที่มีความสำคัญแต่ก็มีความยุ่งยากในการจัดการ

ไพธอนมีมอดูล datetime ซึ่งได้จัดเตรียมออบเจ็กต์ชนิด datetime.time, datetime.date และ datetime.datetime ซึ่งเก็บค่าวันเดือนปีและเวลาในหน่วยต่างๆอย่างเป็นระบบเพื่อให้จัดการง่าย

รายละเอียดได้เขียนเอาไว้แล้วใน https://phyblas.hinaboshi.com/20160621

ใน matplotlib สามารถใช้ข้อมูลจากมอดูล datetime มาเป็นค่าสำหรับวาดกราฟได้ด้วย

เพื่อจะไม่เป็นการอธิบายซ้ำซ้อน เนื้อหาเกี่ยวกับ datetime ทั้งหมดจะอ้างอิงจากบทความเรื่อง datetime ดังนั้นก่อนที่จะอ่านเนื้อหาในบทนี้จำเป็นจะต้องอ่านเนื้อหาตรงนั้นก่อน เพราะใช้ออบเจ็กต์ datetime จากในนั้นเป็นหลัก



การใช้ datetime เป็นข้อมูลในกราฟ
เมื่อมีการใช้ datetime มาเป็นค่าในกราฟ ตำแหน่งเส้นขีดและรูปแบบการแสดงผลจะถูกกำหนดขึ้นโดยอัตโนมัติตามความเหมาะสม

ตัวอย่าง ลองให้ข้อมูลแนวตั้งเป็นข้อมูลชนิด datetime และแกน x เป็นเลขสุ่มซึ่งมีค่าเปลี่ยนแปลงไปตามเวลา
import numpy as np
import
matplotlib.pyplot as plt
import datetime
roem = datetime.datetime(2016,3,21,0,0,0)
x = np.random.uniform(-3,3,120).cumsum()
y = [roem+datetime.timedelta(0,2000*i) for i in range(0,120)]
plt.figure(figsize=[6,7])
ax = plt.axes([0.2,0.05,0.78,0.90])
plt.plot(x,y,'ro-')
plt.show()



จะเห็นว่าเส้นขีดแนวตั้งแสดงเวลาที่ห่างไปเป็นชั่วโมงไปเรื่อยๆแต่ไม่ได้แสดงวันเดือนปีที่ใส่

แต่ถ้าช่วงเวลาห่างกันมากเป็นวันก็จะได้เส้นขีดแบ่งเป็นวัน แล้วก็ไม่ได้แสดงเวลาให้เห็น

เช่นลองดูตัวอย่างนี้ คราวนี้ลองให้เวลาเป็นแกน x บ้าง จึงต้องปรับตัวเลขให้เอียงเล็กน้อยเพื่อไม่ให้ทับกัน
roem = datetime.datetime(2016,3,21,0,0,0)
x = [roem+datetime.timedelta(i) for i in range(0,120)]
y1 = np.random.uniform(-3,3,120).cumsum()
y2 = np.random.uniform(-1,1,120).cumsum()
plt.plot(x,y1,'ro-',mfc='#aa77cc',ms=4)
plt.plot(x,y2,'go-',mfc='#4477ee',ms=4)
plt.xticks(rotation=15)
plt.show()



หรือถ้าหากช่วงห่างกันมากนักก็จะแสดงแต่ปีอย่างเดียว

บางทีรูปแบบที่แสดงนี้อาจไม่เป็นไปตามที่ต้องการ เช่นอาจจะอยากให้แสดงทั้งวันที่และเวลาพร้อมกัน หรืออยากเปลี่ยนวันที่ให้เรียงตาม วัน-เดือน-ปี

แต่เราก็สามารถปรับ รูปแบบและตำแหน่งขีดเอาเองได้ตามที่ต้องการ โดยใช้เมธอดของ xaxis และ yaxis เช่นเดียวกับที่กล่าวถึงในบทที่แล้ว เพียงแต่ว่าในบทนี้ออบเจ็กต์ที่จะใช้จะมาจากอีกมอดูลย่อยหนึ่งใน matplotlib นั่นคือมอดูล matplotlib.dates ซึ่งเป็นมอดูลย่อยที่มีหน้าที่จัดการเกี่ยวกับเวลา



การเปลี่ยนรูปแบบการแสดงผลของวันเวลา
ในการปรับรูปแบบการแสดงผลวันเวลาให้เป็นไปในแบบตามที่ต้องการมีฟังก์ชันที่ สะดวกอยู่คือ matplotlib.dates.DateFormatter ฟังก์ชันนี้จะกำหนดรูปแบบของวันเวลาที่แสดงออกมาโดยวิธีการแบบเดียวกับ ฟังก์ชัน datetime.strftime คือใช้ %d แทนวัน %m แทนเดือน %Y แทนปี เป็นต้น

การใช้งานคือสร้างต้องออบเจ็กต์ matplotlib.dates.DateFormatter โดยใส่อาร์กิวเมนต์เป็นสายอักขระที่ระบุรูปแบบการแสดงผลที่ต้องการ จากนั้นนำมาใช้ในเมธอด set_major_formatter หรือ set_minor_formatter

ตัวอย่าง แสดงในวันเดือนปีในรูปแบบ วัน-เดือน-ปี
import numpy as np
import
matplotlib.pyplot as plt
import matplotlib as mpl
import datetime
roem = datetime.datetime(2016,3,21,0,0,0)
x = np.random.uniform(-2,3,120).cumsum()
y = [roem+datetime.timedelta(i) for i in range(0,120)]
plt.figure(figsize=[6,7])
ax = plt.axes([0.18,0.05,0.79,0.92])
plt.plot(x,y,'o-',mfc='#bb66cc')
dfmt = mpl.dates.DateFormatter('%d-%m-%Y')
ax.yaxis.set_major_formatter(dfmt)



แต่ถ้าหากต้องการจะตั้งรูปแบบการแสดงผลให้อิสระกว่านั้นก็อาจเป็นการดีกว่าหากใช้ matplotlib.ticker.FuncFormatter ซึ่งแนะนำไปในบทที่แล้ว

เพียงแต่ต้องระวังว่า ค่า datetime เวลาที่อยู่ในระบบของ matplotlib นั้นที่จริงแล้วจะถูกแปลงให้อยู่ในรูปของตัวเลขจำนวนจริงซึ่งเป็นค่าจำนวนวันซึ่งนับไล่ตั้งแต่วันที่ 1 ม.ค. ปี ค.ศ. 1

เราสามารถเปลี่ยนเป็นค่าตัวเลขนั้นได้โดยใช้ฟังก์ชัน matplotlib.dates.date2num เช่น
print(mpl.dates.date2num(datetime.datetime(1,1,1,0,0,0))) # ได้ 1.0
print(mpl.dates.date2num(datetime.datetime(2016,6,21,0,0,0))) # ได้ 736136.0

ในทางกลับกันตัวเลขนี้สามารถแปลงกลับเป็น datetime ได้โดยฟังก์ชัน matplotlib.dates.num2date เช่น
print(mpl.dates.num2date(1)) # ได้ 0001-01-01 00:00:00+00:00
print(mpl.dates.num2date(700000.1)) # ได้ 1917-07-15 02:24:00+00:00

หากลองวาดกราฟโดยใช้ datetime กับ matplotlib.ticker.FuncFormatter ค่าที่ถูกส่งไปเป็นตัวแปรตัวแรกจะถูกแปลงเป็นตัวเลขเหมือนกับใช้ matplotlib.dates.date2num

ตัวอย่าง
def fy(k,p):
    return '%.3f'%k
roem = datetime.datetime(2016,6,21,0,0,0)
x = np.random.uniform(-1,3,120).cumsum()
y = [roem+datetime.timedelta(0,500*i) for i in range(0,120)]
plt.figure(figsize=[6,7])
ax = plt.axes([0.18,0.05,0.79,0.92])
plt.plot(x,y,'o-',mfc='#bb6622')
ax.yaxis.set_major_formatter(mpl.ticker.FuncFormatter(fy))
plt.show()



วันที่ 21 มิ.ย. 2016 มีค่าเท่ากับ 736136 ส่วนทศนิยมก็คือเวลาเป็นชั่วโมงในวันนั้น

ในเมื่อค่าที่ถูกส่งมาอยู่ในรูปแบบตัวเลขอย่างนี้ เราสามารถแปลงกลับเป็น datetime อีกทีได้ด้วยฟังก์ชัน matplotlib.dates.num2date จากนั้นก็นำไปใช้ทำอะไรได้ง่าย

ตัวอย่างเช่น ลองทำให้แสดงผลค่าชั่วโมงเป็นแบบไทยๆ
def mong(k,p):
    c = mpl.dates.num2date(k).hour
    if(c==0):
        return u'เที่ยงคืน'
    if(c>0 and c<6):
        return u'ตี %d'%c
    if(c>=6 and c<12):
        return u'%d โมงเช้า'%c
    if(c==12):
        return u'เที่ยง'
    if(c==13):
        return u'บ่ายโมง'
    if(c==14 or c==15):
        return u'บ่าย %d โมง'%(c-12)
    if(c>15 and c<=18):
        return u'%d โมงเย็น'%(c-12)
    if(c>18):
        return u'%d ทุ่ม'%(c-18)
roem = datetime.datetime(2016,6,21,0,0,0)
y = [roem+datetime.timedelta(0,1400*i) for i in range(0,60)]
plt.figure(figsize=[6,7])
ax = plt.axes([0.18,0.05,0.79,0.92])
for i in range(15):
    x = np.random.uniform(-2,3,60).cumsum()
    plt.plot(x,y,'.-')
plt.yticks(fontname='Tahoma',fontsize=16)
ax.yaxis.set_major_formatter(mpl.ticker.FuncFormatter(mong))
plt.show()





การปรับตำแหน่งขีดของเวลา
ในการกำหนดตำแหน่งขีดของเวลาให้เป็นไปตามที่ต้องการนั้นสามารถทำได้โดยใช้เมธอด set_major_locator หรือ set_minor_locator

สำหรับข้อมูลที่เป็นวันเวลาแล้วค่าที่เหมาะจะใส่ในเมธอดนี้ก็คือออบเจ็กต์ที่เป็น คลาสย่อยของคลาสที่ชื่อ matplotlib.dates.DateLocator ซึ่งเป็นคลาสของตัวกำหนดขีดที่เป็นเวลาในรูปแบบต่างๆหลายแบบ ซึ่งได้แก่
YearLocator
MonthLocator
WeekdayLocator
DayLocator
HourLocator
MinuteLocator
SecondLocator
MicrosecondLocator

YearLocator จะกำหนดให้วางขีดโดยนับเป็นปีๆไป โดยที่จำนวนปีต่อขีดกำหนดโดยอาร์กิวเมนต์ตัวแรก แล้วก็กำหนดวันที่ที่จะทำการขีดในแต่ละปีโดยคีย์เวิร์ด month และ day

ตัวอย่าง ลองให้ตั้งขีดทุกวันที่ 29 กุมภาพันธ์ของทุก ๔ ปี ซึ่งเป็นปีอธิกสุรทิน (ปีที่มี ๓๖๖ วัน)
roem = datetime.datetime(2016,6,21,0,0,0)
y = [roem-datetime.timedelta(400*i) for i in range(0,60)]
plt.figure(figsize=[6,7])
ax = plt.axes([0.18,0.05,0.79,0.92])
for i in range(30):
    x = np.random.uniform(-1,1,60).cumsum()
    plt.plot(x,y,'.-')
ax.yaxis.set_major_formatter(mpl.dates.DateFormatter('%d-%m-%Y'))
ax.yaxis.set_major_locator(mpl.dates.YearLocator(4,month=2,day=29))
plt.show()



MonthLocator จะเป็นการขีดทุกเดือนที่กำหนดในแต่ละปี โดยใส่เดือนที่กำหนดเป็นอาร์กิวเมนต์ตัวแรกจะกำหนดเดือนเดียวก็ได้หรือถ้าจะ กำหนดให้ขีดหลายเดือนในแต่ละปีก็ใส่ในรูปลิสต์

จากนั้นอาร์กิวเมนต์ตัวที่สองจะเป็นตัวกำหนดว่าจะให้ขีดวันที่เท่าไหร่ในเดือน จะกำหนดเป็นวันเดียวหรือหลายวันเป็นลิสต์ก็ได้

ตัวอย่างเช่น ถ้าต้องการให้ขีกทุกวันที่ 5 ของเดือนกรกฎาคมและธันวาคม
roem = datetime.datetime(2016,6,21,0,0,0)
y = [roem-datetime.timedelta(50*i) for i in range(0,60)]
plt.figure(figsize=[6,7])
ax = plt.axes([0.18,0.05,0.79,0.92])
for i in range(30):
    x = np.random.uniform(-1,1,60).cumsum()
    plt.plot(x,y,'.-')
ax.yaxis.set_major_formatter(mpl.dates.DateFormatter('%d-%m-%Y'))
ax.yaxis.set_major_locator(mpl.dates.MonthLocator([7,12],5))
plt.show()



นอกจากนี้ยังอาจกำหนดให้ขีดโดยมีระยะห่างเป็นจำนวนเดือนเท่าๆกันได้โดยการกำหนด เป็นอาร์กิวเมนต์ตัวที่สามแทน ส่วนอาร์กิวเมนต์ตัวแรกก็ปล่อยเป็น None

ตัวอย่าง ลองเอาตัวอย่างที่แล้วมาแก้บรรทัด set_major_locator ให้ขีดทุกวันที่ 10 ของทุก ๕ เดือน
ax.yaxis.set_major_locator(mpl.dates.MonthLocator(None,10,5))



กรณีนี้อาจเขียนในรูปของคีย์เวิร์ดแทนได้ โดยเลขวันใส่เป็น bymonthday= และจำนวนเดือนที่เว้นใส่เป็น interval= แบบนี้ก็จะไม่ต้องใส่ None ขึ้นไว้ข้างหน้า

กล่าวคือ แก้บรรทัด set_major_locator เป็น
ax.yaxis.set_major_locator(mpl.dates.MonthLocator(bymonthday=10,interval=5))

WeekdayLocator จะกำหนดให้ขีดทุกวันใดวันหนึ่งในสัปดาห์ อาร์กิวเมนต์ตัวแรกเป็นตัวกำหนดว่าจะขีดวันไหนในสัปดาห์ โดยวันจันทร์เป็น 0 อาทิตย์เป็น 6 จะใส่ตัวเดียวหรือใส่เป็นลิสต์ของตัวเลขก็ได้ ส่วนอาร์กิวเมนต์ตัวที่สองจะกำหนดว่าจะเว้นกี่สัปดาห์ต่อขีด

ตัวอย่าง กำหนดให้ขีดทุกวันอังคาร (เลข 1) โดยเว้นช่วง 2 สัปดาห์
roem = datetime.datetime(2016,6,21,0,0,0)
y = [roem-datetime.timedelta(3*i) for i in range(0,60)]
plt.figure(figsize=[6,7])
ax = plt.axes([0.18,0.05,0.79,0.92])
for i in range(30):
    x = np.random.uniform(-1,1,60).cumsum()
    plt.plot(x,y,'.-')
plt.yticks(fontsize=21)
ax.yaxis.set_major_formatter(mpl.dates.DateFormatter('%d/%m'))
ax.yaxis.set_major_locator(mpl.dates.WeekdayLocator(1,2))
plt.show()



สำหรับ DayLocator นั้นจะขีดทุกวันที่ที่กำหนดในแต่ละเดือน วันที่ต้องการขีดนั้นให้กำหนดเป็นลิสต์ ไม่จำเป็นต้องเว้นห่างเท่ากัน

ตัวอย่าง
roem = datetime.datetime(2016,6,21,0,0,0)
y = [roem-datetime.timedelta(i*2.5) for i in range(0,60)]
plt.figure(figsize=[6,7])
ax = plt.axes([0.18,0.05,0.79,0.92])
for i in range(30):
    x = np.random.uniform(-1,1,60).cumsum()
    plt.plot(x,y,'.-')
ax.yaxis.set_major_formatter(mpl.dates.DateFormatter('%d/%m/%Y'))
ax.yaxis.set_major_locator(mpl.dates.DayLocator([7,14,21],2))
plt.show()



นอกจากกำหนดเป็นรายการของเลขวันแล้วเราอาจกำหนดเป็นระยะเว้นเท่าๆกันโดยกำหนดจำนวน วันได้ ทำได้โดยใส่จำนวนวันที่จะเว้นช่วงไว้ในอาร์กิวเมนต์ตัวที่สอง ส่วนตัวแรกก็ปล่อย None ไป

เช่นลองแก้บรรทัด set_major_formatter เป็น
ax.yaxis.set_major_locator(mpl.dates.DayLocator(None,8))



แบบนี้ก็จะได้ขีดที่เว้นระยะห่างเท่าๆกันทีละ ๘ วัน

หรือจะใส่ในรูปคีย์เวิร์ดเป็น interval= โดยไม่ใส่อาร์กิวเมนต์ตัวแรกก็ได้
ax.yaxis.set_major_locator(mpl.dates.DayLocator(interval=8))

ส่วน HourLocator ก็ทำนองเดียวกับ DayLocator คือกำหนดเวลาชั่วโมงภายในวัน หรือจะกำหนดระยะห่างเป็นชั่วโมงก็ได้

ตัวอย่าง
roem = datetime.datetime(2016,6,21,0,0,0)
y = [roem+datetime.timedelta(0,i*8000) for i in range(0,60)]
plt.figure(figsize=[6,7])
ax = plt.axes([0.18,0.05,0.79,0.92])
for i in range(30):
    x = np.random.uniform(-1,1,60).cumsum()
    plt.plot(x,y,'.-')
ax.yaxis.set_major_formatter(mpl.dates.DateFormatter('%I:%M'))
ax.yaxis.set_major_locator(mpl.dates.HourLocator([1,6,11]))
plt.show()



MinuteLocator ก็เช่นกัน กำหนดนาทีภายในชั่วโมง หรือกำหนดระยะห่างเป็นนาที

ตัวอย่าง
def nalika(k,p):
    c = mpl.dates.num2date(k)
    return u'%d:%02d น.'%(c.hour,c.minute)
roem = datetime.datetime(2016,6,21,0,0,0)
y = [roem+datetime.timedelta(0,i*900) for i in range(0,60)]
plt.figure(figsize=[6,7])
ax = plt.axes([0.18,0.05,0.79,0.92])
for i in range(30):
    x = np.random.uniform(-1,1,60).cumsum()
    plt.plot(x,y,'.-')
plt.yticks(fontname='Tahoma',fontsize=16)
ax.yaxis.set_major_formatter(mpl.ticker.FuncFormatter(nalika))
ax.yaxis.set_major_locator(mpl.dates.MinuteLocator(None,50))
plt.show()





อ้างอิง


<< บทที่แล้ว     บทถัดไป >>
หน้าสารบัญ


-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python >> matplotlib

ไม่อนุญาตให้นำเนื้อหาของบทความไปลงที่อื่นโดยไม่ได้ขออนุญาตโดยเด็ดขาด หากต้องการนำบางส่วนไปลงสามารถทำได้โดยต้องไม่ใช่การก๊อปแปะแต่ให้เปลี่ยนคำพูดเป็นของตัวเอง หรือไม่ก็เขียนในลักษณะการยกข้อความอ้างอิง และไม่ว่ากรณีไหนก็ตาม ต้องให้เครดิตพร้อมใส่ลิงก์ของทุกบทความที่มีการใช้เนื้อหาเสมอ

目次

日本による名言集
モジュール
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
機械学習
-- ニューラル
     ネットワーク
javascript
モンゴル語
言語学
maya
確率論
日本での日記
中国での日記
-- 北京での日記
-- 香港での日記
-- 澳門での日記
台灣での日記
北欧での日記
他の国での日記
qiita
その他の記事

記事の類別



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  記事を検索

  おすすめの記事

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

月別記事

2023年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2022年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2021年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2020年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2019年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

もっと前の記事

ไทย

日本語

中文