ใน
บทที่แล้วได้พูดถึงการเขียนกราฟเบื้องต้นและการแต่งตัวกราฟไปแล้ว แต่ยังไม่ได้พูดถึงปรับแต่งส่วนประกอบต่างๆของกราฟ
การจะปรับแต่งอะไรเพิ่มเติมได้มากขึ้นจำเป็นจะต้องเข้าใจภาพรวมของโครงสร้างส่วนประกอบต่างๆที่ประกอบเป็นฉากสำหรับวาดกราฟก่อน
เนื่องจาก ส่วนประกอบต่างๆมีลักษณะเป็นออบเจ็กต์ ดังนั้นควรจะเข้าใจเรื่องการเขียนโปรแกรมเชิงวัตถุในระดับนึง ไม่เช่นนั้นอาจไม่สามารถเข้าใจกลไกการทำงานของมันได้ดีพอและสามารถใช้งานได้ แค่ผิวเผิน
ส่วนประกอบของกราฟ ภาพที่เราได้ออกมาเมื่อวาดกราฟนั้นถูกเขียนอยู่บนฉากวาดภาพที่เรียกว่า figure
และบนฉากวาดภาพก็จะมีส่วนแกนวางกราฟ เรียกว่า axes
คำว่า axes เป็นพหูพจน์ของ axis ที่แปลว่าแกน กราฟนึงประกอบขึ้นจากแกนสองแกน ดังนั้นจึงใช้รูปพหูพจน์ อาจจะแปลว่าชุดแกนหรือกลุ่มแกนก็ได้
(ภาษาอังกฤษยุ่งยากตรงที่ต้องพะวงว่าเอกพจน์หรือพหูพจน์ แถมบางคำไม่ไช่แค่เติม s เฉยๆเต็มไปด้วยข้อยกเว้นมากมาย)
ใน figure นึงจะมีกี่ axes ก็ได้ การใส่หลาย axes ลงใน figure นึงจะใช้ฟังก์ชัน subplot ซึ่งจะยังไม่พูดถึงในบทนี้
และกราฟจะถูกวาดลงบนใน axes โดยใน axes นึงอาจวาดหลายกราฟได้ ดังที่ได้แสดงไปใน
บทที่แล้ว ยกตัวอย่าง วาดภาพที่มีเส้นกราฟ ๒ เส้นใน axes เดียวกัน
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(100)
y1 = np.sin(x)*np.exp(-x/100)+1
y2 = np.sin(x)*np.exp((100-x)/100)-1
plt.plot(x,y1,'g')
plt.plot(x,y2,'m')
plt.show()
เราจะได้เส้นกราฟแบบนี้ออกมา
ตัวภาพทั้งหมดก็คือ figure ส่วนพื้นขาวๆและเส้นแกนสีดำคือส่วนของ axes และเส้นกราฟที่เห็นทั้ง ๒ สีนี้คือส่วนของเส้นกราฟที่วาดลงบน axes
อาจเขียนภาพร่างคร่าวๆได้เป็นแบบนี้
ความจริงแล้วการที่เริ่มต้นมาถึงเราสามารถใช้คำสั่ง plot ได้ทันทีเพราะตัวโปรแกรมนั้นจะทำการตั้ง figure กับ axes ให้อัตโนมัติ
การเขียนย่อแบบนี้มีข้อเสียคือหากเราไม่ได้กดปิดภาพเดิมก่อนที่จะรันใหม่มันก็จะวาดทับกราฟเดิมไป
หากเขียนเต็มๆโดยเริ่มตั้งแต่ตั้ง figure และตั้ง axes จะเขียนแบบนี้ (ขอละตรงส่วน import และนิยามค่า x, y1, y2 ออก)
fig1 = plt.figure()
ax1 = fig1.gca()
ax1.plot(x,y1,'g')
ax1.plot(x,y2,'m')
plt.show()
จะเห็นว่าเพิ่มมาอีก ๒ บรรทัด ภาพที่ได้จะเหมือนเดิม เพียงแต่ว่าจะมีความต่างตรงที่ว่าหากใช้แบบนี้หากเรารันซ้ำโดยที่ยังไม่ได้ ปิดภาพเดิมทิ้งจะมีการเปิดแถบใหม่ขึ้นมาแล้ววาดใหม่ ไม่ได้เขียนทับภาพเดิม
ในที่นี้ fig1 = plt.figure() คือการสร้างออบเจ็กต์ figure ขึ้นมาแล้วเก็บไว้ในตัวแปรชื่อ fig1
และ ax1 = fig1.gca() คือการสร้างออบเจ็กต์ axes ขึ้นมาภายใน fig1 แล้วเก็บไว้ในตัวแปรชื่อ ax1
gca ย่อมาจาก get current axes หมายถึงเอาแกนปัจจุบัน ที่จริงแล้วฟังก์ชันนี้มีไว้เพื่อปรับตั้งค่าต่างๆใน axes ที่ใช้งานอยู่ปัจจุบัน แต่กรณีที่ figure ที่เราเลือกอยู่ยังไม่มี axes จะเป็นการสร้างขึ้นมาใหม่ หากจะปรับตั้งค่าอะไรต่างๆของ axes ก็จะมาปรับในฟังก์ชันนี้ แต่ตอนนี้เราจะยังไม่ปรับแต่จะสร้างขึ้นมาเฉยๆก่อน การปรับอะไรต่างๆจะพูดถึงใน
บทถัดไป จะเห็นว่า gca ตามหลัง fig1 ไม่ใช่ plt นั่นหมายความว่ามันเป็นเมธอดที่กระทำต่อออบเจ็กต์ fig1 โดยตรง
แต่ที่จริงแล้วเราสามารถเขียนแบบนี้ได้เช่นกัน
plt.figure()
ax1 = plt.gca()
ax1.plot(x,y1,'g')
ax1.plot(x,y2,'m')
plt.show()
ในที่นี้ ๒ บรรทัดแรกเปลี่ยนไป โดย plt.figure() ไม่ต้องใช้ตัวแปรอะไรมารองรับ ส่วน fig1.gca() เปลี่ยนเป็น plt.gca()
การเขียน fig1.gca กับ plt.gca นั้นจะมีความหมายต่างกันเล็กน้อย โดย fig1.gca เป็นเมธอดที่กระทำต่อออบเจ็กต์ fig1 โดยตรง โดยจะเพิ่ม axes ลงใน figure นี้
ส่วน plt.gca นั้นเป็นการใช้ในรูปแบบฟังก์ชัน โดยไม่ได้ระบุชัดว่าจะกระทำต่อออบเจ็กต์ไหน จึงเป็นการสร้าง axes ให้กับ figure ที่เลือกอยู่ปัจจุบัน ซึ่งก็คือ figure ที่เพิ่งสร้างขึ้นมา
ลองเปลี่ยนโค้ดเป็นแบบนี้ดู (ยังไม่ต้องใส่ ax1.plot)
plt.figure()
plt.figure()
ax1 = plt.gca()
plt.show()
ในนี้มีการใช้ plt.figure() ๒ ครั้ง ผลที่ได้คือจะเห็นว่ามีภาพโผล่ขึ้นมา ๒ ภาพ โดยวางซ้อนกันอยู่ อันที่อยู่ด้านหน้าคืออันที่สร้างขึ้นทีหลัง (อาจต้องเลื่อนอันด้านหน้าออกจึงจะเห็นอันด้านหลัง)
จะเห็นว่าอันที่อยู่ด้านหน้ามีพื้นสีขาวพร้อมแกนอยู่ ในขณะที่อันข้างหลังว่างเปล่า เพราะอันด้านหน้า ถูกเติม axes ลงไปนั่นเอง
ลองเปลี่ยนโค้ดเป็น
fig1 = plt.figure()
fig2 = plt.figure()
ax1 = fig1.gca()
plt.show()
ผลที่ได้จะกลับกัน คืออันหลังมีฉากสีขาวอยู่
นั่นเพราะคราวนี้พิมพ์เป็น fig1.gca แทนที่จะเป็น plt.gca นั่นคือเราระบุชัดว่าจะให้สร้าง axes ที่ fig1 ไม่ใช่ fig2 ดังนั้นแม้ว่าเราจะสร้าง fig2 ขึ้นมาทีหลัง axis ก็จะยังถูกเพิ่มให้ fig1 อยู่ดี
ตรงนี้น่าจะพอทำให้เห็นภาพชัดขึ้นมาบ้างไม่มากก็น้อย
กลับมาดูที่โค้ดเดิมซึ่งเขียนไว้ข้างต้น
fig1 = plt.figure()
ax1 = fig1.gca()
ax1.plot(x,y1,'g')
ax1.plot(x,y2,'m')
plt.show()
พิจารณาส่วนถัดมา คือ ax1.plot(x,y1) นี่คือการสร้างออบเจ็กต์เส้นกราฟขึ้นภายใน ax1
จะเห็นว่า plot ในที่นี้ไม่ได้ตามหลัง plt แต่ตามหลัง ax1 แทน หมายความว่า plot ในที่นี้เป็นเมธอดที่กระทำต่อออบเจ็กต์ ax1 โดยตรง
ซึ่งที่จริงแล้วเราสามารถเขียนเป็น plt.plot แบบนี้ได้เช่นกัน
fig1 = plt.figure()
ax1 = fig1.gca()
plt.plot(x,y1,'g')
plt.plot(x,y2,'m')
plt.show()
กรณีที่เขียน plt.plot แบบนี้จะเป็นการวาดกราฟที่ตัว axes ที่ถูกใช้อยู่ในปัจจุบัน ซึ่งก็คือ axes ที่เพิ่งสร้างขึ้นมาหรือถูกใช้งานล่าสุด
หลักการเช่นเดียวกับ plt.gca คือสามารถเลือกทำแบบระบุตัวออบเจ็กต์ก็ได้ หรือจะไม่ระบุก็ได้ซึ่งจะเป็นการเลือกตัวที่ใช้งานอยู่ล่าสุด
สรุปขั้นตอนก็คือ
สร้าง figure ชื่อ fig1 > สร้าง axes ชื่อ ax1 บน fig1 > วาดกราฟที่ 1 ลงบน ax1 > วาดกราฟที่ 2 ลงบน ax1
หากลองแก้เป็น
fig1 = plt.figure()
ax1 = fig1.gca()
fig2 = plt.figure()
ax2 = fig2.gca()
ax1.plot(x,y1,'g')
ax2.plot(x,y2,'m')
plt.show()
ผลที่ได้คือคราวนี้จะออกมาเป็น ๒ กราฟแยกกัน (อย่าลืมว่าอาจซ้อนกันอยู่)
หากวาดภาพร่างก็จะได้แบบนี้
ซึ่งทั้งหมดนี้อาจสามารถเขียนใหม่โดยไม่ใช่การเขียนเชิงวัตถุได้เลยเช่นกัน
plt.figure()
plt.gca()
plt.plot(x,y1,'g')
plt.figure()
plt.gca()
plt.plot(x,y2,'m')
plt.show()
อาจดูเข้าใจง่ายกว่า คือวาดไปทีละภาพเลย แต่ข้อเสียของการเขียนแบบนี้คือทุกอย่างต้องทำตามลำดับขั้น คือจัดการทุกอย่างในภาพแรกให้เสร็จ แล้วจึงมาทำอะไรในภาพหลัง จะทำย้อนไปย้อนมาไม่ได้
เรายังอาจเขียนย่อให้เหลือแค่นี้ได้อีกด้วย
plt.plot(x,y1,'g')
plt.figure()
plt.plot(x,y2,'m')
plt.show()
ด้านบนสุดไม่ต้องขึ้นต้นด้วย plt.figure และ plt.gca ก่อนเพราะถ้าหากเริ่มต้นมาถึงก็สั่ง plot ทันทีเลย ทั้ง figure และ axes จะถูกสร้างขึ้นมาอัตโนมัติ
แต่พอจะเปลี่ยนมาเขียนภาพใหม่จะต้อง plt.figure() ใหม่ก่อน ไม่เช่นนั้นจะเป็นการเขียนทับภาพเดิม และในส่วนนี้ plt.gca ก็ละได้อีกเช่นกันเพราะ axes จะถูกสร้างอัตโนมัติเวลาที่สั่ง plot
การเซฟภาพด้วย plt.savefig นั้นที่จริงแล้วก็เป็นการเซฟภาพ figure ที่เรากำลังเลือกอยู่ล่าสุด
แต่สามารถเปลี่ยนเป็นเซฟภาพใดๆได้โดยการเขียนในรูปเมธอดที่กระทำต่อตัว figure ที่ต้องการ เช่น fig1.savefig
fig1 = plt.figure()
plt.plot(x,y1,'g')
fig2 = plt.figure()
plt.plot(x,y2,'m')
fig1.savefig('fig1.png')
fig2.savefig('fig2.png')
แบบนี้ก็จะได้ไฟล์ ๒ ไฟล์ที่มีกราฟคนละอัน
นอกจากนี้ plt.figure() และ plt.gca() ยังสามารถใส่อาร์กิวเมนต์เพื่อกำหนดคุณสมบัติต่างๆของ figure และ axes ที่จะสร้างขึ้นมาใหม่ได้ด้วย
สรุปโดยรวมแนวทางต่อจากนี้ ว่าการใช้ฟังก์ชัน plt.figure() และ plt.gca() ควรจะใช้เมื่อไหรนั้น ก็ขึ้นอยู่กับว่าต้องการจะแต่งเติมอะไรในส่วนของ figure และ axis หรือเปล่า
และควรจะเอาตัวแปรมาเก็บค่าไว้ด้วยหรือเปล่าก็ขึ้นอยู่กับว่าจะมีการเรียกใช้ภายหลังตอนหลังจากที่ไปทำอะไรกับภาพอื่นอีกหรือเปล่า
อ้างอิง