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



numpy & matplotlib เบื้องต้น บทที่ ๑๐: การวาดหลายกราฟในภาพเดียว
เขียนเมื่อ 2016/06/11 18:28
แก้ไขล่าสุด 2022/07/21 20:25
ในบทที่ผ่านมาเราวาดกราฟเพียงกราฟเดียวในหนึ่งภาพตลอด แต่ความจริงแล้ว matplotlib สามารถนำกราฟหลายๆกราฟมายัดรวมกันในภาพเดียวกันได้



การใช้ subplot
ปกติถ้าเราใช้ฟังก์ชัน plt.axes จะเป็นการสร้าง axes (กลุ่มแกนวางกราฟ) อันใหญ่เพียงอันเดียวลงบน figure(ฉากวาดภาพ)

แต่หากต้องการให้ figure หนึ่งมีหลาย axes ก็สามารถทำได้โดยใช้ฟังก์ชัน plt.subplot แทนที่จะใช้ plt.axes

subplot คือกราฟย่อย เราจะสร้างกราฟย่อยกี่อันลงบนฉากก็ได้

เราสามารถเขียน subplot ในรูปฟังก์ชัน plt.subplot หรือในรูปเมธอดของ figure ก็ได้ เพียงแต่รูปเมธอดจะไม่ได้เขียนแค่ fig.subplot เฉยๆ แต่เขียนเป็น fig.add_subplot

ขอเริ่มยกตัวอย่างการใช้ด้วยการใช้ subplot
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,10,101)
fig = plt.figure(edgecolor=(0,0,0),linewidth=5)
ax1 = fig.add_subplot(2,3,1)
ax1.plot(x,np.sqrt(x),'m')
plt.show()



เสร็จแล้วก็จะได้กราฟที่มีขนาดเล็กวางอยู่ที่มุมซ้ายบนของภาพ โดยมีขนาดเป็น ๑ ใน ๖ ของภาพทั้งหมด โดยมีความกว้างเป็น ๑ ใน ๓ ของความกว้างเต็ม และความสูง ๑ ใน ๒ ของความสูงเต็ม

จะเห็นว่า add_subplot นี้มีอาร์กิวเมนต์ที่ต้องใส่อยู่ ๓ ตัว ความหมายก็คือ
(จำนวนแบ่งตามความสูง, จำนวนแบ่งตามความกว้าง, ลำดับที่)

ลำดับที่ในที่นี้นับตั้งแต่ 1 ไล่ไปเรื่อยๆโดยเริ่มตั้งแต่ซ้ายบน ไล่มาทางขวาจนหมดแถวแล้วขึ้นแถวใหม่

ในตัวอย่างนี้แบ่งตามความกว้างเป็น 3 และแบ่งตามความสูงเป็น 2 ดังนั้นจึงมีทั้งหมด ๖ ภาพ ซึ่งตำแหน่งจะเรียงตามนี้
1 2 3
4 5 6

ถ้าเป็น 4x4 ก็จะแบ่งเป็น
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

อาร์กิวเมนต์ ทั้ง ๓ ตัวนี้หากเป็นเลขหลักเดียวทั้งหมดสามารถนำมารวมเป็นเลข ๓ หลักตัวเดียวได้ เช่น 2,3,1 เขียนเป็น 231 ดังนั้นในตัวอย่างนี้จะเขียนเป็น
ax1 = fig.add_subplot(231)

โปรแกรมจะทำการตีความเป็น 2,3,1 ให้โดยอัตโนมัติ

จะใส่เป็นสายอักขระก็ได้เช่นกัน คือเขียนเป็น
ax1 = fig.add_subplot('231')

ลองเขียนใหม่โดยเปลี่ยนจากตำแหน่งที่ 1 เป็น 5
x = np.linspace(0,10,101)
plt.figure(edgecolor=(0,0,0),linewidth=5)
plt.subplot('235',title='$x^{1/2}$')
plt.plot(x,np.sqrt(x),'m')
plt.show()

จะได้ว่ากราฟย้ายมาอยู่ด้านล่างตรงกลาง



อนึ่ง ตัวอย่างนี้ถ้าหากเขียน plt.subplot แทน fig.add_subplot จะเห็นว่าวิธีการเขียนต่างกันเล็กน้อยแต่ทำงานได้เช่นกัน ยังไงก็ควรจะเข้าใจวิธีการเขียนทั้ง ๒ แบบ

