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



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



การขึ้นรูปใหม่
เราสามารถนำสมาชิกในอาเรย์มาจัดเรียงใหม่ได้ด้วยการใช้เมธอด reshape โดยกำหนดรูปร่างใหม่ของอาเรย์

reshape เป็นเมธอดที่จะทำการคืนอาเรย์ใหม่ที่มีขนาดมิติเท่ากับที่กำหนด
import numpy as np
arr = np.arange(8)
print(arr.reshape(2,4))

ผลลัพธ์
[[0 1 2 3]
 [4 5 6 7]]
 
รูปร่างใหม่ของอาเรย์จำเป็นจะต้องรองรับสมาชิกเท่ากับจำนวนสมาชิกในอาเรย์เดิม ไม่เช่นนั้นจะเกิดข้อผิดพลาด
arr = np.arange(10)
print(arr.reshape(3,3)) # ได้ ValueError: total size of new array must be unchanged

มีเมธอดอีกตัวที่คล้ายๆกันคือ resize ซึ่งมีหน้าที่เปลี่ยนแปลงโครงสร้างของอาเรย์ที่นำมากระทำให้เป็นไปตามที่กำหนด

resize จะไม่คืนค่าอะไรกลับมา แต่จะเป็นการเปลี่ยนรูปของอาเรย์นั้นไปเลย ต่างจาก reshape ซึ่งแค่ไว้คืนค่ากลับมาโดยไม่ได้เปลี่ยนตัวอาเรย์นั้นเอง

arr.resize(n,m) เทียบเท่ากับ arr = arr.reshape(n,m)

ตัวอย่าง
arr = np.arange(8)
arr.resize(2,4)
print(arr)

ผลลัพธ์
[[0 1 2 3]
 [4 5 6 7]]



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

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

ตัวอย่างการใช้ flatten
ioooioooi = np.eye(3,3)
print(ioooioooi)
ioooioooi = ioooioooi.flatten()
print(ioooioooi)

ผลลัพธ์
[[ 1.  0.  0.]
 [ 0.  1.  0.]
 [ 0.  0.  1.]]
[ 1.  0.  0.  0.  1.  0.  0.  0.  1.]

ตัวอย่างนี้จะใช้ ravel ก็ได้ผลแบบเดียวกัน

ข้อแตกต่างของ ravel กับ flatten ก็คือ flatten จะทำการคัดลอกสมาชิกในอาเรย์มา ในขณะที่ ravel จะแค่คืนค่ามุมมองที่อ้างอิงถึง

ลองยกตัวอย่างให้เห็นความแตกต่าง
x = np.array(((1,2,3),(4,5,6),(7,8,9)))
xfla = x.flatten()
xfla[0] = 100
print(xfla)
print(x)

ผลลัพธ์
[100   2   3   4   5   6   7   8   9]
[[1 2 3]
 [4 5 6]
 [7 8 9]]


xra = x.ravel()
xra[0] = 0
print(xra)
print(x)

ผลลัพธ์
[0 2 3 4 5 6 7 8 9]
[[0 2 3]
 [4 5 6]
 [7 8 9]]
 
จะเห็นว่า xfla ใช้ flatten ซึ่งเป็นการคัดลอกค่าแล้วสร้างข้อมูลขึ้นใหม่ ดังนั้นพอมีการแก้ไขค่าใน xfla ก็ไม่ได้กระทบอะไรกับ x

แต่ว่า xra นั้นใช้ ravel เป็นแค่การดึงมุมมองมาใช้ไม่ได้สร้างข้อมูลใหม่ ยังใช้หน่วยความจำเดียวกันกับ x อยู่ ดังนั้นพอมีการแก้ไขค่าใน xra แล้ว ค่าใน x ก็จะเปลี่ยนตาม

ravel จะเขียนในรูปฟังก์ชันหรือเมธอดก็ได้ เช่น np.ravel(x) มีค่าเท่ากับ x.ravel() แต่ไม่สามารถเขียน np.flatten(x) ได้ ต้องเขียน x.flatten() เท่านั้น



การสลับแกนของอาเรย์
สำหรับอาเรย์สองมิติสามารถสลับแกนได้ด้วยคำสั่ง transpose จะเขียนในรูปเมธอดหรือฟังก์ชันก็ได้
x = np.array(((1,2,3),(4,5,6)))
print(x)
print(np.transpose(x))
print(x.transpose())

ผลลัพธ์
[[1 2 3]
 [4 5 6]]
