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



numpy & matplotlib เบื้องต้น บทที่ ๑๗: การคำนวณและวิเคราะห์ข้อมูลภายในอาเรย์
เขียนเมื่อ 2016/06/11 23:11
แก้ไขล่าสุด 2021/09/28 16:42
ในบทที่ ๓ ได้พูดถึงการนำอาเรย์สองอันขึ้นไปมาทำการคำนวณกันแล้ว แต่ยังไม่ได้พูดถึงการคำนวณและวิเคราะห์ข้อมูลภายในแต่ละอาเรย์

numpy มีฟังก์ชันที่ใช้ในการจัดการข้อมูลภายในอาเรย์อยู่มากมาย ในบทนี้จะแนะนำส่วนหนึ่ง
np.sum ผลรวม
np.min หรือ np.amin ค่าต่ำสุด
np.max หรือ np.amax ค่าสูงสุด
np.ptp ค่าสูงสุดลบต่ำสุด
np.argmin ตำแหน่งต่ำสุด
np.argmax ตำแหน่งสูงสุด
np.mean ค่าเฉลี่ยเลขคณิต
np.median มัธยฐาน
np.std ส่วนเบี่ยงเบนมาตรฐาน
np.var ความแปรปรวน
np.cumsum ผลรวมสะสม
np.sort จัดเรียงค่าของสมาชิกในอาเรย์
np.argsort หาลำดับการจัดเรียงค่าของสมาชิกในอาเรย์



การหาค่าสูงสุดและต่ำสุด
มีฟังก์ชันที่ใช้หาค่าสูงสุดของสมาชิกในอาเรย์อยู่ ๒ ตัวคือ np.max กับ np.amax ซึ่งความจริงแล้วเป็นฟังก์ชันเดียวกัน เพียงแต่ต่างกันที่ max สามารถเขียนในรูปเมธอดของอาเรย์ได้ นั่นคือ np.max(ar) เท่ากับ ar.max()

ค่าต่ำสุดก็เช่นเดียวกัน มี np.min กับ np.amin

ตัวอย่าง
import numpy as np
ar1 = np.random.randint(0,100,(4,5))
print(ar1)
# ได้
# [[ 3 55 98 20 62]
#  [34 33 12 99 26]
#  [90 55 27 82 40]
#  [25 21 64 49 57]]

print(np.max(ar1)) # ได้ 99
print(np.min(ar1)) # ได้ 3

นอกจากนี้ยังสามารถหาค่าสูงสุดต่ำสุดแยกเป็นแต่ละแกนได้ด้วย โดยเติมคีย์เวิร์ด axis เข้าไป โดยระบุแกนที่จะบวก

กรณีสองมิติจะได้ว่า axis=0 เป็นการคิดตามแนวตั้ง axis=1 เป็นการคิดตามแนวนอน
print(np.min(ar1,axis=0)) # ได้ [ 3 21 12 20 26]
print(np.max(ar1,axis=0)) # ได้ [90 55 98 99 62]
print(np.min(ar1,axis=1)) # ได้ [ 3 12 27 21]
print(np.max(ar1,axis=1)) # ได้ [98 99 90 64]

นอกจากนี้ก็มีฟังก์ชัน np.ptp ซึ่งเอาไว้หาค่าสูงสุดลบค่าต่ำสุด (max-min)
print(np.ptp(ar1)) # ได้ 96
print(np.ptp(ar1,axis=0)) # ได้ [87 34 86 79 36]
print(np.ptp(ar1,axis=1)) # ได้ [95 87 63 43]



การหาจุดที่มีค่าสูงสุดและต่ำสุด
ฟังก์ชันที่ใช้หาตำแหน่งที่มีค่าสูงสุดคือ np.argmax และต่ำสุดคือ np.argmin มีลักษณะเดียวกับ np.max และ np.min คือเขียนในรูปเมธอดของอาเรย์ได้
x = np.arange(0,10,0.2)
y = (x-3)**2+7
print(np.argmin(y)) # ได้ 15
print(x[np.argmin(y)]) # ได้ 3.0

