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



numpy & matplotlib เบื้องต้น บทที่ ๓๑: เส้นกระแส
เขียนเมื่อ 2016/06/12 12:37
แก้ไขล่าสุด 2021/09/28 16:42
ในบทที่ ๒๙ ได้ลองวาดสนามลูกศรขึ้นมา ซึ่งจะเห็นว่าเต็มไปด้วยลูกศรน้อยใหญ่เต็มฉากไปหมด

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

หากลองลากลูกศรเล็กๆแต่ละอันมาต่อกันเป็นลูกศรยาวๆอาจทำให้เราเห็นภาพอะไรชัดยิ่งขึ้น

นอกจากลูกศรเล็กๆตรงๆซึ่งวาดด้วยฟังก์ชัน plt.quiver แล้ว matplotlib ยังสามารถวาดลูกศรยาวๆโค้งๆที่ดูสวยงามได้ด้วย โดยใช้ฟังก์ชัน plt.streamplot



เปรียบเทียบ quiver กับ streamplot
เนื่องจาก quiver กับ streamplot นั้นคล้ายกันมาก และเราก็ได้พูดถึง quiver กันไปแล้ว ดังนั้นเพื่อความเข้าใจ streamplot จึงขอเริ่มต้นจากการเปรียบเทียบ

streamplot นั้นเหมือนกับ quiver ตรงที่ต้องการอาร์กิวเมนต์ ๔ ตัวคือ x,y,u,v ซึ่งแทนตำแหน่งแกน x,y และขนาดลูกศรในแกน x,y ตามลำดับ

สำหรับกรณีของ streamplot ไม่น่าจะเรียกว่าขนาดของลูกศร แต่เป็นขนาดของกระแส เพราะแต่ละจุดจะไม่ได้แทนลูกศรเป็นอันๆ ตำแหน่งและขนาดของจุดในห้วงกระแสจะถูกใช้เพื่อคำนวณและวาดลูกศรยาวออกมาตาม ที่เหมาะสม

เพื่อให้เห็นชัดมาลองวาดดูเลย วาดแผนภาพขึ้นมาสองอันด้วยข้อมูลชุดเดียวกัน มีคอนทัวร์แบบเดียวกัน แต่อันหนึ่งใช้ quiver อีกอันใช้ streamplot

ขอยกตัวอย่างด้วยฟังก์ชันอันนี้ ซึ่งเราสามารถหาอนุพันธ์ย่อยได้ตามนี้


สมมุติว่าให้ฟังก์ชันนี้เป็นตัวบอกความสูงของพื้น และลูกศรเป็นตัวบ่งชี้ถึงความชัน
import numpy as np
import matplotlib.pyplot as plt
x,y = np.meshgrid(np.linspace(-5,5,101),np.linspace(-5,5,101)) # จุดบนภาพ ความละเอียด 101x101
z = np.sqrt(x**4/4-2*x**2+5)+y**2/10 # ค่า z ซึ่งจะใช้เป็นสีในคอนทัวร์
plt.axes([0.05,0.05,1,0.93])
plt.contourf(x,y,z,12,cmap='coolwarm') # วาดคอนทัวร์
plt.colorbar(pad=0.01)
x,y = np.meshgrid(np.linspace(-5,5,21),np.linspace(-5,5,21)) # กำหนดหนด x,y ที่ลดความละเอียดลงสำหรับจุดที่จะวาดลูกศร ความละเอียด 21x21
u = (x**3-4*x)/np.sqrt(x**4/4-2*x**2+5)/2 # ความชันของ z ในแกน x
v = y/5 # ความชันของ z ในแกน y
plt.quiver(x,y,u,v,color='g') # วาดเส้นสนามลูกศร
plt.streamplot(x,y,u,v,color='m') # วาดเส้นกระแส
plt.show()



จะเห็นว่า quiver กับ streamplot นี้เรียกใช้ในลักษณะเดียวกัน ด้วยข้อมูลชุดเดียวกัน ผลที่ได้ก็คือได้ลูกศรที่ชี้ตั้งฉากกับแนวคอนทัวร์เช่นกัน แต่ quiver ให้ลูกศรย่อยๆเป็นจุดๆ ส่วน streamplot ให้ลูกศรยาวๆซึ่งมีการจัดวางที่ซับซ้อน

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



การปรับแต่งความหนาแน่นของเส้น
streamplot ต่างจาก quiver ตรงที่ความหนาแน่นของเส้นกระแสนั้นไม่ขึ้นกับความละเอียดของตำแหน่งพิกัด x,y

ลองวาดใหม่ดูโดยเปลี่ยนจากที่เดิมใช้ x,y เป็น 21,21 เหมือนกับสนามลูกศร ก็เปลี่ยนมาใช้ 101,101 เช่นเดียวกับความละเอียดภาพ
x,y = np.meshgrid(np.linspace(-5,5,101),np.linspace(-5,5,101))
z = np.sqrt(x**4/4-2*x**2+5)+y**2/10
plt.axes([0.05,0.05,1,0.93])
plt.contourf(x,y,z,12,cmap='coolwarm')
plt.colorbar(pad=0.01)
u = (x**3-4*x)/np.sqrt(x**4/4-2*x**2+5)/2
v = y/5
plt.streamplot(x,y,u,v,color='m')
plt.show()