[[1 4]
 [2 5]
 [3 6]]
[[1 4]
 [2 5]
 [3 6]]
 
นอกจาก นี้ยังอาจหาค่าทรานสโพสได้อย่างง่ายๆโดยเขียนต่อท้ายอาเรย์ด้วย .T ก็ได้ ข้อแตกต่างคือ .T ไม่ใช่เมธอด ไม่ต้องมีวงเล็บ () ต่อท้าย
print(np.array([[1],[2],[3]]).T) ได้ # [[1 2 3]]

สำหรับมิติที่สูงขึ้นไปใช้เมธอด swapaxes โดยใส่เลขของมิติ (แกน) ทั้งสองที่ต้องการสลับลงไป โดยมิติแรกนับเป็น 0

ถ้าใช้กับสองมิติ ใส่เป็น (0,1) จะได้ผลเหมือนกับ transpose
x = np.array([[1,2],[3,4],[5,6]])
print(x)
print(x.swapaxes(0,1))

ผลลัพธ์
[[1 2]
 [3 4]
 [5 6]]
[[1 3 5]
 [2 4 6]]

swapaxes จะเขียนในรูปฟังก์ชันก็ได้เช่นกัน คือ np.swapaxes(x,0,1)



การเพิ่มมิติขึ้นมา
ปกติแล้วคำสั่ง transpose หรือ T จะใช้กับที่เป็นสองอาเรย์สองมิติ หากใช้กับอาเรย์ที่เป็นหนึ่งมิติจะไม่มีการเปลี่ยนแปลง
print(np.arange(4).T) # ได้ [0 1 2 3]

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

เช่น
print(np.arange(4)[None]) # ได้ [[0 1 2 3]]

แบบนี้ก็จะได้อาเรย์สองมิติ แม้ว่าแกนตั้งจะมีแค่แถวเดียวก็ตาม

เท่านี้ก็สามารถนำมาทรานสโพสได้
print(np.arange(4)[None].T)
# ได้
# [[0]
#  [1]
#  [2]
#  [3]]

นอกจากนี้เราอาจสลับแกนได้ทันทีด้วยการเติม :, ไว้ด้านหน้า None
print(np.arange(4)[:,None]) # ผลได้เหมือนตัวอย่างที่แล้ว

หากใช้กับอาเรย์สองมิติก็จะกลายเป็นอาเรย์สามมิติได้

นั่นหมายความว่าจากอาเรย์หนึ่งมิติหากใส่ [None] ๒ ครั้งก็จะกลายเป็นสามมิติได้
print(np.arange(4)[None][None]) # ได้ [[[0 1 2 3]]]
print(np.arange(4)[None,None]) # แบบนี้ก็ได้เหมือนกัน

เราสามารถใช้วิธีนี้เพื่อสลับแกนของอาเรย์สองมิติไปอยู่ในมิติที่สามได้
a = np.array([[1,2,3],[4,5,6]])
print(a)

#***ภาพเหล่านี้สร้างด้วยมายาไพธอน

print(a[:,None])


print(a[:,:,None])


print(a.T[:,None])


print(a.T[:,:,None])

นอกจากนี้แล้วยังมีวิธีการอีกอย่างที่ให้ผลเหมือนกัน คือใช้ฟังก์ชัน np.expand_dims
np.expand_dims(a,axis=0) เท่ากับ a[None]
np.expand_dims(a,axis=1) เท่ากับ a[:,None]
np.expand_dims(a,axis=2) เท่ากับ a[:,:,None]

แต่เนื่องจากเขียนยาวกว่าจึงดูเหมือนจะไม่เป็นที่นิยมนัก ใช้ [None] ง่ายกว่า แม้จะดูแล้วอาจเข้าใจยากสักหน่อยก็ตาม

เพื่อให้เข้าใจการทำงานของ None มากขึ้นอาจลองดูตัวอย่างนี้
d = np.array([[3,2,1],[4,6,7]])
print(d.shape) # ได้ (2, 3)
print(d[None].shape) # ได้ (1, 2, 3)
print(d[None,:,:].shape) # ได้ (1, 2, 3)
print(d[:,None].shape) # ได้ (2, 1, 3)
print(d[:,None,:].shape) # ได้ (2, 1, 3)
print(d[:,:,None].shape) # ได้ (2, 3, 1)