ในตัวอย่างนี้ y เป็นฟังก์ชันพาราโบลาร์ หากจะหาว่าจุดต่ำสุดอยู่ที่ x เท่าไหร่ก็ต้องหาดัชนีในอาเรย์ซึ่งให้ค่า y ต่ำสุด ซึ่งทำได้โดยใช้ np.argmin จากนั้นก็เอาตำแหน่งที่ได้มาใช้ใน x ก็จะได้คำตอบเป็น 3

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

ตัวอย่าง
ar2 = np.random.randint(0,100,(3,5))
print(ar2)
# ได้
# [[72 88 50 36 90]
#  [14 46 19 11 12]
#  [91 22 21 66 10]]

print(np.argmax(ar2)) # ได้ 10
print(np.argmin(ar2)) # ได้ 14

แต่หากต้องการจะได้ตำแหน่งพิกัดจำเป็นจะต้องทำการบางอย่างเพิ่มเติม นั่นคือใช้ฟังก์ชัน unravel_index
ar3 = np.random.randint(0,100,(4,5))
print(ar3)
# ได้
# [[49 65  7  1 85]
#  [ 8 66 56 94  7]
#  [13  2 33 67 70]
#  [97  0  7 19 25]]

print(np.argmax(ar3)) # ได้ 15
print(np.argmin(ar3)) # ได้ 16
print(np.unravel_index(np.argmax(ar3),ar3.shape)) # ได้ (3, 0)
print(np.unravel_index(np.argmin(ar3),ar3.shape)) # ได้ (3, 1)
print(np.unravel_index(15,(4,5))) # ได้ (3, 0)
print(np.unravel_index(16,(4,5))) # ได้ (3, 1)
print(np.unravel_index(14,(4,5))) # ได้ (2, 4)

unravel_index ในที่นี้จะนำค่าดัชนีที่ได้จาก argmax และ argmin ซึ่งเป็นดัชนีในหนึ่งมิติมาแปลงให้เป็นสองมิติ โดยใช้รูปร่างของอาเรย์ ar3.shape เป็นตัวพิจารณาเพื่อไล่ดูว่าหากถูกเปลี่ยนเป็นสองมิติแล้วดัชนีค่านี้จะกลาย เป็นเท่าไหร่

กรณีที่ argmin และ argmax มีการระบุคีย์เวิร์ด axis ไปจะเป็นการบอกตำแหน่งของค่าสูงสุดตามแกน
ar4 = np.random.randint(0,100,(2,4))
print(ar4)
# ได้
# [[69 93 27 60]
#  [61 69 42 85]]


print(np.argmax(ar4,axis=0)) # ได้ [0 0 1 1]
print(np.argmin(ar4,axis=0)) # ได้ [1 1 0 0]
print(np.argmax(ar4,axis=1)) # ได้ [1 3]
print(np.argmin(ar4,axis=1)) # ได้ [2 2]



การหาผลรวม, ค่าเฉลี่ย และมัธยฐาน
ของสมาชิกในอาเรย์หาได้ด้วยฟังก์ชัน np.sum ค่าเฉลี่ยนหาได้จาก np.mean ส่วนมัธยฐานใช้ฟังก์ชัน np.median

sum และ mean สามารถเขียนในรูปเมธอดของอาเรย์ได้ ส่วน median จะต้องเขียนเป็นฟังก์ชันเท่านั้น เขียน ar.median() แบบนี้ไม่ได้

ตัวอย่าง
ar5 = np.random.randint(0,100,(5,8))
print(ar5)
# ได้
# [[77 14 52 27 77 37 89 58]
#  [31 35 25 61 30 78 13  8]
#  [16 51 91 69 25 45 67 20]
#  [29 99 95 97 13 24 67 68]
#  [53 72 59  6 54 31 79 19]]

