การแบ่งส่วนของอาร์เรย์ NumPy 2d หรือฉันจะแยก submatrix mxm ออกจากอาร์เรย์ nxn (n> m) ได้อย่างไร


174

ฉันต้องการแบ่งอาร์เรย์ NumPy nxn ฉันต้องการที่จะแยกโดยพลการเลือกแถวม. และคอลัมน์ของอาร์เรย์นั้น (คือไม่มีรูปแบบใด ๆ ในจำนวนของแถว / คอลัมน์) ทำให้มันใหม่ MXM อาร์เรย์ สำหรับตัวอย่างนี้ให้เราบอกว่าอาร์เรย์เป็น 4x4 และฉันต้องการที่จะดึงอาร์เรย์ 2x2 ออกมา

นี่คืออาร์เรย์ของเรา:

from numpy import *
x = range(16)
x = reshape(x,(4,4))

print x
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]

บรรทัดและคอลัมน์ที่จะลบเหมือนกัน กรณีที่ง่ายที่สุดคือเมื่อฉันต้องการแยก submatrix 2x2 ที่อยู่ที่จุดเริ่มต้นหรือตอนท้ายเช่น:

In [33]: x[0:2,0:2]
Out[33]: 
array([[0, 1],
       [4, 5]])

In [34]: x[2:,2:]
Out[34]: 
array([[10, 11],
       [14, 15]])

แต่ถ้าฉันต้องการลบการผสมแถว / คอลัมน์อื่นอีกล่ะ? ถ้าฉันต้องการลบบรรทัด / แถวแรกและแถวที่สามการแยกเมทริกซ์ย่อยจะทำ[[5,7],[13,15]]อย่างไร สามารถมีองค์ประกอบของแถว / บรรทัดใด ๆ ฉันอ่านที่ไหนสักแห่งที่ฉันต้องทำดัชนีอาร์เรย์ของฉันโดยใช้อาร์เรย์ / รายการดัชนีสำหรับทั้งแถวและคอลัมน์ แต่ดูเหมือนจะไม่ทำงาน:

In [35]: x[[1,3],[1,3]]
Out[35]: array([ 5, 15])

ฉันพบวิธีหนึ่งซึ่งก็คือ:

    In [61]: x[[1,3]][:,[1,3]]
Out[61]: 
array([[ 5,  7],
       [13, 15]])

ปัญหาแรกของเรื่องนี้คือมันอ่านยาก แต่ฉันสามารถอยู่กับมันได้ หากใครบางคนมีทางออกที่ดีกว่าแน่นอนฉันอยากได้ยิน

สิ่งอื่นคือฉันอ่านฟอรัมที่การทำดัชนีอาร์เรย์ด้วยอาร์เรย์บังคับให้ NumPy ทำสำเนาของอาร์เรย์ที่ต้องการดังนั้นเมื่อทำการรักษาด้วยอาร์เรย์ขนาดใหญ่สิ่งนี้อาจกลายเป็นปัญหาได้ เหตุใดกลไกนี้จึงทำงานอย่างไร

คำตอบ:


62

ดังที่สเวนระบุไว้x[[[0],[2]],[1,3]]จะให้ 0 และ 2 แถวที่ตรงกับ 1 และ 3 คอลัมน์ในขณะที่x[[0,2],[1,3]]จะคืนค่า x [0,1] และ x [2,3] ในอาร์เรย์

มีฟังก์ชั่นที่มีประโยชน์สำหรับทำตัวอย่างแรกที่ฉันให้, numpy.ix_. x[numpy.ix_([0,2],[1,3])]คุณสามารถทำสิ่งเดียวกันเป็นตัวอย่างแรกของฉันด้วย การทำเช่นนี้จะช่วยให้คุณไม่ต้องเข้าไปในวงเล็บใหญ่พิเศษเหล่านั้นทั้งหมด


111

เพื่อตอบคำถามนี้เราต้องดูว่าการจัดทำดัชนีอาร์เรย์หลายมิติทำงานใน Numpy ได้อย่างไร ก่อนอื่นสมมติว่าคุณมีอาร์เรย์xจากคำถามของคุณ บัฟเฟอร์ที่กำหนดให้xจะมีจำนวนเต็ม 16 จำนวนจาก 0 ถึง 15 ถ้าคุณเข้าถึงองค์ประกอบหนึ่งกล่าวว่าx[i,j]NumPy จะต้องค้นหาตำแหน่งหน่วยความจำขององค์ประกอบนี้ซึ่งสัมพันธ์กับจุดเริ่มต้นของบัฟเฟอร์ สิ่งนี้ทำได้โดยการคำนวณผลกระทบi*x.shape[1]+j(และคูณด้วยขนาดของ int เพื่อให้ได้หน่วยความจำจริงชดเชย)