คราวนี้ลองมาใส่กราฟให้ครบทั้ง ๖ ตำแหน่งกันดู
x = np.linspace(0,10,101)
fig = plt.figure()
ax1 = fig.add_subplot(231,title='$x^{1/2}$')
ax1.plot(x,np.sqrt(x),'m')
ax2 = fig.add_subplot(232,title='$x^2$')
ax2.plot(x,x**2,'m')
ax3 = fig.add_subplot(233,title='$sin(x)$, $cos(x)$')
ax3.plot(x,np.sin(x),'m')
ax3.plot(x,np.cos(x),'r')
ax4 = fig.add_subplot(234,title='$e^x$')
ax4.plot(x,np.exp(x),'m')
ax5 = fig.add_subplot(235,title='$ln(x+1)$')
ax5.plot(x,np.log(x+1),'m')
ax6 = fig.add_subplot(236,title='$sinh(x)$')
ax6.plot(x,np.sinh(x),'m')
plt.show()

ออกมาเป็นกราฟ ๖ อันสวยงาม



กราฟย่อยแต่ละอันจะถูกวางในตำแหน่งที่ต่างกันออกไปหมด หาก add_subplot ใหม่ลงตำแหน่งซ้ำเดิมกราฟเดิมที่มีอยู่แล้วกราฟเดิมก็จะหายไป ไม่ได้ซ้อนทับกัน

เพียงแต่ว่าในกราฟย่อยอันหนึ่งจะวาดกราฟกี่อันก็ได้ ดังที่เห็นว่าในตัวอย่างนี้กราฟอันที่ ๓ มีวาดไว้ ๒ เส้น

กราฟแต่ละอันที่วางลงไปไม่จำเป็นต้องมีอาร์กิวเมนต์ ๒ ตัวแรกเท่ากันก็ได้ เช่นลองทำแบบนี้
x = np.linspace(0,10,101)
fig = plt.figure()
ax1 = fig.add_subplot(311,title='$x^{1/2}$')
ax1.plot(x,np.sqrt(x),'m')
ax2 = fig.add_subplot(324,title='$x^2$')
ax2.plot(x,x**2,'m')
ax3 = fig.add_subplot(234,title='$sin(x)$, $cos(x)$')
ax3.plot(x,np.sin(x),'m')
ax3.plot(x,np.cos(x),'r')
ax4 = fig.add_subplot(338,title='$e^x$')
ax4.plot(x,np.exp(x),'m')
ax5 = fig.add_subplot(9,2,7,title='$ln(x+1)$')
ax5.plot(x,np.log(x+1),'m')
ax6 = fig.add_subplot(4,3,12,title='$sinh(x)$')
ax6.plot(x,np.sinh(x),'m')
plt.show()

แต่ละอันจะมีขนาดต่างกันไป ขึ้นกับค่าที่ใส่ใน add_subplot



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



การใช้ subplot2grid
นอกจาก subplot แล้วยังมีอีกวิธีในการสร้างกราฟย่อย คือใช้ subplot2grid

วิธีนี้จะดีกว่าในกรณีที่ต้องการจัดวางกราฟด้วยกันจำนวนมากซึ่งมีความซับซ้อน ไม่สะดวกที่จะใช้ subplot ธรรมดา

ตัวอย่างการใช้
plt.subplot2grid((3,2), (0,0),facecolor='#FFEEEE')
plt.subplot2grid((3,2), (0,1),facecolor='#FFEEFF')
plt.subplot2grid((3,2), (1,0),facecolor='#FFFFEE')
plt.subplot2grid((3,2), (1,1),facecolor='#EEFFEE')
plt.subplot2grid((3,2), (2,0),facecolor='#EEFFFF')
plt.subplot2grid((3,2), (2,1),facecolor='#EEEEFF')
plt.show()



จะเห็นว่า subplot2grid สามารถสร้างกราฟที่แบ่งเป็นส่วนๆได้เช่นเดียวกับ subplot แต่วิธีการเขียนจะต่างกันเล็กน้อย

subplot2grid ใช้อาร์กิวเมนต์ที่เป็นคู่อันดับ ๒ ตัวเป็นตัวกำหนดขนาดและตำแหน่งของกราฟ

โดยคู่อันดับตัวแรกคือขนาดของกราฟ ตัวแรกคือแนวตั้ง ตัวหลังคือแนวนอน

ส่วนคู่อันดับตัวหลังคือพิกัดของกราฟ โดยจะต่างจากของ subplot ตรงที่ใช้ตำแหน่งตามแนวตั้งและแนวนอน โดยเริ่มจาก (0,0)

สามารถแบ่งกราฟให้แต่ละอันมีขนาดไม่เท่ากันได้เช่นกัน และดูจะเข้าใจง่ายกว่าด้วย เพราะใช้ตำแหน่งพิกัดบอก ไม่ต้องมาไล่ตัวเลข