print(np.sum(ar5)) # ได้ 1961
print(np.mean(ar5)) # ได้ 49.025
print(np.median(ar5)) # ได้ 51.5
print(np.sum(ar5,axis=0)) # ได้ [206 271 322 260 199 215 315 173]
print(np.mean(ar5,axis=0)) # ได้ [ 41.2  54.2  64.4  52.   39.8  43.   63.   34.6]
print(np.median(ar5,axis=0)) # ได้ [ 31.  51.  59.  61.  30.  37.  67.  20.]
print(np.sum(ar5,axis=1)) # ได้ [431 281 384 492 373]
print(np.mean(ar5,axis=1)) # ได้ [ 53.875  35.125  48.     61.5    46.625]
print(np.median(ar5,axis=1)) # ได้ [ 55.   30.5  48.   67.5  53.5]

การใช้ฟังก์ชันเหล่านี้นอกจากจะเขียนง่ายแล้วก็ยังเร็วกว่ามาใช้การวนซ้ำเพื่อคำนวณเองด้วย

ยกตัวอย่างเช่น np.sum ถ้าหากลองสร้างอาเรย์ที่มีสิบล้านตัวแล้วใช้การวนซ้ำบวกสะสมไปทีละตัวตามธรรมดาจะเป็นแบบนี้
x = np.ones(10000000)
s = 0
for c in x:
    s += c

ซึ่งจะรู้สึกได้ว่าใช้เวลาไปนานพอสมควร แต่หากใช้ np.sum จะเป็นแบบนี้
x = np.ones(10000000)
s = np.sum(x)

จะเห็นว่าเขียนแค่สั้นๆ แถมยังคำนวณเสร็จในพริบตา

ข้อความระวังอย่างหนึ่งก็คือในฟังก์ชันมาตรฐานของไพธอนก็มี sum อยู่แล้ว และก็สามารถใช้กับอาเรย์ได้เช่นกัน เช่น
x = np.ones(10000000)
s = sum(x)

จะ เห็นว่าต่างจากตัวอย่างที่แล้วแค่ตรงที่ว่าไม่มี .np เท่านั้น แต่ผลที่ได้ก็คือการคำนวณช้ากว่ามาก เพราะ sum นั้นจะคำนวณอาเรย์ในลักษณะเหมือนเป็นลิสต์ จึงไม่ได้ประโยชน์จากความสามารถในการคำนวณเร็วของอาเรย์

ที่น่าสนใจอีกอย่างหนึ่งก็คือ np.sum ก็ใช้กับลิสต์ได้เช่นกัน เช่น
x = [1]*10000000
s = np.sum(x)

แต่ก็จะเห็นว่ามันทำงานช้า ไม่ต่างจากใช้ sum ธรรมดา

สรุปก็คือ เราจะได้ความเร็วที่เหนือชั้นนี้เมื่อใช้ฟังก์ชันของ numpy ทำงานกับอาเรย์ ดังนั้นหากรู้ว่าจะทำงานกับอาเรย์ก็ให้ใช้ฟังก์ชันของ numpy เป็นหลัก และหลีกเลี่ยงการใช้ for โดยไม่จำเป็น แบบนี้เราก็จะสามารถใช้ numpy ได้อย่างเต็มประสิทธิภาพ



การหาส่วนเบี่ยงเบนมาตรฐานและความแปรปรวน
ส่วนเบี่ยงเบนมาตรฐานหาได้จากฟังก์ชัน np.std ส่วนความแปรปรวนหาได้จากฟังก์ชัน np.var ทั้งสองตัวนี้สามารถเขียนในรูปของเมธอดของอาเรย์ได้

ตัวอย่าง
ar6 = np.random.randint(0,100,(3,3))
print(ar6)
# ได้
# [[75 35 78]
#  [92 84 82]
#  [22 75 26]]

print(np.std(ar6)) # ได้ 25.79740632
print(np.var(ar6)) # ได้ 665.50617284
print(np.std(ar6,axis=0)) # ได้ [ 29.81051269  21.29684379  25.50816863]
print(np.var(ar6,axis=0)) # ได้ [ 888.66666667  453.55555556  650.66666667]
print(np.std(ar6,axis=1)) # ได้ [ 19.60158724   4.3204938   24.0970261 ]
print(np.var(ar6,axis=1)) # ได้ [ 384.22222222   18.66666667  580.66666667]
print(np.std(ar6)**2-np.var(ar6)) # ได้ 2.2737367544323206e-13

