Así es como numpy usa la indexación avanzada para transmitir formas de matriz. Cuando pasa un 0para el primer índice, y ypara el último índice, numpy transmitirá el 0para que tenga la misma forma que y. El siguiente equivalencia sostiene: x[0,:,:,y] == x[(0, 0, 0),:,:,y]. Aquí hay un ejemplo
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
Ahora, debido a que está pasando efectivamente dos conjuntos de índices, está utilizando la API de indexación avanzada para formar (en este caso) pares de índices.
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)
]
Que tiene una primera dimensión igual a la longitud de y. Esto es lo que estás viendo.
Como ejemplo, observe una matriz con 4 dimensiones que se describen en el siguiente fragmento:
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 tiene una forma secuencial realmente fácil de entender que ahora podemos usar para mostrar lo que está sucediendo ...
La primera dimensión es como tener 2 libros de Excel, la segunda dimensión es como tener 3 hojas en cada libro, la tercera dimensión es como tener 4 filas por hoja, y la última dimensión es 5 valores para cada fila (o columnas por hoja).
Mirándolo de esta manera, preguntando x[0,:,:,0], está el dicho: "en el primer libro de trabajo, para cada hoja, para cada fila, dame el primer valor / columna".
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]
Pero ahora con la indexación avanzada, podemos pensar x[(0,0,0),:,:,y]como "en el primer libro de trabajo, para cada hoja, para cada fila, dame el yvalor / columna th. Ok, ahora hazlo para cada valor de 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]]])
Donde se vuelve loco es que numpy se transmitirá para que coincida con las dimensiones externas de la matriz de índice. Entonces, si desea hacer la misma operación que anteriormente, pero para AMBOS "libros de Excel", no tiene que hacer un bucle y concatenar. Puede pasar una matriz a la primera dimensión, pero DEBE tener una forma compatible.
Pasar un número entero se transmite a y.shape == (3,). Si desea pasar una matriz como primer índice, solo la última dimensión de la matriz debe ser compatible y.shape. Es decir, la última dimensión del primer índice debe ser 3 o 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)
Encontré una breve explicación en los documentos: https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#combining-advanced-and-basic-indexing
Editar:
De la pregunta original, para obtener una línea de su subslicing deseado, puede usar x[0][:,:,y]:
x[0][:,:,y].shape
# returns
(2, 50, 3)
Sin embargo, si está intentando asignar a esos subslices, debe tener mucho cuidado de ver una vista de memoria compartida de la matriz original. De lo contrario, la asignación no será a la matriz original, sino a una copia.
La memoria compartida solo ocurre cuando usa un número entero o un segmento para subconjuntar su matriz, es decir, x[:,0:3,:,:]o x[0,:,:,1:-1].
np.shares_memory(x, x[0])
# returns:
True
np.shares_memory(x, x[:,:,:,y])
# returns:
False
Tanto en su pregunta original como en mi ejemplo yno se trata de un int o un sector, por lo que siempre se terminará asignando a una copia del original.
¡PERO! Debido a que su matriz ypuede expresarse como un segmento, usted PUEDE realmente obtener una vista asignable de su matriz a través de:
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
Aquí usamos el segmento 0:21:10para tomar cada índice que estaría en range(0,21,10). Tenemos que usar 21y no 20porque el punto de parada está excluido del segmento, al igual que en la rangefunción.
Básicamente, si puede construir un segmento que se ajuste a sus criterios de sublición, puede hacer la asignación.