ในบทที่แล้วได้พูดถึงการนำกราฟหลายอันมารวมอยู่ในภาพเดียวกันแล้ว แต่ว่ากราฟแต่ละอันก็แยกกันอยู่คนละส่วน เหมือนแค่เอามาวางคู่กันเฉยๆ
ดังนั้นในบทนี้จะพูดถึงการนำเอากราฟมาประกอบกัน
การใช้ twinx และ twiny เพื่อนำแกนมาซ้อนกัน ในบางครั้งเราเขียนกราฟของคนละสิ่งกันซึ่งมีมาตราส่วนและขอบเขตที่ต่างกันออกไปแต่ว่าต้องการเอามาเทียบกัน จึงต้องนำมาซ้อนทับกัน
ปกติ ถ้ามีการนำกราฟมาซ้อนกันด้วย subplot กราฟที่ถูกเขียนก่อนจะถูกทับหายไปทันที ไม่ได้ถูกเขียนซ้อนกัน ทำให้ต้องใช้วิธีการบางอย่างหากต้องการนำกราฟมาซ้อนกัน
การจะสามารถเขียนกราฟ ๒ กราฟให้ซ้อนอยู่บนที่เดียวกันได้อาจมีหลายวิธี ในที่นี้จะแนะนำวิธีการด้วยการใช้เมธอด twinx และ twiny
twinx และ twiny เป็นเมธอดของ axes ซึ่งมีไว้สร้าง axes อันใหม่ขึ้นอีกอันที่มีการใช้แกน x หรือ y ร่วมกัน
twinx คือเพิ่ม axes ใหม่ที่มีแกน x ร่วมกัน แต่ y ต่าง
twiny คือเพิ่ม axes ใหม่ที่มีแกน y ร่วมกัน แต่ x ต่าง
ขอยกตัวอย่างด้วยกราฟที่เทียบประชากรกับ GDP ของญี่ปุ่นตั้งแต่ปี 1950
(ข้อมูลจาก
https://ja.wikipedia.org/wiki/国内総生産 และ https://ja.wikipedia.org/wiki/日本の人口統計)
ถ้าใช้ subplot ก็จะออกมาแบบนี้
import matplotlib.pyplot as plt
pi = range(1950,2011,10)
pop = [84115,94302,104665,117060,123611,126962,128058]
gdp = [71683.1,188323.1,290551.1,284375.0,447369.9,474847.2,512364.6]
ax1 = plt.subplot(2,1,1)
ax1.set_title(u'ประชากร',fontname='Tahoma',fontsize=15)
ax1.plot(pi,pop)
ax2 = plt.subplot(2,1,2)
ax2.set_title('GDP',y=0.8)
ax2.plot(pi,gdp)
plt.show()
จากนั้นลองเปลี่ยนมาวางกราฟซ้อนกันดู ในที่นี้เราต้องการกราฟที่มี x เหมือนกัน คือเป็นปีเหมือนกัน แต่ที่ต่างคือแกน y ดังนั้นใช้ twinx
จะเขียนเป็น
pi = range(1950,2011,10)
pop = [84115,94302,104665,117060,123611,126962,128058]
gdp = [71683.1,188323.1,290551.1,284375.0,447369.9,474847.2,512364.6]
ax1 = plt.gca()
ax1.set_title(u'ประชากร',fontname='Tahoma',fontsize=15)
ax1.plot(pi,pop)
ax2 = ax1.twinx()
ax2.set_title('GDP')
ax2.plot(pi,gdp)
plt.show()
ผลที่ได้จะเห็นว่าแกน x ถูกใช้ร่วมกัน และแกน y ของกราฟที่เพิ่มเข้ามาก็ไปอยู่ทางขวาแทน
อย่างไร ก็ตามปัญหาคือหัวข้อด้านบนซ้อนทับกันอยู่เพราะมีการตั้งไว้ทั้งคู่ กรณีแบบนี้แทนที่จะใช้หัวข้อน่าจะเปลี่ยนมาใช้ legend แทนมากกว่า และเส้นกราฟก็เปลี่ยนสีให้ต่างกันด้วย
พอแก้แล้วก็จะเป็นแบบนี้
import matplotlib.pyplot as plt
import matplotlib as mpl
fp = mpl.font_manager.FontProperties(family='Tahoma',size=16)
pi = range(1950,2011,10)
pop = [84115,94302,104665,117060,123611,126962,128058]
gdp = [71683.1,188323.1,290551.1,284375.0,447369.9,474847.2,512364.6]
ax1 = plt.gca()
ax1.set_xlabel(xlabel=u'ปี',fontproperties=fp)
ax1.plot(pi,pop,'m',label=u'ประชากร')
ax1.legend(loc=2,prop=fp,fontsize=15)
ax2 = ax1.twinx()
ax2.plot(pi,gdp,'r',label='GDP')
ax2.legend(loc=4)
plt.show()
การใช้ subplot เพื่อเชื่อมโยงกราฟเข้าด้วยกัน บางครั้งเราก็อาจไม่ได้ต้องการให้กราฟซ้อนกัน แต่ต้องการจับมาเรียงต่อกันเพื่อแสดงความสัมพันธ์อะไรบางอย่าง
แต่การแค่จัดกราฟมาเรียงต่อกันเฉยๆด้วย subplot อาจจะยังดูไม่สวย ดังนั้นอาจต้องมีการจัดการอะไรเพิ่มเติม
ตัวอย่าง ลองดูกราฟที่เปรียบเทียบการสั่นของวัตถุที่ถูกหน่วง เมื่อนำมา subplot ต่อกันโดยไม่ได้ตกแต่งอะไรเป็นพิเศษก็จะได้เป็นแบบนี้
import numpy as np
x = np.linspace(0,16,500)
plt.subplot(311)
plt.plot(x,np.cos(x),'m')
plt.subplot(312)
plt.plot(x,np.cos(0.995*x)*np.exp(-0.1*x),'m')
plt.subplot(313)
plt.plot(x,np.cosh(0.458*x)*np.exp(-1.1*x),'m')
plt.show()
ซึ่งจะเห็นว่าดูแล้วไม่ค่อยเป็นระเบียบ เพราะ
- แกน y ของแต่ละอันดูมีสัดส่วนไม่เท่ากัน ดูแล้วเทียบกันยาก
- ไม่มีตัวหนังสือที่อธิบายแต่ละกราฟ
- ตัวเลขแกน x ดูแล้วรก ถ้าจะใช้แกนร่วมกัน มีแค่อันเดียวด้านล่างสุดก็พอแล้ว
เมื่อพิจารณาดูแล้วสิ่งที่ควรต้องทำก็คือ
- ตั้งขอบเขตของแกน y ให้เท่ากันหมด ใช้ ylim
- ใส่ตัวหนังสืออธิบายความหมายของ y ในกราฟแต่ละกราฟ ใช้ ylabel
- เลื่อน ylabel มาอยู่ตรงกลาง หมุนให้เป็นแนวนอน แล้วขยายให้เห็นชัด ใช้ yaxis.set_label_coords, fontsize=20 และ rotation=0
- ใส่ชื่อแกน x เฉพาะกราฟล่างสุด
- ลบตัวเลขในแกน x ของ ๒ กราฟบนออก ใช้ tick_params(labelbottom='off')
จะเห็นว่าทั้งหมดสามารถทำได้ด้วยการประยุกต์เนื้อหาในบทก่อนๆที่เขียนถึงไปแล้ว
พอลองแก้ดูแล้วก็จะออกมาเป็นแบบนี้
x = np.linspace(0,16,500)
ax1 = plt.subplot(311,ylim=(-1,1))
ax1.set_ylabel('$cos(t)$',fontsize=14,rotation=0)
ax1.tick_params(labelbottom='off')
ax1.yaxis.set_label_coords(0.5, 0.7)
ax1.plot(x,np.cos(x),'m')
ax2 = plt.subplot(312,ylim=(-1,1))
ax2.set_ylabel('$e^{-0.1t}cos(0.995t)$',fontsize=14,rotation=0)
ax2.tick_params(labelbottom='off')
ax2.yaxis.set_label_coords(0.5, 0.7)
ax2.plot(x,np.cos(0.995*x)*np.exp(-0.1*x),'m')
ax3 = plt.subplot(313,ylim=(-1,1),xlabel='t')
ax3.set_ylabel('$e^{-1.1t}cosh(0.458t)$',fontsize=14,rotation=0)
ax3.yaxis.set_label_coords(0.5, 0.7)
ax3.plot(x,np.cosh(0.458*x)*np.exp(-1.1*x),'m')
plt.show()
การสร้างกราฟซ้อนกราฟ บางครั้งแทนที่จะเอากราฟมาวางต่อกัน หากเอากราฟมาวางซ้อนกันอาจสะดวกกว่า
หาก ใช้ plt.axes แล้วเราสามารถจะวางกราฟซ้อนกันยังไงก็ได้ ในที่นี้เราจะลองสร้างกราฟใหญ่ขึ้นมาอันหนึ่งแล้วสร้างกราฟอันเล็กที่ขยาย ส่วนหนึ่งบนนั้น
ตัวอย่าง
x = np.linspace(0,10,10001)
y = np.sin(x)+np.sin(x*100)/10
plt.axes([0.08,0.05,0.87,0.9],ylim=[-1.2,1.3]) # กราฟหลัก
plt.plot(x,y)
plt.plot([1.3,1.8,1.8,1.3,1.3],[1.2,1.2,0.8,0.8,1.2],'r') # วาดกรอบสีแดงเพื่อระบุจุดที่จะขยาย
ax = plt.axes([0.5,0.5,0.42,0.42],xlim=[1.3,1.8],ylim=[0.8,1.2]) # กราฟเล็ก กำหนด xlim และ ylim ให้ต่างจากกราฟหลัก
plt.setp(list(ax.spines.values()),color='r')
plt.plot(x,y) # พล็อตโดยใช้ x,y ตัวเดียวกัน แต่ขอบเขตต่างกัน
plt.show()
อ้างอิง