NumPy เลือกดัชนีคอลัมน์เฉพาะต่อแถวโดยใช้รายการดัชนี


93

ฉันกำลังดิ้นรนเพื่อเลือกคอลัมน์เฉพาะต่อแถวของเมทริกซ์ NumPy

สมมติว่าฉันมีเมทริกซ์ต่อไปนี้ซึ่งฉันจะเรียกX:

[1, 2, 3]
[4, 5, 6]
[7, 8, 9]

ฉันยังมีlistดัชนีคอลัมน์ต่อทุกแถวที่ฉันจะเรียกY:

[1, 0, 2]

ฉันต้องการรับค่า:

[2]
[4]
[9]

แทนที่จะใช้listดัชนีYฉันยังสามารถสร้างเมทริกซ์ที่มีรูปร่างเหมือนกับXโดยที่ทุกคอลัมน์เป็นbool/ intในค่าช่วง 0-1 ซึ่งระบุว่านี่เป็นคอลัมน์ที่ต้องการหรือไม่

[0, 1, 0]
[1, 0, 0]
[0, 0, 1]

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

ฉันจึงสงสัยว่ามีทางออกที่ดีกว่านี้หรือไม่?


คำตอบที่ดีกว่าสำหรับคุณหรือไม่? stackoverflow.com/a/17081678/5046896
GoingMyWay

คำตอบ:


104

หากคุณมีอาร์เรย์บูลีนคุณสามารถทำการเลือกโดยตรงตามสิ่งนั้น:

>>> a = np.array([True, True, True, False, False])
>>> b = np.array([1,2,3,4,5])
>>> b[a]
array([1, 2, 3])

ในการทำตามตัวอย่างเริ่มต้นของคุณคุณสามารถทำสิ่งต่อไปนี้:

>>> a = np.array([[1,2,3], [4,5,6], [7,8,9]])
>>> b = np.array([[False,True,False],[True,False,False],[False,False,True]])
>>> a[b]
array([2, 4, 9])

นอกจากนี้คุณยังสามารถเพิ่มarangeและทำการเลือกโดยตรงได้ แต่ขึ้นอยู่กับว่าคุณสร้างอาร์เรย์บูลีนของคุณอย่างไรและโค้ดของคุณมีลักษณะอย่างไร YMMV

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

หวังว่าจะช่วยได้โปรดแจ้งให้เราทราบหากคุณมีคำถามเพิ่มเติม


13
+1 สำหรับตัวอย่างโดยใช้arange. สิ่งนี้มีประโยชน์อย่างยิ่งสำหรับฉันในการดึงบล็อกที่แตกต่างกันจากเมทริกซ์หลายตัว (โดยทั่วไปแล้วกรณี 3 มิติของตัวอย่างนี้)
Griddo

1
สวัสดีคุณช่วยอธิบายได้ไหมว่าทำไมเราต้องใช้arangeแทน:? ฉันรู้ว่าวิธีของคุณได้ผลและของฉันไม่ได้ผล แต่ฉันอยากจะเข้าใจว่าทำไม
marcotama

@tamzord เนื่องจากเป็นอาร์เรย์ที่เป็นตัวเลขและไม่ใช่รายการ vanilla python ดังนั้น:ไวยากรณ์จึงไม่ทำงานในลักษณะเดียวกัน
Slater Victoroff

1
@SlaterTyranus ขอบคุณสำหรับการตอบกลับ ความเข้าใจของฉันหลังจากอ่านบางส่วนเป็นที่ผสม:ด้วยวิธีการการจัดทำดัชนีขั้นสูง: "สำหรับคนที่ย่อยพื้นที่พร้อม:ใช้การได้รับการจัดทำดัชนีขั้นสูง" ความเข้าใจของฉันถูกต้องหรือไม่?
marcotama

@tamzord อธิบายความหมายของ "sub-space"
Slater Victoroff

36

คุณสามารถทำสิ่งนี้:

In [7]: a = np.array([[1, 2, 3],
   ...: [4, 5, 6],
   ...: [7, 8, 9]])

In [8]: lst = [1, 0, 2]

In [9]: a[np.arange(len(a)), lst]
Out[9]: array([2, 4, 9])

เพิ่มเติมเกี่ยวกับการสร้างดัชนีอาร์เรย์หลายมิติ: http://docs.scipy.org/doc/numpy/user/basics.indexing.html#indexing-multi-dimensional-arrays


2
พยายามที่จะเข้าใจว่าเหตุใดจึงต้องใช้ arange แทนที่จะเป็นเพียง ':' หรือ range
MadmanLee

