Lập chỉ mục kỳ lạ bằng cách sử dụng numpy


27

Tôi có một biến, x, đó là hình dạng (2,2,50,100).

Tôi cũng có một mảng, y, bằng np.array ([0,10,20]). Một điều kỳ lạ xảy ra khi tôi lập chỉ mục x [0,:,:, y].

x = np.full((2,2,50,100),np.nan)
y = np.array([0,10,20])
print(x.shape)
(2,2,50,100)
print(x[:,:,:,y].shape)
(2,2,50,3)
print(x[0,:,:,:].shape)
(2,50,100)
print(x[0,:,:,y].shape)
(3,2,50)

Tại sao một đầu ra cuối cùng (3,2,50) mà không (2,50,3)?


Tôi chưa quen với numpy, vì vậy tôi không có câu trả lời cho câu hỏi của bạn. Để nghiên cứu thêm, tôi khuyên bạn nên tìm một ví dụ nhỏ hơn chỉ có 2D hoặc 3D và giống như nhiều nhất là 10 phần tử trên bất kỳ trục nào.
Code-Apprentice

Câu trả lời:


21

Đây là cách numpy sử dụng lập chỉ mục nâng cao để phát hình dạng mảng. Khi bạn vượt qua a 0cho chỉ mục đầu tiên và ycho chỉ mục cuối cùng, numpy sẽ phát sóng 0thành hình dạng giống như y. Tương đương sau giữ : x[0,:,:,y] == x[(0, 0, 0),:,:,y]. đây là một ví dụ

import numpy as np

x = np.arange(120).reshape(2,3,4,5)
y = np.array([0,2,4])

np.equal(x[0,:,:,y], x[(0, 0, 0),:,:,y]).all()
# returns:
True

Bây giờ, vì bạn thực sự chuyển qua hai bộ chỉ mục, nên bạn đang sử dụng API lập chỉ mục nâng cao để tạo thành các cặp chỉ mục (trong trường hợp này).

x[(0, 0, 0),:,:,y])

# equivalent to
[
  x[0,:,:,y[0]], 
  x[0,:,:,y[1]], 
  x[0,:,:,y[2]]
]

# equivalent to
rows = np.array([0, 0, 0])
cols = y
x[rows,:,:,cols]

# equivalent to
[
  x[r,:,:,c] for r, c in zip(rows, columns)
]

Mà có một chiều đầu tiên giống như chiều dài của y. Đây là những gì bạn đang thấy.

Ví dụ, nhìn vào một mảng có 4 kích thước được mô tả trong đoạn tiếp theo:

x = np.arange(120).reshape(2,3,4,5)
y = np.array([0,2,4])

# x looks like:
array([[[[  0,   1,   2,   3,   4],    -+      =+
         [  5,   6,   7,   8,   9],     Sheet1  |
         [ 10,  11,  12,  13,  14],     |       |
         [ 15,  16,  17,  18,  19]],   -+       |
                                                Workbook1
        [[ 20,  21,  22,  23,  24],    -+       |
         [ 25,  26,  27,  28,  29],     Sheet2  |
         [ 30,  31,  32,  33,  34],     |       |
         [ 35,  36,  37,  38,  39]],   -+       |
                                                |
        [[ 40,  41,  42,  43,  44],    -+       |
         [ 45,  46,  47,  48,  49],     Sheet3  |
         [ 50,  51,  52,  53,  54],     |       |
         [ 55,  56,  57,  58,  59]]],  -+      =+


       [[[ 60,  61,  62,  63,  64],
         [ 65,  66,  67,  68,  69],
         [ 70,  71,  72,  73,  74],
         [ 75,  76,  77,  78,  79]],

        [[ 80,  81,  82,  83,  84],
         [ 85,  86,  87,  88,  89],
         [ 90,  91,  92,  93,  94],
         [ 95,  96,  97,  98,  99]],

        [[100, 101, 102, 103, 104],
         [105, 106, 107, 108, 109],
         [110, 111, 112, 113, 114],
         [115, 116, 117, 118, 119]]]])

x có một dạng tuần tự thực sự dễ hiểu mà bây giờ chúng ta có thể sử dụng để hiển thị những gì đang xảy ra ...

Kích thước thứ nhất giống như có 2 Sổ làm việc Excel, kích thước thứ hai giống như có 3 trang tính trong mỗi sổ làm việc, kích thước thứ ba giống như có 4 hàng trên mỗi trang tính và kích thước cuối cùng là 5 giá trị cho mỗi hàng (hoặc cột trên mỗi trang tính).

Nhìn vào nó theo cách này, yêu cầu x[0,:,:,0], là câu nói: "trong sổ làm việc đầu tiên, cho mỗi tờ, cho mỗi hàng, hãy cho tôi giá trị / cột đầu tiên."

x[0,:,:,y[0]]
# returns:
array([[ 0,  5, 10, 15],
       [20, 25, 30, 35],
       [40, 45, 50, 55]])

# this is in the same as the first element in:
x[(0,0,0),:,:,y]

Nhưng bây giờ với lập chỉ mục nâng cao, chúng ta có thể nghĩ x[(0,0,0),:,:,y]là "trong sổ làm việc đầu tiên, cho mỗi trang tính, cho mỗi hàng, hãy đưa cho tôi ygiá trị / cột thứ. Ok, bây giờ hãy làm điều đó cho từng giá trị của y"

