ในบทที่ผ่านมาเราวาดกราฟเพียงกราฟเดียวในหนึ่งภาพตลอด แต่ความจริงแล้ว 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()
อ้างอิง