φυβλαςのβλογ
บล็อกของ phyblas



numpy & matplotlib เบื้องต้น บทที่ ๓๗: การจัดรูปแบบการแสดงผลของขีดบอกค่าบนแกน
เขียนเมื่อ 2016/06/26 00:02
แก้ไขล่าสุด 2022/07/21 20:41
ในบทที่ ๙ ได้พูดถึงการจัดการอะไรต่างๆมากมายบนแกนในเบื้องต้น ซึ่งรวมถึงการแก้ไขในส่วนของขีดบอกค่าบนแกนด้วย สำหรับบทนี้จะพูดถึงต่อในส่วนของการปรับในรายละเอียดขึ้นไปอีก



การแบ่งขีดเป็นขีดหลักและขีดรอง
หากมองไปที่ไม้บรรทัดจะเห็นว่าประกอบไปด้วยเส้นขีดมากมายซึ่งแสดงบอกค่าเส้นขีดนั้นไม่ได้ยาวเท่ากันทั้งหมด แต่บางขีดยาวบางขีดสั้น เช่นเส้นที่บอกหน่วยเซนติเมตรอาจทั้งยาวและหนาว่าเส้นที่บอกหน่วยมิลลิเมตร

กราฟใน matplotlib เองก็สามารถทำให้ขีดบอกบนแกนแบ่งออกเป็นขีดหลักซึ่งยาวกว่าและขีดย่อยซึ่งสั้นกว่าแบบนั้นได้เช่นกัน

ในเบื้องต้นเราสามารถตั้งให้แสดงขีดย่อยขึ้นมาได้โดยใช้ฟังก์ชัน plt.minorticks_on เช่น
import numpy as np
import
matplotlib.pyplot as plt
ax = plt.axes(xlim=[-10,10],ylim=[-8,8],aspect=1,facecolor='#ddffdd')
plt.plot(np.array([-9,0,9]),np.array([-7,2,7]),'o-r')
plt.minorticks_on()
plt.show()



จะเห็นว่ามีขีดเล็กๆสั้นๆปรากฏขึ้นมาจำนวนหนึ่งระหว่างขีดหลัก เพียงแต่ว่าอาจเล็กมาจนมองแทบไม่เห็น ดังนั้นอาจต้องทำการตกแต่งเพิ่มเติมสักหน่อย

การปรับแต่งขีดย่อยทำได้โดยใช้ฟังก์ชัน plt.tick_params ซึ่งได้อธิบายไปในบทที่ ๙ แล้ว เพียงแต่โดยปกติแล้วถ้าเราไม่ได้ระบุอะไรจะเป็นการปรับแต่งขีดหลัก แต่เราสามารถปรับแต่งขีดย่อได้โดยเพิ่มคีย์เวิร์ด which ลงไป

which='major' คือปรับแค่ขีดหลัก (นี่เป็นค่าตั้งต้น)
which='minor' คือปรับแค่ขีดย่อย
which='both' คือปรับทั้งขีดหลักและขีดย่อย

ตัวอย่าง ลองพิมพ์ตามนี้เพิ่มเข้าไปจากตัวอย่างด้านบนจะเห็นความเปลี่ยนแปลงของทั้งขีดหลักและขีดย่อย
plt.tick_params(colors='b',length=30,direction='inout')
plt.tick_params(which='both',width=3)
plt.tick_params(which='minor',colors='m',length=10,top=1)





การจัดตำแหน่งขีดบอกค่า
จะเห็นว่าโดยรูปแบบตั้งต้นแล้วเส้นขีดย่อยถูกขีดเพิ่มตามความเหมาะสมโดยกำหนดอัตโนมัติ และไม่มีตัวเลข

ที่จริงตำแหน่งเส้นขีดหลักเองก็ถูกกำหนดขึ้นมาโดยอัตโนมัติเหมือนกัน แต่เราสามารถปรับแก้ได้ด้วยการใช้ plt.xticks ดังที่กล่าวไปในบทที่ ๗ แต่วิธีนั้นเราต้องเตรียมค่าขีดทั้งหมดที่ต้องการไปใส่เอง อีกทั้งไม่สามารถใช้กับเส้นขีดรองได้

มีอีกวิธีหนึ่งที่สามารถปรับตำแหน่งเส้นขีด ซึ่งแม้ว่าจะเข้าใจยากกว่าแต่เป็นวิธีที่สะดวกและยืดหยุ่นกว่า และยังใช้ได้กับทั้งเส้นขีดหลักและเส้นขีดรอง นั่นคือการตั้ง set_major_locator และ set_minor_locator ที่แอตทริบิวต์ xaxis และ yaxis ของ axes

