Rebanada de índice voluminosa sin perder información de dimensión

98

Estoy usando numpy y quiero indexar una fila sin perder la información de la dimensión.

import numpy as np
X = np.zeros((100,10))
X.shape        # >> (100, 10)
xslice = X[10,:]
xslice.shape   # >> (10,)  

En este ejemplo, xslice es ahora 1 dimensión, pero quiero que sea (1,10). En R, usaría X [10,:, drop = F]. ¿Hay algo similar en numpy. No pude encontrarlo en la documentación y no vi una pregunta similar.

¡Gracias!

asuntos mentales
fuente

Respuestas:

59

Probablemente sea más fácil de hacer x[None, 10, :]o equivalente (pero más legible) x[np.newaxis, 10, :].

En cuanto a por qué no es el predeterminado, personalmente, encuentro que tener matrices constantemente con dimensiones singleton se vuelve molesto muy rápidamente. Supongo que los desarrolladores numpy sintieron lo mismo.

Además, numpy maneja muy bien las matrices de difusión, por lo que generalmente hay pocas razones para retener la dimensión de la matriz de la que proviene el segmento. Si lo hizo, entonces cosas como:

a = np.zeros((100,100,10))
b = np.zeros(100,10)
a[0,:,:] = b

o no funcionaría o sería mucho más difícil de implementar.

(O al menos esa es mi suposición sobre el razonamiento del desarrollador numpy detrás de la caída de información de dimensión al cortar)

Joe Kington
fuente
6
@Lisa: x[None, 10]harás lo que quieras.
naught101
Sip. Pon tu Nones al lado de los atenuadores que estás cortando.
Mad Physicist
1
En el ejemplo faltan corchetes adicionales para la tupla en la asignación a b; debería ser b = np.zeros((100,10)).
Jerzy
¿Cuál es la razón para usar 3 índices en total en lugar de solo dos? Quiero decir X[10,None](usando su código como ejemplo).
greenoldman
8
" normalmente hay pocas razones para retener la dimensión de la matriz " ... Bueno, ciertamente, arruinará total y completamente la multiplicación de matrices ( np.matmul()o@ ). Esto me quemó.
Jean-François Corbett
89

Otra solución es hacer

X[[10],:]

o

I = array([10])
X[I,:]

La dimensionalidad de una matriz se conserva cuando la indexación se realiza mediante una lista (o una matriz) de índices. Esto es bueno porque te deja la opción entre mantener la dimensión y apretar.

gnebehay
fuente
2
Esto copia los datos de la matriz
por
Este no es siempre el caso. Ver: x = np.array([[1,2,3,4]]) si luego lo corta, x[[0],[1,2]] obtiene el unidimensional. array([2, 3]) Mi opinión es que al seleccionar los vectores de columna o fila, es mejor hacer el corte simple y luego usarlo np.reshape, así que en mi ejemplo seríanp.reshape(x[0,[1,2]],[1,2])
Alexander
1
otros, tenga en cuenta el punto y coma al final: es importante, X[[10]]se interpretaría como X[10]y la forma será más pequeña; de manera similar, X[[10, 20]] == X[10, 20]y la forma es aún más pequeña
Ben Usman
1
Advertencia : ¡no mezcle esta forma de indexación con solo indexación de enteros! Si tuvieras aforma (10, 20, 30), entonces a[0, :, [0]]tendrá forma (1, 20), no (20, 1), porque en estos últimos se emiten índices a los a[[0], :, [0]]que muchas veces no es exactamente lo que esperas! Considerando que a[0, :, :1]le dará lo (20, 1)esperado. Además, consulte el comentario anterior para un caso de borde extraño con índice único. En general, parece que este método tiene demasiados casos extremos.
Ben Usman
30

Encontré algunas soluciones razonables.

1) uso numpy.take(X,[10],0)

2) usa esta extraña indexación X[10:11:, :]

Idealmente, este debería ser el predeterminado. Nunca entendí por qué se eliminan las dimensiones. Pero esa es una discusión para numpy ...

asuntos mentales
fuente
1
Las 'dimensiones' se eliminan al indexar listas de Python alist[0]y se mantienen al dividirlas.
hpaulj
4
La opción 2 (que se puede escribir como slice(n, n+1)para extraer índice n) debería ser la respuesta aceptada, ya que es la única que se extiende naturalmente al caso n-dimensional.
norok2
La opción 2 parece poder escribirse como X[10:11, :]en Python 3.7.5 (es decir, sin los dos puntos adicionales después del 11)
Joe
6

Aquí hay una alternativa que me gusta más. En lugar de indexar con un solo número, indexe con un rango. Es decir, use X[10:11,:]. (Tenga en cuenta que 10:11no incluye 11).

import numpy as np
X = np.zeros((100,10))
X.shape        # >> (100, 10)
xslice = X[10:11,:]
xslice.shape   # >> (1,10)

Esto también hace que sea fácil de entender con más dimensiones, sin Nonehacer malabares y sin saber qué eje usar qué índice. Además, no es necesario realizar una contabilidad adicional con respecto al tamaño de la matriz, solo i:i+1para cualquiera ique haya utilizado en la indexación regular.

b = np.ones((2, 3, 4))
b.shape # >> (2, 3, 4)
b[1:2,:,:].shape  # >> (1, 3, 4)
b[:, 2:3, :].shape .  # >> (2, 1, 4)
Andrew Schwartz
fuente
0

Esto es especialmente molesto si está indexando mediante una matriz que podría tener una longitud de 1 en tiempo de ejecución. Para ese caso, hay np.ix_:

some_array[np.ix_(row_index,column_index)]
Jthorpe
fuente