6.6. Chỉ mục và slicing

Một ndarray được truy cập theo bốn cách: chỉ mục đơn, slice, mặt nạ boolean, và các dạng gán của mỗi loại.

6.6.1. Phần tử đơn

Chỉ mục bằng dấu ngoặc vuông trả về giá trị tại vị trí đã cho:

a = np.arange(10, dtype=np.uint8)
print(a[0], a[-1])      # 0 9
print(a[1], a[-2])      # 1 8

Chỉ mục âm đếm từ cuối, giống như với list Python. Chỉ mục ngoài phạm vi sẽ phát sinh IndexError.

Với mảng bậc cao hơn, mỗi trục nhận một chỉ mục. Các chỉ mục nằm trong một cặp dấu ngoặc, cách nhau bằng dấu phẩy:

m = np.arange(9, dtype=np.uint8).reshape((3, 3))
print(m[1, 1])          # 4
print(m[2, 0])          # 6

Khi cung cấp ít chỉ mục hơn số trục, các trục chưa được chỉ mục sẽ được giữ nguyên. Kết quả là một view bậc giảm của nguồn:

print(m[0])             # the first row, as a 1-D view of m

6.6.2. Slices

Một slice start:stop:step trả về một view của mảng. View chia sẻ bộ đệm dữ liệu nền với nguồn; ghi qua view sẽ ghi vào nguồn:

a = np.arange(10, dtype=np.uint8)
v = a[::2]              # array([0, 2, 4, 6, 8], dtype=uint8)
v[0] = 99
print(a)
# array([99, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8)

Khi cần một bộ đệm độc lập, copy() tạo ra một cái một cách tường minh.

Slicing mở rộng tự nhiên sang các chiều cao hơn. Mỗi trục nhận slice riêng của nó:

m = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]], dtype=np.uint8)

m[0]            # first row
m[0, :2]        # first two elements of row 0
m[:, 0]         # column 0 (still 2-D in ulab)
m[-1]           # last row
m[::2, ::2]     # every other row, every other column

Kết hợp số nguyên (chỉ mục đơn, bỏ trục) và slice (giữ trục) được phép và là cách thông thường để truy cập một hàng / một cột đơn.

6.6.3. Mặt nạ boolean

Một mảng boolean có cùng hình dạng với nguồn chọn các phần tử tại nơi mặt nạ là True. Chỉ mục boolean hiện chỉ hoạt động trên mảng 1-D; đầu vào bậc cao hơn sẽ phát sinh NotImplementedError

a = np.arange(9, dtype=np.float)
mask = a < 5
print(a[mask])

Đầu ra:

array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)

Mặt nạ là một bool ndarray thông thường, vì vậy mọi biểu thức tạo ra một mặt nạ đều có thể dùng được:

b = np.array([4, 4, 4, 3, 3, 3, 13, 13, 13], dtype=np.uint8)
a = np.arange(9, dtype=np.uint8)
print(a[a * a > np.sin(b) * 100.0])

Chỉ mục boolean trả về một bản sao. Các phần tử được chọn nằm ở bất kỳ vị trí nào mà mặt nạ là True -- không theo bước đều đặn qua nguồn -- vì vậy không có bộ mô tả nào một view có thể dùng để định địa chỉ chúng, và kết quả được vật chất hóa vào bộ đệm riêng của nó.

6.6.4. Chỉ mục mảng số nguyên

Truyền một danh sách hoặc mảng chỉ mục trong dấu ngoặc sẽ lấy các phần tử đó ra trong một bước:

a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
a[[0, 2, 4]]
# array([10, 30, 50], dtype=uint8)

Kết quả là một bản sao; các phần tử được chọn không còn chia sẻ bộ nhớ với nguồn nữa. Dạng tương tự hoạt động ở phía trái của phép gán:

a[[0, 2, 4]] = 0
# array([0, 20, 0, 40, 0], dtype=uint8)

take() (đề cập tại Lựa chọn và sắp xếp lại) là dạng hàm của cùng thao tác và chấp nhận từ khóa out= để sử dụng không cấp phát trong vòng lặp truyền phát.

6.6.5. Gán slice

Slice và mặt nạ xuất hiện ở phía trái của phép gán cũng như phía phải. Phía bên phải có thể là một scalar, một mảng khác, hoặc một view:

m = np.zeros((3, 3), dtype=np.uint8)
m[0]      = 1            # whole row 0 set to 1
m[:, 2]   = 3            # whole column 2 set to 3
m[1, 1:3] = [7, 8]       # row 1, columns 1 and 2

Mặt nạ boolean ở phía trái thay thế các phần tử thỏa mãn điều kiện:

a = np.arange(9, dtype=np.uint8)
a[a < 3] = 99
# array([99, 99, 99, 3, 4, 5, 6, 7, 8], dtype=uint8)

a = np.arange(9, dtype=np.uint8)
b = np.array(range(9)) + 12
a[b < 15] = b[b < 15]
# array([12, 13, 14, 3, 4, 5, 6, 7, 8], dtype=uint8)

6.6.6. Tại sao gán slice quan trọng trên camera

Gán slice ghi qua một mảng đã tồn tại. Không có mảng mới nào được cấp phát. Đó là sự khác biệt giữa:

out = a + b              # makes a new array the size of a
out = out * 2            # makes another new array

và:

out[:] = a               # writes into the existing out
out   += b               # in place
out   *= 2               # in place

Phiên bản đầu tiên yêu cầu camera cấp RAM cho hai mảng mới; phiên bản thứ hai không yêu cầu gì. Trên vi điều khiển với RAM hạn chế, sự khác biệt đó thường là ranh giới giữa một tập lệnh chạy thoải mái và một tập lệnh hết bộ nhớ.

Hiệu năng đề cập mẫu này chi tiết hơn. Quy tắc quan trọng hiện tại là gán slice, các toán tử số học tại chỗ (+=, *=, ...), và từ khóa out= trên các hàm đa dụng là ba công cụ giúp cập nhật không cấp phát trở nên khả thi.