ค่าที่ต้องใส่ใน set_major_locator และ set_minor_locator นั้นต้องเป็นออบเจ็กต์ที่อยู่ในซับคลาสของ matplotlib.ticker.Locator ซึ่งอยู่ในมอดูลย่อยอันหนึ่งของ matplotlib ที่ชื่อ ticker

ซับคลาสของ Locator มีอยู่หลายชนิด เช่น
MultipleLocator วางขีดให้เว้นระยะห่างเท่ากันตามระยะที่กำหนด
LinearLocator วางขีดเป็นระยะห่างเท่ากันตามจำนวนที่กำหนด
MaxNLocator วางขีดให้ห่างเท่ากันโดยแบ่งเป็นจำนวนไม่เกินที่กำหนด โดยเลขจะถูกวางให้เป็นเลขลงตัว
IndexLocator วางขีดเฉพาะในขอบเขตที่มีจุดข้อมูลบนกราฟ
FixedLocator วางขีดในตำแหน่งที่กำหนด
LogLocator วางขีดตามตำแหน่งค่าเลขยกกำลัง
AutoMinorLocator ใช้กับขีดย่อย วางขีดย่อยโดยแบ่งย่อยจากขีดหลักตามจำนวนที่กำหนด
NullLocator ไม่วาดขีด

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

ตัวอย่าง
import numpy as np
import
matplotlib.pyplot as plt
import matplotlib as mpl
ax = plt.axes(xlim=[-10,10],ylim=[-8,8],aspect=1,facecolor='#ddffdd')
plt.plot(np.array([-1,0,5]),np.array([-7,2,6]),'o-m')
plt.plot(np.array([-8,-1,1]),np.array([-6,1,7]),'o-r')
plt.minorticks_on()
plt.tick_params(colors='b',length=20)
plt.tick_params(which='both',width=3)
plt.tick_params(which='minor',colors='c',length=10)
ax.xaxis.set_major_locator(mpl.ticker.MultipleLocator(6))
ax.xaxis.set_minor_locator(mpl.ticker.MultipleLocator(2))
ax.yaxis.set_major_locator(mpl.ticker.MultipleLocator(5))
ax.yaxis.set_minor_locator(mpl.ticker.MultipleLocator(1.5))
plt.show()



ส่วน LinearLocator จะคล้ายกับ MultipleLocator ตรงที่เป็นการแบ่งเท่าๆกันเหมือนกัน แต่จะกำหนดจำนวนขีดเอา

ลองแก้ ๔ บรรทัดที่ใช้ MultipleLocator เป็น
ax.xaxis.set_major_locator(mpl.ticker.LinearLocator(6))
ax.xaxis.set_minor_locator(mpl.ticker.LinearLocator(36))
ax.yaxis.set_major_locator(mpl.ticker.LinearLocator(5))
ax.yaxis.set_minor_locator(mpl.ticker.LinearLocator(16))



สำหรับ MaxNLocator จะกำหนดขีดอัตโนมัติตามความเหมาะสมโดยเราสามารถกำหนดจำนวนช่องแบ่งสูงสุดได้

อาร์กิวเมนต์ที่ต้องใส่คือจำนวนช่องแบ่งสูงสุด และนอกจากนี้ยังอาจใส่คีย์เวิร์ดเพิ่มเติม ได้แก่
integer ถ้าเป็น 1 ขีดจะวางเฉพาะที่เป็นค่าจำนวนเต็มเท่านั้น
symmetric ถ้าเป็น 1 ขีดจะวางโดยมีสมมาตรบวกลบ
prune กำหนดว่าจะเอาขีดที่ชิดขอบออกหรือไม่ ถ้าเป็น upper จะละขีดค่าฝั่งมากสุด lower จะละขีดค่าฝั่งน้อยสุด both จะละทั้งสองฝั่ง

ลองแก้เป็น
ax.xaxis.set_major_locator(mpl.ticker.MaxNLocator(6))
ax.xaxis.set_minor_locator(mpl.ticker.MaxNLocator(36,integer=1,prune='both'))
ax.yaxis.set_major_locator(mpl.ticker.MaxNLocator(5,prune='upper'))
ax.yaxis.set_minor_locator(mpl.ticker.MaxNLocator(35,prune='lower'))



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