ลอง
plt.subplot2grid((2,1), (0,0),facecolor='#FFFFEE')
plt.subplot2grid((2,2), (1,0),facecolor='#EEFFEE')
plt.subplot2grid((3,2), (2,1),facecolor='#EEFFFF')
plt.show()



แต่ที่เหนือกว่าก็คือ สามารถใส่คีย์เวิร์ด colspan และ rowspan ลงไปเพื่อทำให้กราฟขยายตัวออกมากขึ้นไล่จากตำแหน่งเดิมตามพิกัด

ลองดูตัวอย่าง
plt.subplot2grid((2,4), (0,0),rowspan=2,facecolor='#FFFFEE')
plt.subplot2grid((2,4), (0,1),colspan=3,facecolor='#EEEEFF')
plt.show()



แล้วพอใช้แบบนี้แล้วก็จะทำให้สามารถแบ่งเขตได้อย่างซับซ้อน
plt.subplot2grid((3,4), (0,0),rowspan=2,facecolor='#FFEEEE')
plt.subplot2grid((3,4), (0,1),colspan=2,facecolor='#FFEEFF')
plt.subplot2grid((3,4), (1,1),facecolor='#FFFFEE')
plt.subplot2grid((3,4), (1,2),rowspan=2,facecolor='#EEFFEE')
plt.subplot2grid((3,4), (2,0),colspan=2,facecolor='#EEFFFF')
plt.subplot2grid((3,4), (0,3),rowspan=3,facecolor='#EEEEFF')
plt.show()





การปรับขนาดและการจัดวางกลุ่มกราฟย่อย
จะเห็นว่าเมื่อสร้างกลุ่มกราฟย่อยขึ้นมาแล้วแต่ละกราฟย่อยจะจัดเรียงตัวห่างกันในระยะหนึ่ง และขนาดโดยรวมก็จะเล็กกว่าขอบเขตของภาพทั้งหมดเล็กน้อยเพื่อให้มีที่เขียน ตัวเลขด้านนอกแกน

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

เพื่อที่จะตั้งค่าขอบเขตของกราฟย่อยรวมถึงระยะห่างระหว่างกราฟย่อย สามารถใช้ฟังก์ชัน plt.subplots_adjust ได้

ตัวอย่างเช่น ลองพิมพ์ต่อท้ายตัวอย่างที่แล้วไปว่า
plt.subplots_adjust(0.05,0.05,0.97,0.97,0.4,0.4)

จะได้กราฟที่ปรับให้ดูเหมาะสมขึ้นมา



อาร์กิวเมนต์ ของฟังก์ชันนี้เป็นไปตามนี้ ไล่ตามลำดับ หรือหากจะปรับแค่บางอันก็ใส่เป็นคีย์เวิร์ด ส่วนอันที่ไม่ได้ใส่จะเป็นไปตามค่าตั้งต้น
คีย์เวิร์ด ความหมาย ค่าตั้งต้น
left ขอบเขตฝั่งซ้าย 0.125
bottom ขอบเขตด้านล่าง 0.1
right ขอบเขตด้านขวา 0.9
top ขอบเขตด้านบน 0.9
wspace ระยะห่างแนวตั้งระหว่างกราฟย่อย 0.2
hspace ระยะห่างแนวนอนระหว่างกราฟย่อย 0.2

ค่าทั้งหมดมีหน่วยเป็นสัดส่วนต่อขนาดภาพทั้งหมด โดยนับจากซ้ายล่างสุด

ถ้าให้ขอบเขตเป็นตั้งแต่ 0 ถึง 1 และระยะเว้นห่างระหว่างกราฟย่อยเป็น 0 ไปหมดก็จะได้ว่ามีแต่พื้นกราฟเต็มหน้าจอ
plt.subplots_adjust(0,0,1,1,0,0)





การใช้ subplots
หาก subplot เติม s เพิ่มมาอีกตัวนึงก็จะกลายเป็นคนละฟังก์ชันกันแล้ว ต้องระวังสับสนระหว่าง subplot กับ subplots เป็นฟังก์ชันที่คล้ายกันแต่ก็ไม่เหมือนกันทีเดียว

plt.subplots เป็นฟังก์ชันที่ใช้สร้าง figure อันหนึ่งที่มี axes ย่อยเป็นจำนวนมากอยู่กันพร้อมตามจำนวนที่กำหนด ต่างจาก plt.subplot ที่เป็นการสร้าง axes ขึ้นมาใหม่บน figure ที่มีอยู่แล้ว