ส่วนเบี่ยงเบนมาตรฐานเป็นสิ่งที่บอกถึงความกระจัดกระจายของข้อมูล ลองวาดฮิสโทแกรมเพื่อเปรียบเทียบดู
import numpy as np
import matplotlib.pyplot as plt

ar7 = np.random.randint(110,210,(3000,3)) # สร้างค่าสุ่ม ๓๐๐๐ แถว ๓ หลัก จุดกึ่งกลางอยู่ที่ 160
ar7[:,1] *= 2 # ปรับค่าหลักกลางให้กระจายมากเป็น 2 เท่า จุดกึ่งกลางย้ายไปอยู่ที่ 320
ar7[:,2] *= 4 # ปรับค่าหลักสุดท้ายให้กระจายมากเป็น 4 เท่า จุดกึ่งกลางย้ายไปอยู่ที่ 640
chalia = np.mean(ar7,axis=0) # หาค่าเฉลี่ยแต่ละหลัก
biangben = np.std(ar7,axis=0) # หาส่วนเบี่ยงเบนมาตรฐานแต่ละหลัก
# สร้างลิสต์ที่เก็บข้อความบรรยายกราฟทั้ง ๓
cb = [u'ค่าเฉลี่ย %.1f\nส่วนเบี่ยงเบนมาตรฐาน %.1f'%(c,b) for (c,b) in zip(chalia,biangben)]
# วาดฮิสโทแกรม
plt.hist(ar7,bins=100,stacked=1,label=cb,ec='k')
plt.legend(loc=1,prop={'family':'Tahoma','size':14}) # ใส่คำอธิบาย
plt.show()





ผลรวมสะสมของอาเรย์
บางครั้งเราอาจต้องการหาผลรวมของอาเรย์ที่ไล่จากแถวแรกไปจนถึงแถวหนึ่งๆ ซึ่งสามารถทำได้ด้วยฟังก์ชัน np.cumsum หรือจะเขียนในรูปเมธอดของอาเรย์ก็ได้

ฟังก์ชัน np.cumsum มีไว้หาผลรวมสะสมของอาเรย์ ถ้าไม่ได้กำหนดค่า axis จะได้เป็นอาเรย์หนึ่งมิติ แต่ถ้าระบุ axis จะเป็นการหาผลรวมสะสมตามแนวแกนนั้น

ตัวอย่าง
ar8 = np.random.randint(0,200,(2,5))
print(ar8)
# ได้
# [[ 84  92  25  82 102]
#  [190  94 119  36 136]]

print(np.cumsum(ar8)) # ได้ [ 84 176 201 283 385 575 669 788 824 960]
print(np.cumsum(ar8,axis=0))
# ได้
# [[ 84  92  25  82 102]
#  [274 186 144 118 238]]

print(np.cumsum(ar8,axis=1))
# ได้
# [[ 84 176 201 283 385]
#  [190 284 403 439 575]]

ลองวาดกราฟเปรียบเทียบระหว่างค่าของตัวอาเรย์กับ cumsum ของอาเรย์
arr = np.random.normal(1,2,201)
ax1 = plt.subplot(2,1,1,ylabel='arr')
ax1.spines['bottom'].set_position(('data',0))
ax1.plot(np.arange(201),arr)
ax2 = plt.subplot(2,1,2,ylabel='arr.cumsum()')
ax2.plot(np.arange(201),arr.cumsum())
plt.show()



สามารถนำมาใช้ประโยชน์ได้เช่นถ้าต้องการหาตำแหน่งของวัตถุที่เคลื่อนที่อยู่ ก็เอาตำแหน่งที่วัตถุเคลื่อนที่ได้ในแต่ละช่วงมา cumsum ก็จะได้ตำแหน่ง

