φυβλαςのβλογ
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
蒙古语
语言学
maya
概率论
与日本相关的日记
与中国相关的日记
-- 与北京相关的日记
-- 与香港相关的日记
-- 与澳门相关的日记
与台湾相关的日记
与北欧相关的日记
与其他国家相关的日记
qiita
其他日志

按类别分日志



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  查看日志

  推荐日志

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