x[(0,0,0),:,:,y]
# returns:
array([[[ 0,  5, 10, 15],
        [20, 25, 30, 35],
        [40, 45, 50, 55]],

       [[ 2,  7, 12, 17],
        [22, 27, 32, 37],
        [42, 47, 52, 57]],

       [[ 4,  9, 14, 19],
        [24, 29, 34, 39],
        [44, 49, 54, 59]]])

Điều khiến nó trở nên điên rồ là numpy sẽ phát sóng để phù hợp với kích thước bên ngoài của mảng chỉ mục. Vì vậy, nếu bạn muốn thực hiện thao tác tương tự như trên, nhưng đối với CẢ "sổ làm việc Excel", bạn không phải lặp và nối. Bạn chỉ có thể truyền một mảng cho kích thước đầu tiên, nhưng nó PHẢI có hình dạng tương thích.

Truyền một số nguyên được phát đến y.shape == (3,). Nếu bạn muốn vượt qua một mảng làm chỉ mục đầu tiên, chỉ có thứ nguyên cuối cùng của mảng phải tương thích với y.shape. Tức là kích thước cuối cùng của chỉ mục đầu tiên phải là 3 hoặc 1.

ix = np.array([[0], [1]])
x[ix,:,:,y].shape
# each row of ix is broadcast to length 3:
(2, 3, 3, 4)

ix = np.array([[0,0,0], [1,1,1]])
x[ix,:,:,y].shape
# this is identical to above:
(2, 3, 3, 4)

ix = np.array([[0], [1], [0], [1], [0]])
x[ix,:,:,y].shape
# ix is broadcast so each row of ix has 3 columns, the length of y
(5, 3, 3, 4)

Tìm thấy một lời giải thích ngắn trong các tài liệu: https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#combining-advified-and-basic-indexing


Biên tập:

Từ câu hỏi ban đầu, để có được một lớp lót về mức độ mong muốn của bạn, bạn có thể sử dụng x[0][:,:,y]:

x[0][:,:,y].shape
# returns
(2, 50, 3)

Tuy nhiên, nếu bạn đang cố gắng gán cho các phần tử đó, bạn phải rất cẩn thận rằng bạn đang xem chế độ xem bộ nhớ được chia sẻ của mảng ban đầu. Nếu không, bài tập sẽ không thuộc về mảng ban đầu, mà là một bản sao.

Bộ nhớ dùng chung chỉ xảy ra khi bạn đang sử dụng một số nguyên hoặc lát để tập hợp mảng của bạn, tức là x[:,0:3,:,:]hoặc x[0,:,:,1:-1].

np.shares_memory(x, x[0])
# returns:
True

np.shares_memory(x, x[:,:,:,y])
# returns:
False

Trong cả câu hỏi ban đầu của bạn và ví dụ của tôi ykhông phải là int hay lát, vì vậy sẽ luôn luôn gán cho một bản sao của bản gốc.

NHƯNG! Bởi vì mảng của bạn cho ycó thể được diễn tả như một lát, bạn CÓ THỂ thực sự có được một cái nhìn chuyển nhượng của mảng của bạn thông qua:

x[0,:,:,0:21:10].shape
# returns:
(2, 50, 3)

np.shares_memory(x, x[0,:,:,0:21:10])
# returns:
True

# actually assigns to the original array
x[0,:,:,0:21:10] = 100

Ở đây chúng tôi sử dụng lát cắt 0:21:10để lấy mọi chỉ số sẽ có trong đó range(0,21,10). Chúng ta phải sử dụng 21và không phải 20vì điểm dừng được loại trừ khỏi lát cắt, giống như trong rangehàm.

Vì vậy, về cơ bản, nếu bạn có thể xây dựng một lát cắt phù hợp với tiêu chí của bạn, bạn có thể thực hiện chuyển nhượng.


4

Nó được gọi là combining advanced and basic indexing. Trong combining advanced and basic indexing, numpy thực hiện lập chỉ mục trong lập chỉ mục nâng cao trước và không gian con / nối kết quả theo thứ nguyên của lập chỉ mục cơ bản.

Ví dụ từ các tài liệu:

Đặt x.shape là (10,20,30,40,50) và giả sử ind_1 và ind_2 có thể được phát thành hình (2,3,4). Sau đó x [:, ind_1, ind_2] có hình dạng (10,2,3,4,40,50) vì không gian con có hình (20,30) từ X đã được thay thế bằng không gian con (2,3,4) từ các chỉ số. Tuy nhiên, x [:, ind_1,:, ind_2] có hình dạng (2,3,4,10,30,50) vì không có vị trí rõ ràng để thả vào không gian con lập chỉ mục, do đó, nó được xử lý ngay từ đầu . Luôn luôn có thể sử dụng .transpose () để di chuyển không gian con bất cứ nơi nào bạn muốn. Lưu ý rằng ví dụ này không thể được sao chép bằng cách sử dụng Take.

như vậy, trên x[0,:,:,y], 0yđang tiến lập chỉ mục. Chúng được phát sóng cùng nhau để mang lại kích thước (3,).

In [239]: np.broadcast(0,y).shape
Out[239]: (3,)

Điều này (3,)khắc phục sự khởi đầu của chiều thứ 2 và thứ 3 để thực hiện(3, 2, 50)

Để thấy rằng ngày 1 và kích thước cuối cùng được thực sự phát sóng với nhau, bạn có thể thử thay đổi 0để [0,1]cho thấy lỗi phát sóng

print(x[[0,1],:,:,y])

Output:
IndexError                                Traceback (most recent call last)
<ipython-input-232-5d10156346f5> in <module>
----> 1 x[[0,1],:,:,y]

IndexError: shape mismatch: indexing arrays could not be broadcast together with
 shapes (2,) (3,)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.