Array indexing (אינדוקס במערכים)#

ל־NumPy יש כמה דרכים לגשת לטווח אינדקסים במערכים.

Slicing (פריסה)#

בדומה לרשומות בפייתון, גם מערכים ניתנים לפריסה (slice). מאחר שמערכים יכולים להיות רב־ממדיים, יש לציין פריסה לכל ממד.

ניצור מערך בגודל 3x4, כלומר שלוש שורות וארבעה עמודות:

🤖 Ask PyProf.
import numpy as np
# Create the following rank 2 array with shape (3, 4)
a = np.array([[1,2,3,4], 
              [5,6,7,8], 
              [9,10,11,12]])

חיתוך (Slicing) של מערך#

נשתמש ב־slicing כדי לבחור תת־מערך: שתי השורות הראשונות (אינדקסים 0 ו־1) ועמודות 1 ו־2 (האינדקסים האמצעיים).

# Use slicing to pull out the subarray consisting of the first 2 rows
# and columns 1 and 2; b is the following array of shape (2, 2):
b = a[:2, 1:3]

קשר בין מערך ותת־מערך#

חשוב לדעת: חיתוך במערכים של NumPy מחזיר מבט (View) אל הנתונים המקוריים, ולא עותק. לכן שינוי בתת־מערך ישפיע על המערך המקורי.

# A slice of an array is a view into the same data, so modifying it
# will modify the original array.
print(a[0, 1])   
b[0, 0] = 77     # b[0, 0] is the same piece of data as a[0, 1]
print(a[0, 1])   
2
77

ניתן לשלב אינדוקס עם פריסה. שילוב כזה מפיק מערך בדרגה נמוכה יותר מהמקור.

אינדוקס של שורות: Rank 1 לעומת Rank 2#

כאשר משלבים אינדוקס עם מספר שלם וחיתוך, מתקבלת שורה בייצוג חד־ממדי (Rank 1). כאשר משתמשים רק בפריסה (slice), נשמרת הדרגה המקורית (Rank 2) ולכן מקבלים עדיין מערך דו־ממדי.

row_r1 = a[1, :]    # Rank 1 view of the second row of a
row_r2 = a[1:2, :]  # Rank 2 view of the second row of a
print(row_r1, row_r1.shape)  
print(row_r2, row_r2.shape)  
[5 6 7 8] (4,)
[[5 6 7 8]] (1, 4)

אינדוקס של עמודות: Rank 1 לעומת Rank 2#

אותו עיקרון חל גם על עמודות:

  • אינדוקס עם מספר שלם יחזיר וקטור חד־ממדי.

  • שימוש בפריסה ישאיר את העמודה כתת־מערך דו־ממדי.

col_r1 = a[:, 1]
col_r2 = a[:, 1:2]
print(col_r1, col_r1.shape)  
print(col_r2, col_r2.shape)  
[77  6 10] (3,)
[[77]
 [ 6]
 [10]] (3, 1)

Integer array indexing (אינדוקס במערך באמצעות מספרים שלמים)#

בעת פריסה מתקבל תמיד תת־מערך. אינדוקס בעזרת מערכי מספרים שלמים מאפשר לבנות מערך חדש מאיברים שרירותיים של מערך אחר.

ניצור מערך בגודל 3x2, כלומר שלוש שורות ושתי עמודות:

import numpy as np
a = np.array([[1,2],
              [3, 4],
              [5, 6]])
print(a)
[[1 2]
 [3 4]
 [5 6]]

ניתן לבחור איברים ממקומות שונים במערך בו־זמנית, באמצעות מתן רשימות של אינדקסים. התוצאה היא מערך חדש שבו מופיעים הערכים שנבחרו לפי המיקומים שצוינו.

print(a[[0, 1, 2],
        [0, 1, 0]]) 
[1 4 5]

התוצאה הקודמת זהה לבחירה ידנית של אותם איברים אחד־אחד, אך כאן עושים זאת בפקודה אחת.

print(np.array([a[0, 0], a[1, 1], a[2, 0]]))  
[1 4 5]

באמצעות אינדוקס שלם אפשר גם לבחור את אותו איבר מספר פעמים, ולבנות ממנו מערך חדש.

print(a[[0, 0], [1, 1]])  
[2 2]

גם כאן, אפשר לכתוב את אותו הדבר בעזרת גישה רגילה לכל איבר, ולבנות ידנית מערך חדש.

print(np.array([a[0, 1], a[0, 1]]))  
[2 2]

טריק נפוץ: לבחור/לעדכן איבר אחד מכל שורה באמצעות אינדקסים#

נבנה מערך בגודל 4x3 (ארבע שורות ושלושה עמודות):

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

נגדיר מערך של אינדקסים (b) שאומר לנו איזה עמודה לבחור בכל שורה. במקרה הזה: בשורה הראשונה נבחר את העמודה ה־0, בשנייה את ה־2, בשלישית שוב את ה־0, וברביעית את ה־1.

b = np.array([0, 2, 0, 1])

השילוב a[np.arange(4), b] מאפשר לבחור איבר אחד מכל שורה:

  • np.arange(4) מייצר את הרשימה [0,1,2,3] - כלומר את כל השורות.

  • b נותן את האינדקס של העמודה המתאימה בכל שורה.

ביחד מתקבלים ארבעה איברים - אחד מכל שורה.

print(a[np.arange(4), b])
[ 1  6  7 11]

ניתן גם לעדכן ישירות את אותם איברים שנבחרו, למשל להוסיף להם 10. כיוון שמדובר בגישה ישירה לנתוני המערך, השינוי מתבצע בתוך המערך המקורי.

a[np.arange(4), b] += 10
print(a)
[[11  2  3]
 [ 4  5 16]
 [17  8  9]
 [10 21 12]]

Boolean array indexing (אינדוקס בוליאני)#

מאפשר לבחור איברים שמקיימים תנאי.

נבנה מערך בגודל 3x2:

import numpy as np

a = np.array([[1,2],
              [3,4],
              [5,6]])
print(a)
[[1 2]
 [3 4]
 [5 6]]

נשתמש במסיכה בוליאנית (Boolean Mask). נבדוק אילו איברים במערך גדולים מ־2. התוצאה היא מערך חדש באותו גודל, המכיל ערכי True/False לפי התנאי.

bool_idx = (a > 2)
print(bool_idx)
[[False False]
 [ True  True]
 [ True  True]]

כאשר משתמשים במערך הבוליאני כאינדקס, מתקבל מערך חד־ממדי עם כל האיברים עבורם התנאי מתקיים.

print(a[bool_idx])
[3 4 5 6]

ניתן לבצע את כל השלבים יחד בשורה אחת, על ידי הכנסת התנאי ישירות לאינדוקס.

print(a[a > 2])
[3 4 5 6]