@MadmanLee สวัสดีการใช้:จะแสดงผลลัพธ์หลายlen(a)ครั้งแทนการระบุดัชนีของแต่ละแถวจะพิมพ์ผลลัพธ์ที่คาดการณ์ไว้
GoingMyWay

1
ฉันคิดว่านี่เป็นวิธีที่ถูกต้องและสง่างามในการแก้ปัญหานี้
GoingMyWay

6

วิธีง่ายๆอาจมีลักษณะดังนี้:

In [1]: a = np.array([[1, 2, 3],
   ...: [4, 5, 6],
   ...: [7, 8, 9]])

In [2]: y = [1, 0, 2]  #list of indices we want to select from matrix 'a'

range(a.shape[0]) จะกลับมา array([0, 1, 2])

In [3]: a[range(a.shape[0]), y] #we're selecting y indices from every row
Out[3]: array([2, 4, 9])

1
โปรดพิจารณาเพิ่มคำอธิบาย
souki

@souki ฉันได้เพิ่มคำอธิบายแล้ว ขอบคุณ
Dhaval Mayatra

6

numpyเวอร์ชันล่าสุดได้เพิ่มtake_along_axis(และput_along_axis) ที่ทำดัชนีนี้อย่างหมดจด

In [101]: a = np.arange(1,10).reshape(3,3)                                                             
In [102]: b = np.array([1,0,2])                                                                        
In [103]: np.take_along_axis(a, b[:,None], axis=1)                                                     
Out[103]: 
array([[2],
       [4],
       [9]])

มันทำงานในลักษณะเดียวกับ:

In [104]: a[np.arange(3), b]                                                                           
Out[104]: array([2, 4, 9])

แต่มีการจัดการแกนที่แตกต่างกัน โดยเฉพาะอย่างยิ่งมีจุดมุ่งหมายเพื่อใช้ผลลัพธ์ของargsortargmaxและ


3

คุณสามารถทำได้โดยใช้ตัวทำซ้ำ แบบนี้:

np.fromiter((row[index] for row, index in zip(X, Y)), dtype=int)

เวลา:

N = 1000
X = np.zeros(shape=(N, N))
Y = np.arange(N)

#@Aशwini चhaudhary
%timeit X[np.arange(len(X)), Y]
10000 loops, best of 3: 30.7 us per loop

#mine
%timeit np.fromiter((row[index] for row, index in zip(X, Y)), dtype=int)
1000 loops, best of 3: 1.15 ms per loop

#mine
%timeit np.diag(X.T[Y])
10 loops, best of 3: 20.8 ms per loop

1
OP กล่าวว่าควรทำงานอย่างรวดเร็วในอาร์เรย์ขนาดใหญ่ดังนั้นเกณฑ์มาตรฐานของคุณจึงไม่เป็นตัวแทน ฉันอยากรู้ว่าวิธีสุดท้ายของคุณทำงานอย่างไรสำหรับอาร์เรย์ที่ใหญ่กว่า (มาก)!

@moarningsun: อัพเดท np.diag(X.T[Y])ช้ามาก ... แต่np.diag(X.T)เร็วมาก (10us) ฉันไม่รู้ว่าทำไม
Kei Minagawa

0

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

X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])
Y = np.array([1, 0, 2, 2])

np.diag(X.T[Y])

เป็นขั้นเป็นตอน:

อาร์เรย์เดิม:

>>> X
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])

>>> Y
array([1, 0, 2, 2])

เปลี่ยนเพื่อให้สามารถจัดทำดัชนีได้อย่างถูกต้อง

>>> X.T
array([[ 1,  4,  7, 10],
       [ 2,  5,  8, 11],
       [ 3,  6,  9, 12]])

รับแถวตามลำดับ Y

>>> X.T[Y]
array([[ 2,  5,  8, 11],
       [ 1,  4,  7, 10],
       [ 3,  6,  9, 12],
       [ 3,  6,  9, 12]])

เส้นทแยงมุมควรชัดเจนแล้ว

>>> np.diag(X.T[Y])
array([ 2,  4,  9, 12]

1
วิธีนี้ใช้งานได้จริงและดูหรูหรามาก อย่างไรก็ตามฉันพบว่าวิธีนี้จะระเบิดอย่างสมบูรณ์เมื่อคุณจัดการกับอาร์เรย์ขนาดใหญ่ ในกรณีของฉัน NumPy กลืน 30GB ของการแลกเปลี่ยนและเติม SSD ของฉัน ขอแนะนำให้ใช้วิธีการจัดทำดัชนีขั้นสูงแทน
5nefarious
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.