ถ้าคุณแยก subarray โดยหั่นพื้นฐานเช่นวัตถุที่เกิดขึ้นจะแบ่งปันบัฟเฟอร์อ้างอิงกับy = x[0:2,0:2] xแต่จะเกิดอะไรขึ้นถ้าคุณเห็นด้วยy[i,j]? NumPy ไม่สามารถใช้i*y.shape[1]+jในการคำนวณออฟเซ็ตลงในอาร์เรย์ได้เนื่องจากข้อมูลที่อยู่yในหน่วยความจำไม่ต่อเนื่องกัน

NumPy แก้ปัญหานี้โดยการแนะนำความก้าวหน้า เมื่อคำนวณหน่วยความจำออฟเซ็ตสำหรับการเข้าถึงx[i,j]สิ่งที่คำนวณจริงคือi*x.strides[0]+j*x.strides[1](และสิ่งนี้มีปัจจัยสำหรับขนาดของ int อยู่แล้ว):

x.strides
(16, 4)

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

y.shape
(2,2)
y.strides
(16, 4)

ด้วยวิธีนี้การคำนวณหน่วยความจำออฟเซ็ตสำหรับy[i,j]จะให้ผลลัพธ์ที่ถูกต้อง

แต่ NumPy ควรทำอะไรเพื่ออะไรเช่นนี้z=x[[1,3]]? zกลไกความก้าวหน้าอย่างจะไม่อนุญาตให้จัดทำดัชนีที่ถูกต้องหากบัฟเฟอร์เดิมที่ใช้สำหรับ ในทางทฤษฎี NumPy สามารถเพิ่มกลไกที่ซับซ้อนกว่าแบบก้าวกระโดด แต่สิ่งนี้จะทำให้การเข้าถึงองค์ประกอบค่อนข้างแพง แต่ก็ท้าทายความคิดทั้งหมดของอาร์เรย์ นอกจากนี้มุมมองจะไม่เป็นวัตถุที่มีน้ำหนักเบาจริงๆอีกต่อไป

นี้ได้รับการคุ้มครองในเชิงลึกในเอกสาร NumPy ในการจัดทำดัชนี

โอ้และเกือบลืมคำถามที่เกิดขึ้นจริงของคุณ: นี่คือวิธีการสร้างดัชนีที่มีหลายรายการทำงานตามที่คาดไว้:

x[[[1],[3]],[1,3]]

นี่เป็นเพราะอาร์เรย์ดัชนีถูกถ่ายทอดไปยังรูปร่างทั่วไป แน่นอนว่าสำหรับตัวอย่างนี้คุณสามารถทำการแบ่งส่วนพื้นฐานได้ด้วย:

x[1::2, 1::2]

มันเป็นไปได้ที่จะทำ subclass arrays เพื่อให้มีวัตถุ "slcie-view" ซึ่งจะทำการแมปดัชนีไปยังอาร์เรย์เดิมอีกครั้ง ที่อาจตอบสนองความต้องการของ OP
jsbueno

@jsbueno: จะใช้งานได้กับรหัส Python แต่ไม่ใช่สำหรับ C / Fortran ตามปกติที่ Scipy / Numpy ล้อมรอบ กิจวัตรที่ห่อนั้นคือพลังของ Numpy
Dat Chu

