这是numpy如何使用高级索引来广播数组形状的方法。当您0
为第一个索引和y
最后一个索引传递a时,numpy将广播0
与相同的形状y
。下列等价成立:x[0,:,:,y] == x[(0, 0, 0),:,:,y]
。这是一个例子
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
现在,由于您有效地传递了两组索引,因此您正在使用高级索引API来形成(在这种情况下)成对的索引。
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)
]
其第一维度与的长度相同y
。这就是您所看到的。
例如,看一下具有4个维的数组,这些数组在下一个块中进行了描述:
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
有一个非常容易理解的顺序形式,我们现在可以使用它来显示正在发生的事情...
第一个维度就像有2个Excel工作簿,第二个维度就像每个工作簿中有3个工作表,第三个维度就像每张工作表有4行,最后一个维度是每行5个值(或每张工作表的列)。
这样看待,就是问x[0,:,:,0]
:“在第一个工作簿中,对于每一页,每一行,给我第一个值/列。”
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]
但是,现在有了高级索引,我们可以将其x[(0,0,0),:,:,y]
视为“在第一个工作簿中,对于每张工作表,每一行,给我y
第一个值/列。好吧,现在针对每个值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]]])
令人发疯的是,numpy将广播以匹配索引数组的外部尺寸。因此,如果您要执行与上述相同的操作,但对于“ Excel工作簿”都不需要循环和连接。您可以只将数组传递给第一维,但它必须具有兼容的形状。
传递整数将广播到y.shape == (3,)
。如果您想将数组作为第一个索引传递,则仅数组的最后一个维度必须与兼容y.shape
。即,第一个索引的最后一个维度必须为3或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)
在文档中找到了简短的解释:https : //docs.scipy.org/doc/numpy/reference/arrays.indexing.html#combining-advanced-and-basic-indexing
编辑:
从最初的问题开始,要获得所需的单线定价,可以使用x[0][:,:,y]
:
x[0][:,:,y].shape
# returns
(2, 50, 3)
但是,如果要分配给这些子切片,则必须非常小心,因为它正在查看原始阵列的共享内存视图。否则,分配将不是原始数组,而是副本。
仅当使用整数或切片对数组进行子集化(即x[:,0:3,:,:]
或)时,才会发生共享内存x[0,:,:,1:-1]
。
np.shares_memory(x, x[0])
# returns:
True
np.shares_memory(x, x[:,:,:,y])
# returns:
False
在您的原始问题和我的示例y
中,都不是int或slice,因此最终总是会分配给原始副本。
但!因为你的阵列y
可以表示为一个切片,你CAN实际上得到通过您的阵列的分配图:
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
在这里,我们使用切片0:21:10
来获取其中的每个索引range(0,21,10)
。我们必须使用,21
而不是20
因为像在range
函数中那样,从切片中排除了停止点。
因此,基本上,如果您可以构造适合您的细分条件的切片,则可以进行分配。