ลองวาดดู กราฟแสดงตำแหน่งของวัตถุที่มีการเคลื่อนที่แบบสุ่มตามเวลา
doen = np.random.rand(7,2000)-0.5
tamnaeng = doen.cumsum(axis=1)
for i in range(7):
    plt.plot(np.arange(2000),tamnaeng[i])
plt.show()



ส่วนอันนี้ดูตำแหน่งการเคลื่อนที่ในสองมิติ
doen_x = np.random.rand(8,5000)-0.5
doen_y = np.random.rand(8,5000)-0.5
tamnaeng_x = doen_x.cumsum(axis=1)
tamnaeng_y = doen_y.cumsum(axis=1)
plt.figure(figsize=(6,6))
for i in range(8):
    plt.plot(tamnaeng_x[i],tamnaeng_y[i],lw=0.2)
plt.show()





การจัดเรียงค่าภายในอาเรย์
ฟังก์ชันที่ใช้จัดเรียงค่าภายในอาเรย์คือ np.sort โดยจะจัดเรียงตามแกนที่ระบุในคีย์เวิร์ด axis แต่ถ้าไม่ระบุจะจัดเรียงตามแกนสุดท้าย

sort สามารถเขียนในรูปเมธอดของอาเรย์ได้ แต่ความหมายต่างกัน np.sort(ar) เป็นการคืนอาเรย์ที่จัดเรียงแล้วโดยไม่ได้เปลี่ยนแปลงอาเรย์ตัวเก่า แต่ ar.sort() เป็นการจัดเรียงตัวอาเรย์นั้นและไม่มีค่าคืนกลับ

ตัวอย่าง
ar9 = np.random.randint(0,100,(4,4))
print(ar9)
# ได้
# [[11 59 84 34]
#  [66 59 46 48]
#  [87 28 31 97]
#  [ 8 27 62 59]]

print(np.sort(ar9))
# ได้
# [[11 34 59 84]
#  [46 48 59 66]
#  [28 31 87 97]
#  [ 8 27 59 62]]

print(np.sort(ar9,axis=0))
# ได้
# [[ 8 27 31 34]
#  [11 28 46 48]
#  [66 59 62 59]
#  [87 59 84 97]]

print(ar9.sort(axis=1)) # ได้ None
print(ar9)
# ได้
# [[11 34 59 84]
#  [46 48 59 66]
#  [28 31 87 97]
#  [ 8 27 59 62]]

นอกจากนี้ยังมีฟังก์ชัน argsort ที่เอาไว้หาลำดับของการจัดเรียงค่า

ตัวอย่าง
ar10 = np.random.randint(0,100,(3,4))
print(ar10)
# ได้
# [[89 72 55 65]
#  [74 35  2 21]
#  [31 30 51 87]]

print(np.argsort(ar10))
# ได้
# [[2 3 1 0]
#  [2 3 1 0]
#  [1 0 2 3]]

print(np.argsort(ar10,axis=0))
# ได้
# [[2 2 1 1]
#  [1 1 2 0]
#  [0 0 0 2]]

print(ar10.argsort(axis=1))
# ได้
# [[2 3 1 0]
#  [2 3 1 0]
#  [1 0 2 3]]



อ้างอิง


<< บทที่แล้ว     บทถัดไป >>
หน้าสารบัญ


-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python >> numpy

ไม่อนุญาตให้นำเนื้อหาของบทความไปลงที่อื่นโดยไม่ได้ขออนุญาตโดยเด็ดขาด หากต้องการนำบางส่วนไปลงสามารถทำได้โดยต้องไม่ใช่การก๊อปแปะแต่ให้เปลี่ยนคำพูดเป็นของตัวเอง หรือไม่ก็เขียนในลักษณะการยกข้อความอ้างอิง และไม่ว่ากรณีไหนก็ตาม ต้องให้เครดิตพร้อมใส่ลิงก์ของทุกบทความที่มีการใช้เนื้อหาเสมอ

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
ภาษา javascript
ภาษา mongol
ภาษาศาสตร์
maya
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



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

  ค้นหาบทความ

  บทความแนะนำ

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

ไทย

日本語

中文