ซู .. อะไรคือความแตกต่างระหว่าง x [[[1], [3]], [1,3] และ x [[1,3],:] [:, [1,3]]? ฉันหมายถึงมีตัวแปรที่ดีกว่าการใช้งานหรือไม่?
levesque

1
@JC: x[[[1],[3]],[1,3]]สร้างหนึ่งอาร์เรย์ใหม่เท่านั้นในขณะที่x[[1,3],:][:,[1,3]]คัดลอกสองครั้งดังนั้นให้ใช้อาร์เรย์แรก
Sven Marnach

@JC: หรือใช้วิธีการจากคำตอบของจัสติน
Sven Marnach

13

ฉันไม่คิดว่าx[[1,3]][:,[1,3]]มันอ่านยาก หากคุณต้องการชัดเจนเกี่ยวกับความตั้งใจของคุณคุณสามารถทำสิ่งต่อไปนี้

a[[1,3],:][:,[1,3]]

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

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

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


10

หากคุณต้องการข้ามแถวอื่นและคอลัมน์อื่น ๆ คุณสามารถทำได้โดยใช้การแบ่งส่วนพื้นฐาน:

In [49]: x=np.arange(16).reshape((4,4))
In [50]: x[1:4:2,1:4:2]
Out[50]: 
array([[ 5,  7],
       [13, 15]])

สิ่งนี้ส่งคืนมุมมองไม่ใช่สำเนาของอาร์เรย์ของคุณ

In [51]: y=x[1:4:2,1:4:2]

In [52]: y[0,0]=100

In [53]: x   # <---- Notice x[1,1] has changed
Out[53]: 
array([[  0,   1,   2,   3],
       [  4, 100,   6,   7],
       [  8,   9,  10,  11],
       [ 12,  13,  14,  15]])

ขณะที่z=x[(1,3),:][:,(1,3)]ใช้การจัดทำดัชนีขั้นสูงและส่งคืนสำเนา:

In [58]: x=np.arange(16).reshape((4,4))
In [59]: z=x[(1,3),:][:,(1,3)]

In [60]: z
Out[60]: 
array([[ 5,  7],
       [13, 15]])

In [61]: z[0,0]=0

โปรดทราบว่าxไม่เปลี่ยนแปลง:

In [62]: x
Out[62]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

หากคุณต้องการเลือกแถวและคอลัมน์โดยพลการคุณจะไม่สามารถใช้การแบ่งส่วนพื้นฐานได้ คุณจะต้องใช้การจัดทำดัชนีขั้นสูงโดยใช้บางอย่างเช่นx[rows,:][:,columns]ที่ใดrowsและcolumnsเรียงลำดับอย่างไร แน่นอนว่าสิ่งนี้จะให้สำเนาของอาร์เรย์ดั้งเดิมของคุณไม่ใช่มุมมอง สิ่งนี้เป็นสิ่งที่ควรคาดหวังเนื่องจากอาร์เรย์ numpy ใช้หน่วยความจำต่อเนื่อง (ที่มี strides คงที่) และจะไม่มีวิธีในการสร้างมุมมองที่มีแถวและคอลัมน์โดยพลการ


5

ด้วย numpy คุณสามารถผ่านส่วนย่อยสำหรับแต่ละองค์ประกอบของดัชนีได้ดังนั้นx[0:2,0:2]ตัวอย่างข้างต้นจะใช้ได้

หากคุณต้องการข้ามคอลัมน์หรือแถวเท่า ๆ กันคุณสามารถส่งชิ้นส่วนที่มีสามองค์ประกอบ (เช่นเริ่มต้นหยุดขั้นตอน)

อีกครั้งสำหรับตัวอย่างของคุณด้านบน:

>>> x[1:4:2, 1:4:2]
array([[ 5,  7],
       [13, 15]])

ซึ่งโดยพื้นฐานแล้ว: หั่นในมิติแรกโดยเริ่มต้นที่ดัชนี 1 หยุดเมื่อดัชนีเท่ากับหรือมากกว่า 4 และเพิ่ม 2 เข้ากับดัชนีในแต่ละรอบ เช่นเดียวกันสำหรับมิติที่สอง อีกครั้ง: ใช้งานได้เฉพาะกับขั้นตอนคงที่เท่านั้น

ไวยากรณ์ที่คุณต้องทำในสิ่งที่แตกต่างกันมากภายใน - สิ่งที่x[[1,3]][:,[1,3]]จริง ๆ แล้วคือการสร้างอาร์เรย์ใหม่ซึ่งประกอบด้วยแถวที่ 1 และ 3 จากอาร์เรย์ดั้งเดิมเท่านั้น (จากx[[1,3]]ส่วนที่ทำ) จากนั้นทำการแบ่งอีกครั้ง - สร้างอาร์เรย์ที่สาม - รวมเฉพาะ คอลัมน์ 1 และ 3 ของอาร์เรย์ก่อนหน้า


1
โซลูชันนี้ใช้งานไม่ได้เนื่องจากเป็นเฉพาะกับแถว / คอลัมน์ที่ฉันพยายามแยก ลองนึกภาพเหมือนกันในเมทริกซ์ 50x50 เมื่อฉันต้องการแยกแถว / คอลัมน์ 5,11,12,32,39,45 ไม่มีวิธีการทำเช่นนี้กับชิ้นส่วนที่เรียบง่าย ขออภัยถ้าฉันไม่ชัดเจนในคำถามของฉัน
levesque

3

ฉันมีคำถามที่คล้ายกันที่นี่: เขียนใน sub-ndarray ของ ndarray ในแบบ pythonian มากที่สุด งูหลาม 2

ติดตามโซลูชันของการโพสต์ก่อนหน้าสำหรับกรณีของคุณวิธีแก้ไข:

columns_to_keep = [1,3] 
rows_to_keep = [1,3]

การใช้ ix_:

x[np.ix_(rows_to_keep, columns_to_keep)] 

ซึ่งเป็น:

array([[ 5,  7],
       [13, 15]])

0

ฉันไม่แน่ใจว่ามันมีประสิทธิภาพแค่ไหน แต่คุณสามารถใช้ range () เพื่อตัดทั้งสองแกนได้

 x=np.arange(16).reshape((4,4))
 x[range(1,3), :][:,range(1,3)] 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.