จะเข้าถึงคอลัมน์ ith ของอาร์เรย์หลายมิติ NumPy ได้อย่างไร


463

สมมติว่าฉันมี:

test = numpy.array([[1, 2], [3, 4], [5, 6]])

test[i]ได้รับฉันithบรรทัดของอาร์เรย์ (เช่น[1, 2]) ฉันจะเข้าถึงคอลัมน์ith ได้อย่างไร (เช่น[1, 3, 5]) นอกจากนี้สิ่งนี้จะเป็นการดำเนินการที่มีราคาแพงหรือไม่

คำตอบ:


687
>>> test[:,0]
array([1, 3, 5])

ในทำนองเดียวกัน

>>> test[1,:]
array([3, 4])

ช่วยให้คุณเข้าถึงแถว นี้จะครอบคลุมในข้อ 1.4 (ดัชนี) ของการอ้างอิง NumPy อย่างรวดเร็วอย่างน้อยก็ในประสบการณ์ของฉัน แน่นอนว่าเร็วกว่าการเข้าถึงแต่ละองค์ประกอบในลูป


11
สิ่งนี้สร้างสำเนาเป็นไปได้หรือไม่ที่จะได้รับการอ้างอิงเช่นฉันได้รับการอ้างอิงไปยังคอลัมน์การเปลี่ยนแปลงใด ๆ ในการอ้างอิงนี้จะปรากฏในอาร์เรย์เดิม
อันตราย

@harmands สิ่งนี้ไม่ได้สร้างสำเนามันสร้างมุมมอง
ล้าง

69

และถ้าคุณต้องการเข้าถึงมากกว่าหนึ่งคอลัมน์ในเวลาที่คุณสามารถทำได้:

>>> test = np.arange(9).reshape((3,3))
>>> test
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
>>> test[:,[0,2]]
array([[0, 2],
       [3, 5],
       [6, 8]])

แม้ว่าแน่นอนในกรณีนี้คุณไม่เพียง แต่เข้าถึงข้อมูลเท่านั้น คุณกำลังส่งคืนสำเนา (การจัดทำดัชนีแฟนซี)
John Greenall

14
test[:,[0,2]]เพียงแค่เข้าถึงข้อมูลเช่นtest[:, [0,2]] = somethingจะแก้ไขการทดสอบและไม่สร้างอาร์เรย์อื่น แต่copy_test = test[:, [0,2]]จริง ๆ แล้วสร้างสำเนาตามที่คุณพูด
Akavall

3
สิ่งนี้สร้างสำเนาเป็นไปได้ไหมที่จะได้รับการอ้างอิงเช่นฉันได้รับการอ้างอิงไปยังบางคอลัมน์การเปลี่ยนแปลงใด ๆ ในการอ้างอิงนี้จะปรากฏในอาร์เรย์เดิมหรือไม่
อันตราย

@ harman786 คุณสามารถกำหนดอาเรย์ที่ถูกแก้ไขใหม่ให้กับอาเรย์เก่าได้
Tamoghna Chowdhury

ทำไมtest[:,[0,2]]เข้าถึงข้อมูลในขณะที่test[:, [0, 2]][:, [0, 1]]ไม่? ดูเหมือนว่าใช้งานง่ายมากที่ทำสิ่งเดียวกันอีกครั้งมีผลที่แตกต่างกัน
mapf

65
>>> test[:,0]
array([1, 3, 5])

คำสั่งนี้ให้เวกเตอร์แถวกับคุณ, ถ้าคุณแค่อยากวนรอบมัน, ก็โอเค, แต่ถ้าคุณต้องการ hstack กับอาเรย์อื่นที่มีขนาด 3xN, คุณจะได้

ValueError: all the input arrays must have same number of dimensions

ในขณะที่

>>> test[:,[0]]
array([[1],
       [3],
       [5]])

ให้เวกเตอร์คอลัมน์แก่คุณเพื่อให้คุณสามารถดำเนินการต่อกันหรือ hstack

เช่น

>>> np.hstack((test, test[:,[0]]))
array([[1, 2, 1],
       [3, 4, 3],
       [5, 6, 5]])

