การคำนวณเป็นความสามารถโดดเด่นหลักๆของอาเรย์ ในบทนี้จะพูดถึงพื้นฐานการนำอาเรย์มาใช้ในการคำนวณต่างๆ
การคำนวณขั้นพื้นฐานของอาเรย์ อาเรย์สามารถนำมาคำนวณได้โดยตรง การนำอาเรย์มาบวกลบกันนั้นก็ทำได้เหมือนกับเป็นการบวกลบเมทริกซ์ คือบวกลบตัวต่อตัวให้โดยอัตโนมัติ
ขอยกตัวอย่างโดยใช้อาเรย์นี้
import numpy as np
ar1 = np.array([[1,1,2],[2,2,3],[3,3,4]])
ar2 = np.array([[4,4,4],[2,2,2],[1,1,1]])
print(ar1)
print(ar2)
ผลลัพธ์
[[1 1 2]
[2 2 3]
[3 3 4]]
[[4 4 4]
[2 2 2]
[1 1 1]]
นำอาเรย์สองตัวนี้มาคำนวณค่าบวกลบ
print(ar1+ar2)
print(ar1-ar2)
ผลลัพธ์
[[5 5 6]
[4 4 5]
[4 4 5]]
[[-3 -3 -2]
[ 0 0 1]
[ 2 2 3]]
นอกจากการบวกและลบแล้ว การนำเมทริกซ์สองตัวมาคำนวณกันคือการนำสมาชิกแต่ละตัวมาคำนวณกันเป็นคู่ๆเหมือนกันทุกตัว
ดังนั้นที่จริงแล้วอาเรย์มีสมบัติหลายอย่างที่ไม่เหมือนกับเมทริกซ์ เพราะการคูณหารเมทริกซ์จะไม่ใช่การนำสมาชิกมาคูณหารกันเป็นคู่ๆ
ลองดูตัวอย่าง การคูณ, หาร, ยกกำลัง และหารเอาเศษ จะเห็นว่าเป็นการคำนวณเป็นคู่ๆทีละตัว
print(ar1*ar2)
print(ar1/ar2)
print(ar1**ar2)
print(ar1%ar2)
ผลลัพธ์
[[4 4 8]
[4 4 6]
[3 3 4]]
[[ 0.25 0.25 0.5 ]
[ 1. 1. 1.5 ]
[ 3. 3. 4. ]]
[[ 1 1 16]
[ 4 4 9]
[ 3 3 4]]
[[1 1 2]
[0 0 1]
[0 0 0]]
***ในส่วนของการหารถ้าเป็นไพธอน 2 จะถูกปัดเศษเพื่อให้คงความเป็นจำนวนเต็ม จึงได้
[[0 0 0]
[1 1 1]
[3 3 4]]
และยังรวมถึงการหาค่าความจริงเท็จด้วย
ar3 = np.ones([3,3])
ar4 = np.identity(3)
print(ar3)
print(ar4)
print(ar3==ar4)
print(ar3>ar4)
ผลลัพธ์
[[ True False False]
[False True False]
[False False True]]
[[False True True]
[ True False True]
[ True True False]]
การคำนวณอาเรย์ในลักษณะนี้ได้ผลเหมือนกับการที่เราใช้วังวน for เพื่อเข้าถึงสมาชิกในแต่ละตัวและคำนวณไปเรื่อยๆทีละตัว เพียงแต่จะเห็นว่าเขียนง่ายกว่ามาก นอกจากนี้ยังทำงานเร็วกว่าด้วย
เช่นยกตัวอย่าง สร้างอาเรย์ที่มีเลข 1 อยู่สิบล้านตัว จากนั้นก็ใช้ for เพื่อเข้าถึงเลขแต่ละตัวเพื่อให้มันคำนวณบวกตัวเอง
a = np.ones(10000000)
for i in range(len(a)):
a[i] = a[i]+a[i]
แบบนี้จะพบว่าต้องรอนานมากหลายวินาที (ขึ้นกับความเร็วเครื่อง) เพราะโปรแกรมต้องวนคำนวณซ้ำถึงสิบล้านครั้ง
แต่หากลองทำใหม่ให้ได้ผลลัพธ์แบบเดิมโดยใช้การคำนวณกับตัวอาเรย์โดยตรง
a = np.ones(10000000)
a = a+a
จะพบว่านอกจากจะเขียนสั้นกว่ามากแล้ว การคำนวณยังทำเสร็จภายในพริบตาเดียว แทบไม่ต้องรอเลย เพราะเบื้องหลังการทำงานนั้นการคำนวณทำอยู่บนภาษาซีซึ่งทำงานเร็วกว่าเป็น ร้อยเท้า
นี่ให้ข้อคิดที่สำคัญว่าในการจัดการกับข้อมูลภายในอาเรย์ นั้น แม้จะใช้ for เพื่อเข้าถึงสมาชิกแต่ละตัวได้เช่นเดียวกับลิสต์แต่ก็ไม่ควรทำ หากมีวิธีที่ไม่จำเป็นต้องใช้ for อยู่ละก็ควรใช้วิธีนั้นดีที่สุด
หากต้องการเห็นเวลาเปรียบเทียบเป็นตัวเลขชัดอาจลองใช้ฟังก์ชัน time วัดเวลา รายละเอียดลองอ่านดูใน
https://phyblas.hinaboshi.com/20160610 ไม่ใช่แค่เรื่องการคำนวณเท่านั้น อาเรย์ยังมีคำสั่งอีกหลายตัวสำหรับจัดการกับสมาชิกในอาเรย์โดยไม่ต้องวนทำ ซ้ำเอง ซึ่งจะได้กล่าวถึงต่อไป
การคำนวณอาเรย์กับตัวเลขเดี่ยวหรืออาเรย์ที่จำนวนมิติไม่เท่ากัน หากนำอาเรย์ไปคำนวณกับตัวเลขเดี่ยวจะเกิดการกระจายค่า โดยตัวเลขนั้นจะทำกับทุกตัวบนอาเรย์
print(ar3*4)
print(ar4-2)
ผลลัพธ์
[[ 4. 4. 4.]
[ 4. 4. 4.]
[ 4. 4. 4.]]
[[-1. -2. -2.]
[-2. -1. -2.]
[-2. -2. -1.]]
ถ้าขนาดของเมทริกซ์ไม่เท่ากันจะไม่สามารถนำมาคำนวณกันได้
np.ones([3,3])+np.ones([2,2]) # ได้ ValueError: operands could not be broadcast together with shapes (3,3) (2,2)
แต่กรณีที่จำนวนมิติต่างกันจะเกิดการกระจายค่าได้ถ้าหากตัวที่จำนวนมิติน้อยกว่ามีขนาดเท่ากับจำนวนของมิติหลังสุด
ตัวอย่างเช่นกรณีนี้อาเรย์ตัวหลังมีขนาด 3 และอาเรย์ตัวแรกมีขนาดของมิติหลังสุดเป็น 3 จึงกระจายได้
ar5 = np.ones([2,3])*3
ar6 = np.arange(3)
print(ar5)
print(ar6)
print(ar5*ar6)
ได้
[[ 3. 3. 3.]
[ 3. 3. 3.]]
[0 1 2]
[[ 0. 3. 6.]
[ 0. 3. 6.]]
กรณีสองกับสามมิติก็เช่นเดียวกัน เช่นแบบนี้สามารถกระจายได้
ar7 = np.ones([2,2,3])
ar8 = np.eye(2,3)
ฟังก์ชันคำนวณ นอกจาก การคำนวณพื้นฐานแล้ว numpy ยังได้เตรียมฟังก์ชันสำหรับคำนวณที่ใกล้เคียงกับในมอดูล math เช่น abs, sqrt, log, log10, exp, sin, cos, tan, arcsin, arccos, arctan, sinh, cosh, tanh, arcsinh, arccosh, arctanh
ฟังก์ชันพวกนี้มีในมอดูล math หมด ข้อแตกต่างก็คือฟังก์ชันเหล่านี้ใน numpy เมื่อใช้กับอาเรย์จะเกิดการกระจายเพื่อคำนวณกับสมาชิกทุกตัว แต่ก็สามารถใช้กับตัวเลขเดี่ยวได้ด้วยและจะให้ผลไม่ต่างจากของมอดูล math
ari1 = np.arange(1,9)
print(ari1)
print(np.sin(ari1))
print(np.log(ari1))
print(np.exp(np.log(ari1)))
print(np.log(ari1)/np.log(2))
ผลลัพธ์
[1 2 3 4 5 6 7 8]
[ 0.84147098 0.90929743 0.14112001 -0.7568025 -0.95892427 -0.2794155
0.6569866 0.98935825]
[ 0. 0.69314718 1.09861229 1.38629436 1.60943791 1.79175947
1.94591015 2.07944154]
[ 1. 2. 3. 4. 5. 6. 7. 8.]
[ 0. 1. 1.5849625 2. 2.32192809 2.5849625
2.80735492 3. ]
log ในที่นี้คือฐาน e ไม่สามารถเลือกเลขฐานได้เหมือนอย่างในมอดูล math ต้องใช้วิธีการมาหารด้วย log ของฐานที่ต้องการอีกที
นอกจากนี้ยังมีฟังก์ชันสำหรับเปลี่ยนหน่วยที่ใช้ในตรีโกณมิติ คือ deg2rad หรือ radians สำหรับเปลี่ยนจากองศาเป็นเรเดียน และ rad2deg หรือ degrees สำหรับเปลี่ยนจากเรเดียนเป็นองศา
print(np.radians(180)) # ได้ 3.1415926535897931
print(np.degrees(1) == np.rad2deg(1)) # ได้ True
ที่เหลือส่วนใหญ่ก็เหมือนกับมอดูล math สามารถอ่านได้ในเนื้อหา
ภาษาไพธอนเบื้องต้นบทที่ ๑๖ ถ้าเราไม่ใช้ฟังก์ชันคำนวณของ numpy เราสามารถใช้ for วนซ้ำเพื่อทำการคำนวณได้ แต่จะเขียนยุ่งยากกว่าแถมยังทำงานช้ากว่ามากด้วย
ลองสร้างอาเรย์ขนาดใหญ่ๆแล้วเทียบกันดู
ใช้ np.sin จะเป็นแบบนี้
a = np.arange(10000000)
sina = np.sin(a)
ถ้าใช้ math.sin จะเป็นแบบนี้
import math
a = np.arange(10000000)
sina = np.empty(10000000)
for i in range(10000000):
sina[i] = math.sin(a[i])
จะเห็นว่าเวลาที่ใช้นั้นต่างกันมาก
การคูณเมทริกซ์ เราสามารถนำอาเรย์มาคูณกันในลักษณะที่เหมือนเป็นการคูณเมทริกซ์ได้โดยใช้ฟังก์ชัน np.dot
ฟังก์ชันนี้จะใช้ได้ก็ต่อเมื่อจำนวนหลัก (แนวนอน) ของตัวซ้ายเท่ากับจำนวนแถว (แนวตั้ง) ของตัวขวาซึ่งก็เป็นกฎของการคูณเมทริกซ์ทั่วไป
a = np.array([[1,2],[3,4],[5,6]])
b = np.array([[1,3,5],[2,4,6]])
print(a)
print(b)
print(np.dot(a,b))
print(np.dot(b,a))
ผลลัพธ์
[[1 2]
[3 4]
[5 6]]
[[1 3 5]
[2 4 6]]
[[ 5 11 17]
[11 25 39]
[17 39 61]]
[[35 44]
[44 56]]
เพียงแต่ว่ากรณีที่เป็นอาเรย์มิติเดียวทั้งคู่นั้นตัวหลังจะถูกทรานสโพสอัตโนมัติ ผลที่ได้จะเหมือนเป็นการด็อตกันของเว็กเตอร์ (เว็กเตอร์คืออาเรย์หนึงมิติ) คือเอาสมาชิกแต่ละตัวมาคูณกันเป็นคู่ๆแล้วบวกกันทั้งหมด
print(np.dot(np.array([1,2,3]),np.array([2,3,4]))) # ได้ 20
การครอสเว็กเตอร์ ฟังก์ชันสำหรับครอสเว็กเตอร์คือ np.cross มีไว้สำหรับนำอาเรย์แทนเว็กเตอร์สองตัวมาครอสกัน
ตัวอย่าง สร้างอาเรย์แทนเว็กเตอร์ขึ้นมาสองตัวแล้วครอสกัน
a = np.array([2,2,3])
b = np.array([3,3,1])
print(np.cross(a,b)) # ได้ [-7 7 0]
อาเรย์ที่จะครอสกันได้ต้องมีสมาชิก ๒ หรือ ๓ ตัว (แทนเว็กเตอร์สองหรือสามมิติ) เท่านั้น กรณี ๓ ตัวจะได้ผลออกมาเป็น ๓ ตัว แต่กรณี ๒ ตัว ผลที่ได้จะออกมาเป็นตัวเดียว
a = np.array([3,2])
b = np.array([3,3])
print(np.cross(a,b)) # ได้ 3
กรณีที่ใช้กับอาเรย์มากกว่าหนึ่งมิติจะเป็นการจับมาครอสกันเป็นคู่ๆ
a = np.array([[2,2,3],[2,3,3]])
b = np.array([[3,3,1],[2,0,3]])
print(np.cross(a,b))
c = np.array([[2,3],[2,3],[1,4]])
d = np.array([[3,1],[0,3],[2,4]])
print(np.cross(c,d))
ได้
[[-7 7 0]
[ 9 0 -6]]
[-7 6 -4]
อ้างอิง