จะเห็นว่า None เข้าไปแทนตำแหน่งแกนไหนตรงนั้นจะมีขนาดเป็น 1 ส่วนแกนที่เหลือก็จะได้มิติตามอาเรย์เดิม

ที่จริงแล้ว None ซึ่งถูกใช้ในกรณีนี้มักถูกใช้เป็น np.newaxis มากกว่า เพราะดูแล้วเหมือนเข้าใจได้ง่าย แต่ np.newaxis ก็เป็นเหมือนตัวแปรตัวแทนของ None
print(np.newaxis) # ได้ None

ดังนั้นจึงไม่มีความจำเป็นต้องใช้ np.newaxis จริงๆ แต่อย่างไรก็ตามก็แล้วแต่คน หากเห็นว่า None มันดูไม่สวย ดูแล้วไม่สื่อความหมาย ก็อาจใช้ np.newaxis ก็ได้



การนำอาเรย์มาต่อเข้าด้วยกัน
numpy มีคำสั่งที่สะดวกที่ใช้ในการเอาอาเรย์มาต่อกัน ได้แก่
np.concatenate( (a, b),axis=i) นำอาเรย์มารวมกันตามแกนที่ i เมื่อรวมกันแล้วจะได้มิติเท่าเดิม
np.stack( (a, b),axis=i) นำอาเรย์มารวมกันให้ได้มิติเพิ่มขึ้นหนึ่งมิติ
np.hstack( (a, b) ) นำอาเรย์มารวมกันตามมิติที่หนึ่ง
np.vstack( (a, b) ) นำอาเรย์มารวมกันตามมิติที่สอง
np.column_stack( (a, b) ) นำอาเรย์หนึ่งมิติมารวมกันในอีกมิติแล้วทรานสโพส ผลเหมือน np.vstack( (a, b) ).T

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

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

ถ้าใช้กับอาเรย์หนึ่งมิติ
- hstack จะเป็นการเอาอาเรย์มาต่อกัน เท่ากับ np.concatenate( (a, b),axis=0)
- vstack จะเป็นการรวมกันให้เป็นหลายแถว เท่ากับ np.stack( (a,b),axis=0)

ตัวอย่าง
a = [1,1,1]
b = [2,2,2]
print(np.hstack((a,b))) # ได้ [1 1 1 2 2 2]
print(np.vstack((a,b))) # หรือ np.stack((a,b),axis=0)
# ได้
# [[1 1 1]
#  [2 2 2]]

print(np.stack((a,b),axis=1)) # หรือ print(np.column_stack((a,b)))
# ได้
# [[1 2]
#  [1 2]
#  [1 2]]

print(np.concatenate((a,b),axis=0)) # ได้ [1 1 1 2 2 2]

ถ้าใช้กับอาเรย์สองมิติ
- hstack จะเป็นการรวมกันตามแนวนอน เท่ากับ np.concatenate( (a, b),axis=1)
- vstack จะเป็นการรวมกันตามแนวตั้ง เท่ากับ np.concatenate( (a, b),axis=0)
- stack จะเป็นการรวมกันในแนวมิติที่สาม

ตัวอย่าง
a = [[1,1,1],[2,2,2]]
b = [[3,3,3],[4,4,4]]
print(np.hstack((a,b))) # หรือ print(np.concatenate((a,b),axis=1))
# ได้
# [[1 1 1 3 3 3]
#  [2 2 2 4 4 4]]

print(np.vstack((a,b))) # หรือ print(np.concatenate((a,b),axis=0))
# ได้
# [[1 1 1]
#  [2 2 2]
#  [3 3 3]
#  [4 4 4]]

print(np.stack((a,b)))
# ได้อาเรย์สามมิติดังภาพ



การแยกอาเรย์
หากต้องการแยกอาเรย์ออกเป็นส่วนๆไว้ในลิสต์ มีฟังก์ชันกลุ่มหนึ่งที่สามารถใช้ได้
split( (a, n),axis=i) แยกอาเรย์ตามแกนที่ i ออกเป็น n ส่วน
hsplit( (a, n),axis=i) แยกอาเรย์ตามมิติที่หนึ่ง ออกเป็น n ส่วน
vsplit( (a, n),axis=i) แยกอาเรย์ตามมิติที่สอง ออกเป็น n ส่วน

นอกจากนี้ก็มี dsplit ซึ่งเอาไว้แยกตามมิติที่สาม