อาร์กิวเมนต์ที่ต้องใส่มี ๒ ตัว คือจำนวนส่วนแบ่งในแนวตั้ง และแนวนอน ตามลำดับ

โดยปกติเวลาที่ใช้ฟังก์ชันนี้จะต้องมีตัวแปรมารับค่าคืนกลับ โดยค่าคืนกลับจะมาในรูปของทูเพิลที่มีสมาชิกอยู่สองตัว ตัวแรกคือ figure และตัวหลังคืออาเรย์สองมิติที่รวม axes ทั้งหลาย

การใช้งานนั้นสะดวกต่อการใช้คู่กับ for เพื่อวนซ้ำวาดกราฟไปทีละอัน

ตัวอย่างการใช้งาน ลองสร้างกราฟของ sin และ cos ที่ความถี่ต่างๆ
x = np.arange(100)
fig,sax = plt.subplots(4,2)
for i in range(4):
    sax[i,0].plot(x,np.sin(x*(i+1)),'m')
    sax[i,1].plot(x,np.cos(x*(i+1)),'g')
plt.show()



เนื่องจาก subplots เป็นฟังก์ชันที่สร้าง figure ดังนั้นคีย์เวิร์ดที่สามารถใส่เสริมลงไปได้ก็จะเหมือนกับฟังก์ชัน plt.figure เช่นมี figsize, dpi, edgecolor, linewidth, facecolor, ฯลฯ รายละเอียดดูได้ที่บทที่ ๗

ส่วนคีย์เวิร์ดต่างๆที่ปกติใส่ตอนใช้ plt.axes หรือ plt.subplot ได้นั้นจะไม่สามารถใส่เป็นคีย์เวิร์ดใน subplots ได้ ต้องมาแยกตั้งค่าเพิ่มให้กับแต่ละ axes เอง

ตัวอย่างการใช้
x = np.arange(100)
fig,sax = plt.subplots(3,2,figsize=[7,5],dpi=120,edgecolor='#229911',linewidth=25,facecolor='#ddaaff')
[sax[i,j].set_ylim([-1,1]) for i in range(3) for j in [0,1]]
for i in range(3):
    sax[i,0].plot(x,np.sin(x*(i+1)/2.)**2,'#BBAA33')
    sax[i,1].plot(x,np.cos(x*(i+1)/2.)**2,'#2222BB')
plt.show()





การปรับขนาดและจัดเรียงกราฟอย่างอิสระ
ทั้ง ๒ วิธีที่ผ่านมานั้นเป็นการทำให้กราฟถูกจัดวางโดยอัตโนมัติ ตำแหน่งที่ถูกวางนั้นมีข้อจำกัดเพราะต้องเป็นไปตามบล็อกที่กำหนด บางทีอาจไม่เป็นไปตามที่เราต้องการ เช่นภาพมีการซ้อนทับกัน

หากต้องการปรับทุกอย่างได้อย่างอิสระอาจทำได้โดยใช้ฟังก์ชัน plt.axes ดังที่ได้กล่าวถึงไปในบทที่ ๗ แล้ว ฟังก์ชันนี้จะสร้าง axes ขึ้นมาโดยปรับขนาดและตำแหน่งได้ตามที่ต้องการ โดยอาร์กิวเมนต์ที่ต้องใส่เป็นตัวแรกคือลิสต์ที่ประกอบด้วย [ขอบซ้าย,ขอบล่าง,ความกว้าง,ความสูง]

ตัวอย่าง ถ้าตั้งเป็น 0,0,1,1 จะได้กราฟออกมาใหญ่เต็มจอจนมองไม่เห็นตัวเลขซึ่งอยู่ด้านนอกแกน
x = np.arange(-5,6)
plt.axes([0,0,1,1])
plt.plot(x,x,'m',lw=10)
plt.plot(x,-x,'m',lw=10)
plt.show()



เราสามารถเอาไปจัดวางตำแหน่งต่างๆวาดเล่นได้อย่างอิสระ กราฟจะวางยังไงก็ได้ ซ้อนกันก็ได้

ตัวอย่าง
x = np.arange(-5,6)
plt.axes([0,0.5,0.5,0.5])
plt.plot(x,x,'r',lw=30)
plt.plot(x,-x,'r',lw=30)
for i in range(5):
    plt.axes([0.1*i,0,0.1,0.5])
    plt.plot(x,x,'m',lw=10)
    plt.plot(x,-x,'m',lw=10)
for i in range(10):
    plt.axes([0.5,0.1*i,0.5,0.1])
    plt.plot(x,x,'c',lw=10)
    plt.plot(x,-x,'c',lw=10)
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)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

ไทย

日本語

中文