La indexación con listas y matrices en números parece inconsistente

Inspirado en esta otra pregunta , estoy tratando de envolver mi mente en torno a la indexación avanzada en NumPy y desarrollar una comprensión más intuitiva de cómo funciona.

He encontrado un caso interesante. Aquí hay una matriz:

>>> y = np.arange(10) >>> y array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 

si lo indexo como escalar, obtengo un escalar, por supuesto:

 >>> y[4] 4 

con una matriz 1D de enteros, obtengo otra matriz 1D:

 >>> idx = [4, 3, 2, 1] >>> y[idx] array([4, 3, 2, 1]) 

así que si lo indexo con una matriz 2D de enteros, obtengo … ¿qué obtengo?

 >>> idx = [[4, 3], [2, 1]] >>> y[idx] Traceback (most recent call last): File "", line 1, in  IndexError: too many indices for array 

¡Oh no! La simetría está rota. ¡Tengo que indexar con una matriz 3D para obtener una matriz 2D!

 >>> idx = [[[4, 3], [2, 1]]] >>> y[idx] array([[4, 3], [2, 1]]) 

¿Qué hace que Number se comporte de esta manera?

Para hacer esto más interesante, me di cuenta de que la indexación con matrices numpy (en lugar de listas) se comporta de manera intuitiva, y 2D me da 2D:

 >>> idx = np.array([[4, 3], [2, 1]]) >>> y[idx] array([[4, 3], [2, 1]]) 

Esto parece inconsistente de donde estoy. ¿Cuál es la regla aquí?

El motivo es la interpretación de listas como índice para matrices numpy: NumPy interpreta las listas como tuplas y la indexación con una tupla se interpreta como indexación multidimensional.

Al igual que arr[1, 2] devuelve el elemento arr[1][2] el arr[[[4, 3], [2, 1]]] es idéntico a arr[[4, 3], [2, 1]] y, de acuerdo con las reglas de indexación multidimensional, los elementos arr[4, 2] y arr[3, 1] .

Al agregar una lista más, le dice a NumPy que desea dividir a lo largo de la primera dimensión, porque la lista más externa se interpreta efectivamente como si solo pasara en una “lista de índices para la primera dimensión”: arr[[[[4, 3], [2, 1]]]] .

De la documentación :

Ejemplo

De cada fila, se debe seleccionar un elemento específico. El índice de la fila es solo [0, 1, 2] y el índice de la columna especifica el elemento a elegir para la fila correspondiente, aquí [0, 1, 0]. Usando ambos juntos, la tarea se puede resolver usando indexación avanzada:

 >>> x = np.array([[1, 2], [3, 4], [5, 6]]) >>> x[[0, 1, 2], [0, 1, 0]] array([1, 4, 5]) 

y :

Advertencia

La definición de indexación avanzada significa que x[(1,2,3),] es fundamentalmente diferente de x[(1,2,3)] . El último es equivalente a x[1,2,3] que activará la selección básica, mientras que el primero activará la indexación avanzada. Asegúrese de entender por qué ocurre esto.

En tales casos, probablemente sea mejor usar np.take :

 >>> y.take([[4, 3], [2, 1]]) # 2D array array([[4, 3], [2, 1]]) 

Esta función [ np.take ] hace lo mismo que la indexación “elegante” (indexando matrices usando matrices); sin embargo, puede ser más fácil de usar si necesita elementos a lo largo de un eje determinado.

O convertir los índices a una matriz. De esa manera, NumPy lo interpreta (¡la array es de carcasa especial!) Como una indexación elegante en lugar de como una “indexación multidimensional”:

 >>> y[np.asarray([[4, 3], [2, 1]])] array([[4, 3], [2, 1]])