ตัวอย่าง
ax.xaxis.set_major_locator(mpl.ticker.IndexLocator(5,1))
ax.xaxis.set_minor_locator(mpl.ticker.IndexLocator(2,0))
ax.yaxis.set_major_locator(mpl.ticker.IndexLocator(3,2))
ax.yaxis.set_minor_locator(mpl.ticker.IndexLocator(1,0))



สำหรับ FixedLocator นั้นจะวาดขีดในตำแหน่งตามที่ระบุ คล้ายกับการใช้วิธีการกำหนดตำแหน่งขีดโดย set_xticks และ set_yticks

AutoMinorLocator จะใช้กับ set_minor_locator เพื่อกำหนดขีดย่อยให้แบ่งย่อยขีดหลักตามจำนวนที่กำหนด ขีดย่อยจะไม่ไปซ้อนทับกับขีดหลัก

ตัวอย่าง
ax.xaxis.set_major_locator(mpl.ticker.MultipleLocator(7))
ax.xaxis.set_minor_locator(mpl.ticker.AutoMinorLocator(4))
ax.yaxis.set_major_locator(mpl.ticker.LinearLocator(9))
ax.yaxis.set_minor_locator(mpl.ticker.AutoMinorLocator(5))





การจัดรูปแบบตัวเลขบนขีดบอกค่า
ปกติแล้วรูปแบบการแสดงผลของตัวเลขจะถูกกำหนดอย่างอัตโนมัติ แต่เราสามารถกำหนดขึ้นเองได้โดยเมธอด set_major_formatter กับ set_minor_formatter บน xaxis และ yaxis

ออบเจ็กต์ที่ต้องใช้กับ set_major_formatter และ set_minor_formatter ต้องเป็นซับคลาสของคลาส matplotlib.ticker.Formatter ซึ่งอยู่ภายใน matplotlib.ticker เช่นกัน

ออบเจ็กต์เหล่านั้นมีหลายตัว แต่ที่จะพูดถึงในที่นี้ได้แก่
FormatStrFormatter กำหนดรูปแบบด้วย %d %f %e %s
FuncFormatter กำหนดรูปแบบด้วยฟังก์ชัน
FixedFormatter กำหนดตัวหนังสือบนขีดทีละขีดเอาเองโดยใช้ลิสต์
NullFormatter ไม่เขียนตัวเลข

FormatStrFormatter เป็นการกำหนดรูปแบบการแสดงผลของตัวเลขด้วย %d, %f, %e, ฯลฯ รายละเอียดเกี่ยวกับความหมายของการเขียนแบบนี้อ่านได้ในเนื้อหาภาษาไพธอนเบื้องต้นบทที่ ๑๐

อาร์กิวเมนต์ที่ต้องใส่คือสายอักขระที่ประกอบด้วย %d, %f, %e อยู่ในนั้น ซึ่งค่าตัวเลขบอกค่าจะถูกแทนลงตรงนั้น และสามารถเพิ่มเติมอะไรอย่างอื่นได้ตามที่ต้องการ

ตัวอย่าง
plt.figure(figsize=[7,7])
ax = plt.axes([0.25,0.1,0.7,0.85],xlim=[-100,100],ylim=[-0,0.75],facecolor='#ffddff')
plt.scatter(np.random.uniform(-100,100,5000),np.random.uniform(0,0.75,5000),c=np.random.rand(5000),cmap='spring',lw=0)
ax.yaxis.set_major_locator(mpl.ticker.LinearLocator(18))
ax.xaxis.set_major_formatter(mpl.ticker.FormatStrFormatter('%+3d $\\mu m$\n~O~'))
ax.yaxis.set_major_formatter(mpl.ticker.FormatStrFormatter('~%.12f'))
plt.show()



FixedFormatter นั้นจะคล้ายกับการตั้ง set_xticklabels และ set_yticklabels คือวางข้อความหรือตัวเลขบนขีดไล่ตามลิสต์ที่ใส่ลงไป

ในส่วนของเส้นขีดย่อยนั้นโดยตั้งต้นแล้วจะไม่มีตัวเลขบอก นั่นคือเป็น NullFormatter แต่ถ้ามีการตั้ง set_minor_formatter ก็จะทำให้มีตัวเลขขึ้นมาได้ และในทางกลับกันก็สามารถทำให้ขีดหลักหายไปได้โดยตั้ง set_major_formatter เป็น NullFormatter