1
การจัดทำดัชนียังมีคอลัมน์มากกว่าหนึ่งครั้งดังนั้นตัวอย่างสุดท้ายอาจทดสอบ [:, [0,1,0]] หรือทดสอบ [:, [ช่วง (test.shape [1]) + [0] ]
lib

5
+1 สำหรับการระบุ [:, [0]] vs [:, 0] เพื่อรับเวกเตอร์คอลัมน์แทนที่จะเป็นเวกเตอร์แถว พฤติกรรมที่ฉันกำลังมองหา +1 ด้วย lib สำหรับบันทึกการจัดทำดัชนีเพิ่มเติม คำตอบนี้ควรจะอยู่ตรงนั้นด้วยคำตอบที่ดีที่สุด
dhj

1
คำตอบนี้จะต้องเลือก
Gusev Slava

22

คุณสามารถเปลี่ยนตำแหน่งและส่งคืนแถวได้ด้วย:

In [4]: test.T[0]
Out[4]: array([1, 3, 5])

ฉันเคยทำสิ่งนี้มาระยะหนึ่งแล้วก่อนจะมองหาวิธีเข้าถึงคอลัมน์ที่เร็วที่สุดฉันสงสัยว่านี่จะเร็วกว่าช้ากว่าหรือเหมือนกับการทดสอบ [:, [0]]
José Chamorro


5

แม้ว่าคำถามจะได้รับคำตอบให้ฉันพูดถึงความแตกต่างบางอย่าง

สมมติว่าคุณสนใจคอลัมน์แรกของอาร์เรย์

arr = numpy.array([[1, 2],
                   [3, 4],
                   [5, 6]])

ดังที่คุณทราบจากคำตอบอื่น ๆ แล้วเพื่อให้ได้ในรูปแบบของ "row vector" (อาร์เรย์ของรูปร่าง(3,)) คุณใช้การแบ่งส่วน:

arr_c1_ref = arr[:, 1]  # creates a reference to the 1st column of the arr
arr_c1_copy = arr[:, 1].copy()  # creates a copy of the 1st column of the arr

ในการตรวจสอบว่าอาร์เรย์เป็นมุมมองหรือสำเนาของอาร์เรย์อื่นคุณสามารถทำสิ่งต่อไปนี้:

arr_c1_ref.base is arr  # True
arr_c1_copy.base is arr  # False

ดูndarray.base

นอกเหนือจากความแตกต่างที่เห็นได้ชัดระหว่างสอง (การแก้ไขarr_c1_refจะมีผลต่อarr) จำนวนของขั้นตอนไบต์สำหรับการสำรวจแต่ละครั้งจะแตกต่างกัน:

arr_c1_ref.strides[0]  # 8 bytes
arr_c1_copy.strides[0]  # 4 bytes

ดูความก้าวหน้า ทำไมสิ่งนี้จึงสำคัญ ลองนึกภาพว่าคุณมีอาร์เรย์ที่ใหญ่มากAแทนที่จะเป็นarr:

A = np.random.randint(2, size=(10000,10000), dtype='int32')
A_c1_ref = A[:, 1] 
A_c1_copy = A[:, 1].copy()

และคุณต้องการที่จะคำนวณผลรวมของทุกองค์ประกอบของคอลัมน์แรกคือหรือA_c1_ref.sum() A_c1_copy.sum()การใช้เวอร์ชันที่คัดลอกนั้นเร็วกว่ามาก:

%timeit A_c1_ref.sum()  # ~248 µs
%timeit A_c1_copy.sum()  # ~12.8 µs

นี่เป็นเพราะจำนวนก้าวที่แตกต่างกันที่กล่าวถึงก่อนหน้า:

A_c1_ref.strides[0]  # 40000 bytes
A_c1_copy.strides[0]  # 4 bytes

แม้ว่าอาจดูเหมือนว่าการใช้การคัดลอกคอลัมน์นั้นดีกว่า แต่ก็ไม่จริงเสมอไปสำหรับเหตุผลที่การทำสำเนาต้องใช้เวลาและใช้หน่วยความจำเพิ่มขึ้น (ในกรณีนี้มันใช้เวลาประมาณ 200 approxs ในการสร้างA_c1_copy) อย่างไรก็ตามถ้าเราต้องการสำเนาในตอนแรกหรือเราต้องทำการดำเนินการต่าง ๆ ในคอลัมน์เฉพาะของอาร์เรย์และเราก็โอเคกับการเสียสละหน่วยความจำสำหรับความเร็วแล้วการทำสำเนาเป็นวิธีที่จะไป

ในกรณีที่เราสนใจที่จะทำงานกับคอลัมน์เป็นส่วนใหญ่มันอาจเป็นความคิดที่ดีที่จะสร้างอาเรย์ของเราในลำดับของคอลัมน์หลัก ('F') แทนลำดับของแถวหลัก ('C') (ซึ่งเป็นค่าเริ่มต้น ) จากนั้นทำการแบ่งตามเดิมเพื่อรับคอลัมน์โดยไม่คัดลอก:

A = np.asfortranarray(A)  # or np.array(A, order='F')
A_c1_ref = A[:, 1]
A_c1_ref.strides[0]  # 4 bytes
%timeit A_c1_ref.sum()  # ~12.6 µs vs ~248 µs

ตอนนี้การดำเนินการรวม (หรืออื่น ๆ ) ในมุมมองคอลัมน์จะเร็วขึ้นมาก

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

A.T[1,:].strides[0]  # 40000

3
>>> test
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

>>> ncol = test.shape[1]
>>> ncol
5L

จากนั้นคุณสามารถเลือกคอลัมน์ที่ 2 - 4 ด้วยวิธีนี้:

>>> test[0:, 1:(ncol - 1)]
array([[1, 2, 3],
       [6, 7, 8]])
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.