ตัวอย่าง
a = np.arange(1,7).reshape(2,3)
print(np.hsplit(a,3)) # หรือ print(np.split(a,3,axis=1))
# ได้
# [array([[1],
#        [4]]), array([[2],
#        [5]]), array([[3],
#        [6]])]

print(np.vsplit(a,2)) # ได้ [array([[1, 2, 3]]), array([[4, 5, 6]])]
print(np.split(a,2)) # ได้ [array([[1, 2, 3]]), array([[4, 5, 6]])]

การจะใช้ฟังก์ชันแยกอาเรย์ได้นั้นจำนวนส่วนที่แบ่งจะต้องหารลงตัวกับจำนวนแถวหรือหลัก



การซ้ำอาเรย์
หากต้องการนำอาเรย์ตัวเดิมมาต่อกันหลายๆครั้งสามารถใช้ฟังก์ชัน np.tile

การใช้คือ np.tile(อาเรย์, จำนวนที่ซ้ำ)

ตัวอย่าง
a = np.arange(1,7).reshape(2,3)
print(a)
# ได้
# [[1 2 3]
#  [4 5 6]]

print(np.tile(a,3))
# ได้
# [[1 2 3 1 2 3 1 2 3]
#  [4 5 6 4 5 6 4 5 6]]

การใส่อาร์กิวเมนต์ตัวหลังเป็นแค่ตัวเลขเดี่ยวจะเป็นการซ้ำในแนวแกนสุดท้าย แต่ถ้าต้องการให้ซ้ำในแนวอื่นต้องเปลี่ยนเป็นใส่ทูเพิล
print(np.tile(a,(2,1)))

ได้
[[1 2 3]
 [4 5 6]
 [1 2 3]
 [4 5 6]]
 

print(np.tile(a,(2,3)))

ได้
[[1 2 3 1 2 3 1 2 3]
 [4 5 6 4 5 6 4 5 6]
 [1 2 3 1 2 3 1 2 3]
 [4 5 6 4 5 6 4 5 6]]

อีกตัวหนึ่งที่มีไว้สำหรับซ้ำอาเรย์เหมือนกันคือ np.repeat แต่จะต่างกันเล็กน้อยคือ repeat จะนำเอาอาเรย์มายุบเหลือมิติเดียว จากนั้นทำซ้ำโดยเอาตัวเดิมอยู่ติดกัน

ตัวอย่าง
a = np.arange(1,7).reshape(2,3)
print(a)
print(np.repeat(a,3))

ได้
[[1 2 3]
 [4 5 6]]
[1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6]

จะเห็นว่าแม้จะใช้อาเรย์สองมิติ แต่ก็ถูกยุบรวมเหลือมิติเดียว

หากต้องการให้คิดมิติด้วยก็เพิ่มคีย์เวิร์ด axis ลงไป
print(np.repeat(a,3,axis=0)) # ซ้ำตามแนวตั้ง
print(np.repeat(a,3,axis=1)) # ซ้ำตามแนวนอน

ได้
[[1 2 3]
 [1 2 3]
 [1 2 3]
 [4 5 6]
 [4 5 6]
 [4 5 6]]
[[1 1 1 2 2 2 3 3 3]
 [4 4 4 5 5 5 6 6 6]]



การกลับลำดับอาเรย์
numpy ไม่มีฟังก์ชันสำหรับกลับลำดับของอาเรย์โดยเฉพาะ แต่สามารถกลับลำดับของอาเรย์ได้โดยการเขียนแบบนี้ ar[::-1] แบบนี้จะเป็นการกลับลำดับของอาเรย์ในแกนแรก ถ้าต้องการกลับลำดับในแกนถัดมาก็ ar[:,::-1]

กรณีสองมิติ ถ้าต้องการกลับแนวตั้งเขียนเป็น [::-1] ถ้ากลับแนวนอนเขียนเป็น [:,::-1] ถ้ากลับทั้งสองแนวก็เป็น [::-1,::-1]

ตัวอย่าง
arii = np.arange(1,10).reshape(3,3)
print(arii)
print(arii[::-1])
print(arii[:,::-1])
print(arii[::-1,::-1])

ผลลัพธ์
[[1 2 3]
 [4 5 6]
 [7 8 9]]
[[7 8 9]
 [4 5 6]
 [1 2 3]]
[[3 2 1]
 [6 5 4]
 [9 8 7]]
[[9 8 7]
 [6 5 4]
 [3 2 1]]



อ้างอิง


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


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

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

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

หมวดหมู่

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

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

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- 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月

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

ไทย

日本語

中文