ตัวอย่าง
plt.figure(figsize=[7,7])
ax = plt.axes([0.13,0.06,0.85,0.9],xlim=[0,15],ylim=[-100,50],facecolor='#ffffcc')
plt.minorticks_on()
plt.tick_params(width=3,length=20)
plt.tick_params(which='minor',width=2,length=10)
ax.yaxis.set_major_locator(mpl.ticker.LinearLocator(7))
ax.xaxis.set_minor_locator(mpl.ticker.LinearLocator(16))
ax.yaxis.set_minor_locator(mpl.ticker.AutoMinorLocator(4))
ax.xaxis.set_major_formatter(mpl.ticker.NullFormatter())
ax.xaxis.set_minor_formatter(mpl.ticker.FormatStrFormatter('%d'))
ax.yaxis.set_major_formatter(mpl.ticker.FormatStrFormatter('%d~~'))
ax.yaxis.set_minor_formatter(mpl.ticker.FormatStrFormatter('%.2f'))
plt.plot(np.random.uniform(0,15,1000),np.random.uniform(-100,50,1000),'g--D',ms=4)
plt.show()



หากต้องการปรับค่าให้ยืดหยุ่นที่สุดอาจใช้ FuncFormatter การจะใช้จะต้องสร้างฟังก์ชันขึ้นมาเพื่อใช้เป็นตัวกำหนดรูปแบบ

ฟังก์ชันที่จะใช้กับ FuncFormatter จะต้องรับตัวแปร ๒ ตัว ตัวแรกคือค่าตัวเลขบนขีด และอีกตัวคือตำแหน่ง และต้องคืนค่ากลับเป็นสายอักขระ

ตัวอย่าง ลองทำให้แกน x ให้แสดงเป็นเศษส่วนหากไม่เป็นจำนวนเต็ม ส่วนแกน y แสดงค่าเป็นจำนวนเต็มเมื่อหาร 1 ลงตัว นอกนั้นแสดงเป็นจำนวนที่มีทศนิยม ๓ ตำแหน่ง
def fx(k,p):
    n=0
    if(k%1==0):
        return '$%d$'%k
    while(k%1!=0 and n<6):
        k *= 10
        n += 1
    return r'$\frac{%d}{%d}$'%(k,10**n)
def fy(k,p):
    if(k%1==0):
        return '%d'%k
    else:
        return '%.3f'%k

ax = plt.axes([0.15,0.1,0.8,0.85],xlim=[-0,1.25],ylim=[-100,100],facecolor='#ddddff')
ax.tick_params(axis='y',labelsize=10,labelcolor='m')
ax.tick_params(axis='x',labelsize=16,labelcolor='r')
ax.xaxis.set_major_locator(mpl.ticker.LinearLocator(11))
ax.yaxis.set_major_locator(mpl.ticker.LinearLocator(13))
ax.xaxis.set_major_formatter(mpl.ticker.FuncFormatter(fx))
ax.yaxis.set_major_formatter(mpl.ticker.FuncFormatter(fy))
plt.plot(np.random.uniform(-1,3,1000),np.random.uniform(-200,200,1000),'#330000')
plt.show()





การปรับค่าขีดและข้อความบนขีดในแถบสี
ค่าขีดและข้อความบนขีดของแถบสีที่ใช้แสดงเมื่อวาดแผนภาพไล่สีหรือคอนทัวร์ก็สามารถปรับแต่งได้ด้วยวิธีในลักษณะเดียวกันกับของกราฟหลัก

สามารถทำได้โดยปรับค่าแอตทริบิวต์ locator และ formatter ภายในออบเจ็กต์ของแถบสี จากนั้นก็อัปเดตค่าโดยใช้เมธอด update_ticks เพื่อให้แสดงผลค่าที่เปลี่ยนใหม่

ตัวอย่าง
x,y = np.meshgrid(np.linspace(-4,4,101),np.linspace(-4,4,101))
z = np.sqrt(x**4-8*x**2+20)-y**2
plt.axes([0.05,0.05,0.95,0.93])
plt.contourf(x,y,z,20,cmap='inferno')
cb = plt.colorbar(pad=0.01,aspect=14)
cb.locator = mpl.ticker.LinearLocator(20)
cb.formatter = mpl.ticker.FormatStrFormatter('%+.1f')
cb.update_ticks()
plt.show()





อ้างอิง


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


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

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

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

หมวดหมู่

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

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

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
ภาษา javascript
ภาษา mongol
ภาษาศาสตร์
maya
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



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

  ค้นหาบทความ

  บทความแนะนำ

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

ไทย

日本語

中文