6.3. การสร้างอาร์เรย์¶
ทุกตัวอย่างในหน้าถัดไปเริ่มต้นด้วย ndarray ที่พร้อมใช้งานอยู่แล้ว หน้านี้คือรายการวิธีการสร้างอาร์เรย์นั้น มี constructor สี่กลุ่ม:
จาก Python iterable -- รูปแบบ literal / list / tuple ทั่วไป
กรอกข้อมูลล่วงหน้าตามรูปร่างที่กำหนด -- ศูนย์ หนึ่ง ค่าคงที่ หรือเมทริกซ์เอกลักษณ์
สร้างเป็นลำดับ -- ค่าที่เป็นช่วงหรือเว้นระยะเท่ากัน
ห่อบัฟเฟอร์ที่อยู่ใน RAM แล้ว -- กรณีอุปกรณ์ต่อพ่วง
ทุก constructor รับคีย์เวิร์ด dtype= และใช้ค่าเริ่มต้นเป็น float ข้อมูลจาก sensor มักต้องการ dtype ที่เล็กกว่าค่าเริ่มต้น
ทุกตัวอย่างด้านล่างเริ่มต้นด้วย:
from ulab import numpy as np
6.3.1. จาก Python iterable¶
array() สร้าง ndarray จาก iterable ของตัวเลขใดก็ได้:
a = np.array([1, 2, 3, 4])
print(a)
ผลลัพธ์:
array([1.0, 2.0, 3.0, 4.0], dtype=float)
iterable ที่ซ้อนกันจะสร้างอาร์เรย์หลายมิติ iterable ภายในต้องมีความยาวเท่ากันทั้งหมด มิฉะนั้นจะเกิด ValueError
m = np.array([[1, 2, 3],
[4, 5, 6]], dtype=np.uint8)
ndarray ที่มีอยู่แล้วก็เป็นอินพุตที่ถูกต้อง array() จะคัดลอกเสมอ หากต้องการหลีกเลี่ยงการคัดลอกเมื่อไม่จำเป็น ให้ใช้ asarray()
b = np.asarray(a, dtype=np.float) # same dtype -> no copy
6.3.2. กรอกข้อมูลล่วงหน้าตามรูปร่างที่กำหนด¶
เมื่อทราบรูปร่างเป้าหมายแต่ยังไม่มีเนื้อหา ให้จัดสรรบัฟเฟอร์ล่วงหน้าและเขียนข้อมูลลงไปในภายหลัง:
zeros()-- กรอกด้วยศูนย์ones()-- กรอกด้วยหนึ่งfull()-- กรอกด้วยค่าที่กำหนดempty()-- นามแฝงของzeros()(ulabไม่ทิ้งบัฟเฟอร์ไว้โดยไม่กำหนดค่า)eye()-- เมทริกซ์คล้ายเอกลักษณ์ขนาดN-คูณ-Mที่มีค่าหนึ่งบนแนวทแยงkdiag()-- เมทริกซ์แนวทแยงจากเวกเตอร์ หรือแนวทแยงของเมทริกซ์
np.zeros((3, 3)) # 3x3 of zeros
np.ones(5, dtype=np.uint8) # length-5 vector of ones
np.full((2, 3), 7, dtype=np.int8) # 2x3, all 7
np.eye(4) # 4x4 identity
np.diag([1, 2, 3]) # 3x3, [1, 2, 3] on the diagonal
อาร์กิวเมนต์ shape เป็นได้ทั้งจำนวนเต็มเดียว (สำหรับอาร์เรย์ 1 มิติ) หรือ tuple
6.3.3. สร้างเป็นลำดับ¶
arange()-- ค่าที่เว้นระยะเท่ากันคล้ายกับrange()ที่มีอยู่ใน Python แต่จะคืนค่าเป็นndarrayเสมอ:np.arange(0, 10, 2) # array([0, 2, 4, 6, 8])linspace()-- จุดnumจุดที่เว้นระยะเท่ากันระหว่างสองขีดจำกัด โดยรวมขอบบนเมื่อendpoint=Truenp.linspace(0, 1, num=11) # 0.0, 0.1, ..., 1.0logspace()-- จุดที่เว้นระยะเชิงเรขาคณิตstartและstopคือ เลขชี้กำลัง ไม่ใช่จุดปลาย ผลลัพธ์จะอยู่ในช่วงจากbase ** startถึงbase ** stopnp.logspace(0, 3, num=4) # 1.0, 10.0, 100.0, 1000.0meshgrid()-- สร้างเมทริกซ์พิกัดสองตัวจากอาร์เรย์ 1 มิติสองตัว เพื่อให้ฟังก์ชันต่อพิกเซลf(x, y)สามารถประเมินผลทั่วทั้งกริดในการเรียกแบบเวกเตอร์เดียว จาก x-vector ความยาวWและ y-vector ความยาวHmeshgridจะคืนเมทริกซ์สองตัวขนาดH-คูณ-W:Xคือ x-vector ที่ซ้ำตลอดทุกแถวYคือ y-vector ที่ซ้ำตลอดทุกคอลัมน์ ดังนั้นX[i, j]คือพิกัด x และY[i, j]คือพิกัด y ของเซลล์ที่แถวiและคอลัมน์jx = np.arange(4) # [0, 1, 2, 3] y = np.arange(3) # [0, 1, 2] X, Y = np.meshgrid(x, y) # X = [[0, 1, 2, 3], # [0, 1, 2, 3], # [0, 1, 2, 3]] # Y = [[0, 0, 0, 0], # [1, 1, 1, 1], # [2, 2, 2, 2]]
f(X, Y)จะประเมินผลฟังก์ชันที่ทุกเซลล์ของกริดในนิพจน์เดียว ตัวอย่างเช่น แผนที่ระยะห่างจากจุดกึ่งกลางบนเฟรม(H, W)คือnp.sqrt((X - cx)**2 + (Y - cy)**2)จากเมทริกซ์ที่meshgrid()คืนมา
6.3.4. การรวมอาร์เรย์¶
concatenate() รวม tuple ของอาร์เรย์ตามแกนที่มีอยู่:
a = np.array([[1, 2], [3, 4]], dtype=np.uint8)
b = np.array([[5, 6]], dtype=np.uint8)
np.concatenate((a, b), axis=0)
# array([[1, 2], [3, 4], [5, 6]], dtype=uint8)
อินพุตทั้งหมดต้องมี dtype และ ndim เดียวกัน และต้องตรงกันในทุกแกนยกเว้นแกนที่รวม concatenate() จัดสรรอาร์เรย์ใหม่ที่ใหญ่พอจะเก็บอินพุตทุกตัวและคัดลอกข้อมูลเข้าไป จึงเหมาะสำหรับการรวมอาร์เรย์ที่มีอยู่แล้วครั้งเดียว แต่ไม่เหมาะสำหรับ streaming loop ซึ่งควรจัดสรรปลายทางล่วงหน้าครั้งเดียวและเขียนผ่าน slice assignment แทน
6.3.5. การห่อบัฟเฟอร์ที่มีอยู่แล้ว¶
constructor ที่มีประโยชน์ที่สุดบนกล้องคือ frombuffer() ซึ่งตีความบัฟเฟอร์ bytes-like ที่มีอยู่แล้วใหม่เป็น ndarray 1 มิติ โดยไม่คัดลอก ไบต์แม้แต่ไบต์เดียว:
buf = bytearray(8)
audio = np.frombuffer(buf, dtype=np.int16)
# 4 int16 samples, sharing memory with buf
การเขียนผ่าน audio จะปรากฏใน buf และในทางกลับกัน dtype ที่เลือกต้องหารความยาวบัฟเฟอร์ได้ลงตัว
offset= ข้ามส่วนหัวที่ต้นบัฟเฟอร์ count= จำกัดจำนวนองค์ประกอบที่อ่าน:
np.frombuffer(buf, dtype=np.uint8, offset=2, count=4)
นี่คือ constructor ที่เหมาะสมเมื่ออุปกรณ์ต่อพ่วงส่งบัฟเฟอร์ดิบให้แอปพลิเคชัน เช่น ตัวอย่าง ADC ใน bytearray หรือ payload ที่ดึงจาก SPI ไบต์ที่อุปกรณ์ต่อพ่วงเขียนคืออาร์เรย์
เมื่ออุปกรณ์ต่อพ่วงเขียนค่าหลายไบต์ในลำดับไบต์ที่ CPU ของกล้องอ่านไม่ถูกต้องโดยธรรมชาติ byteswap() จะสลับลำดับไบต์ของแต่ละองค์ประกอบเพื่อให้ค่าอ่านถูกต้อง โดยค่าเริ่มต้นจะคืนอาร์เรย์ใหม่ การส่ง inplace=True จะแก้ไขต้นทางในที่เดิม
frombuffer() รองรับเฉพาะ dtype ที่ numpy กำหนดเอง สำหรับอุปกรณ์ต่อพ่วงที่ผลิตตัวอย่างจำนวนเต็ม 32 บิต from_int32_buffer() และฟังก์ชันที่เกี่ยวข้องจะแปลงเป็น float ในการผ่านเดียว
6.3.6. การตัดทอนการแสดงผล¶
การพิมพ์อาร์เรย์ขนาดใหญ่จะแสดงเฉพาะองค์ประกอบแรกและท้ายไม่กี่ตัว พร้อม ... ตรงกลาง เพื่อไม่ให้เทอร์มินัลของ IDE เต็มไปด้วยค่านับพัน:
>>> print(np.arange(1000, dtype=np.uint16))
array([0, 1, 2, ..., 997, 998, 999], dtype=uint16)
set_printoptions() แก้ไขค่าขีดแบ่งเมื่อการดีบักต้องการดูบัฟเฟอร์ทั้งหมด:
np.set_printoptions(threshold=2000) # print up to 2000 elements in full
np.set_printoptions(edgeitems=10) # 10 items at each end, not 3
get_printoptions() อ่านการตั้งค่าปัจจุบันกลับมาเป็น dict