ความหนาแน่นของเส้นกระแสสามารถกำหนดได้ด้วยการใส่คีย์เวิร์ด density โดยถ้าไม่ใส่ค่าตั้งต้นคือ 1 ถ้ายิ่งใส่มากกว่า 1 ก็จะมีเส้นมากขึ้น ถ้าใส่น้อยกว่าเส้นก็น้อยลง

ลองเขียนใหม่โดยแก้เป็น
plt.streamplot(x,y,u,v,color='m',density=3)



จะได้ลูกศรเยอะขึ้นมาก แต่ก็ใช้เวลาวาดนานขึ้นพอสมควร



การปรับสีและความกว้างของเส้น
นอกจากนี้สามารถให้ลูกศรเปลี่ยนสีไปเรื่อยๆในแต่ละบริเวณได้ด้วย โดยใส่ค่าสีในคีย์เวิร์ด color เป็นอาเรย์ที่มีขนาดเท่ากับ x,y,u,v แล้วก็ใส่คีย์เวิร์ด cmap เพื่อกำหนดคัลเลอร์แม็ปที่จะใช้ด้วย

ในที่นี้ลองวาดโดยลงสีให้แปรเปลี่ยนไปตามความชันรวมทั้งแกน x และ y แล้วก็ใส่แถบสีเพิ่มให้ด้วยวางไว้ด้านล่าง
x,y = np.meshgrid(np.linspace(-5,5,101),np.linspace(-5,5,101))
z = np.sqrt(x**4/4-2*x**2+5)+y**2/10
u = (x**3-4*x)/np.sqrt(x**4/4-2*x**2+5)/2
v = y/5
plt.axes([0.05,0.05,1,0.93])
plt.contourf(x,y,z,12,cmap='coolwarm')
plt.colorbar(pad=0.01)
plt.streamplot(x,y,u,v,color=np.sqrt(u**2+v**2),cmap='hot',density=0.4)
plt.colorbar(orientation='horizontal',fraction=0.05,pad=0.05,aspect=100)
plt.show()



ส่วนความกว้างของเส้นก็สามารถให้แปรเปลี่ยนไปตามตำแหน่งได้

เช่น ลองให้ความกว้างเส้นกระแสเปลี่ยนไปในทางตรงกันข้ามกันกับความชัน
x,y = np.meshgrid(np.linspace(-5,5,101),np.linspace(-5,5,101))
z = np.sqrt(x**4/4-2*x**2+5)+y**2/10
u = (x**3-4*x)/np.sqrt(x**4/4-2*x**2+5)/2
v = y/5
c = np.sqrt(u**2+v**2)
lw = 5/(c+0.5)
plt.axes([0.05,0.05,1,0.93])
plt.contourf(x,y,z,12,cmap='coolwarm')
plt.colorbar(pad=0.01)
plt.streamplot(x,y,u,v,color=c,cmap='hot',linewidth=lw,density=0.4)
plt.show()





ตัวอย่างอื่นๆ
เพื่อความเข้าใจลองดูตัวอย่างการใช้อื่นๆเพิ่มเติม

ลูกศรแสดงกระแสลมที่สุ่มขึ้นมา
x,y = np.meshgrid(np.linspace(-5,5,21),np.linspace(-5,5,21))
u = np.random.randn(21,21)
v = np.random.randn(21,21)
plt.axes([0.05,0.05,0.9,0.93],xlim=[-5,5],ylim=[-5,5],facecolor='#FFFFDD')
plt.streamplot(x,y,u,v,color='r',density=0.8)
plt.show()



ลองปรับให้กระแสในทิศ x โดยเพิ่มพจน์ที่มีขนาดเปลี่ยนแปลงเป็นเชิงเส้น แล้วก็ใส่การปรับสีและความหนาของเส้นเพิ่มเข้าไปด้วย
x,y = np.meshgrid(np.linspace(-5,5,21),np.linspace(-5,5,21))
u = np.random.randn(21,21)+np.arange(-10,11)
v = np.random.randn(21,21)
c = np.sqrt(u**2+v**2)
lw = 5/(c+0.5)
plt.axes([0.05,0.05,1,0.93],xlim=[-5,5],ylim=[-5,5],facecolor='#FFFFDD')
plt.streamplot(x,y,u,v,color=c,cmap='cool',linewidth=lw,density=0.8)
plt.colorbar(pad=0.01)
plt.show()



ลูกศรแสดงความชันของฟังก์ชัน cos ที่มีการสุ่มรบกวน กรณีนี้ใช้ np.gradient เพื่อหาค่าความชัน
x,y = np.meshgrid(np.linspace(-5,5,101),np.linspace(-5,5,101))
z = np.cos(x/2)+np.cos(y/4)+np.random.randn(101,101)*0.04
v,u = np.gradient(z,0.1,0.1,edge_order=2)
c = np.sqrt(u**2+v**2)
lw = 5/(c+0.5)
plt.axes([0.05,0.05,1,0.93],xlim=[-5,5],ylim=[-5,5])
plt.contourf(x,y,z,30,cmap='rainbow')
plt.colorbar(pad=0.01)
plt.streamplot(x,y,u,v,color=c,cmap='gnuplot',density=0.6)
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)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

บทความแต่ละเดือน

2024年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2023年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2022年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2021年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2020年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

ค้นบทความเก่ากว่านั้น

ไทย

日本語

中文