python3 - Subconjunto de una matriz numpy 2D
python3 numpy (4)
He buscado documentación y también otras preguntas aquí, pero parece que aún no me he acostumbrado a subconjuntos en matrices numpy.
Tengo una matriz numpy, y por el bien de la discusión, que se defina de la siguiente manera:
import numpy as np
a = np.arange(100)
a.shape = (10,10)
# array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
# [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
# [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
# [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
# [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
# [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]])
ahora quiero elegir las filas y columnas de a
especificado por los vectores n1
y n2
. Como ejemplo:
n1 = range(5)
n2 = range(5)
Pero cuando uso:
b = a[n1,n2]
# array([ 0, 11, 22, 33, 44])
Entonces solo se eligen los primeros cinco elementos diagonales, no el bloque completo de 5x5. La solución que he encontrado es hacerlo así:
b = a[n1,:]
b = b[:,n2]
# array([[ 0, 1, 2, 3, 4],
# [10, 11, 12, 13, 14],
# [20, 21, 22, 23, 24],
# [30, 31, 32, 33, 34],
# [40, 41, 42, 43, 44]])
Pero estoy seguro de que debería haber una forma de hacer esta simple tarea en un solo comando.
Obtuvo algunos buenos ejemplos de cómo hacer lo que quiere. Sin embargo, también es útil comprender lo que está sucediendo y por qué las cosas funcionan de la manera en que lo hacen. Hay algunas reglas simples que te ayudarán en el futuro.
Hay una gran diferencia entre la indexación "sofisticada" (es decir, el uso de una lista / secuencia) y la indexación "normal" (usando una porción). El motivo subyacente tiene que ver con si la matriz se puede "regularizar regularmente" o no y, por lo tanto, si se debe o no hacer una copia. Por lo tanto, las secuencias arbitrarias deben tratarse de manera diferente, si queremos poder crear "vistas" sin hacer copias.
En tu caso:
import numpy as np
a = np.arange(100).reshape(10,10)
n1, n2 = np.arange(5), np.arange(5)
# Not what you want
b = a[n1, n2] # array([ 0, 11, 22, 33, 44])
# What you want, but only for simple sequences
# Note that no copy of *a* is made!! This is a view.
b = a[:5, :5]
# What you want, but probably confusing at first. (Also, makes a copy.)
# np.meshgrid and np.ix_ are basically equivalent to this.
b = a[n1[:,None], n2[None,:]]
La indexación fantástica con secuencias 1D es básicamente equivalente a comprimirlas juntas e indexarlas con el resultado.
print "Fancy Indexing:"
print a[n1, n2]
print "Manual indexing:"
for i, j in zip(n1, n2):
print a[i, j]
Sin embargo, si las secuencias con las que indexa coinciden con la dimensionalidad de la matriz que está indexando (2D, en este caso), la indexación se trata de manera diferente. En lugar de "unir los dos juntos", Numpy usa los índices como una máscara.
En otras palabras, a[[[1, 2, 3]], [[1],[2],[3]]]
se trata de manera completamente diferente que a[[1, 2, 3], [1, 2, 3]]
, porque las secuencias / matrices que estás pasando son bidimensionales.
In [4]: a[[[1, 2, 3]], [[1],[2],[3]]]
Out[4]:
array([[11, 21, 31],
[12, 22, 32],
[13, 23, 33]])
In [5]: a[[1, 2, 3], [1, 2, 3]]
Out[5]: array([11, 22, 33])
Para ser un poco más preciso,
a[[[1, 2, 3]], [[1],[2],[3]]]
se trata exactamente como:
i = [[1, 1, 1],
[2, 2, 2],
[3, 3, 3]])
j = [[1, 2, 3],
[1, 2, 3],
[1, 2, 3]]
a[i, j]
En otras palabras, si la entrada es un vector de fila / columna es una forma abreviada de cómo los índices deberían repetirse en la indexación.
np.meshgrid
y np.ix_
son solo formas convenientes de convertir sus secuencias 1D en sus versiones 2D para indexar:
In [6]: np.ix_([1, 2, 3], [1, 2, 3])
Out[6]:
(array([[1],
[2],
[3]]), array([[1, 2, 3]]))
De manera similar (el argumento sparse
lo haría idéntico a ix_
arriba):
In [7]: np.meshgrid([1, 2, 3], [1, 2, 3], indexing=''ij'')
Out[7]:
[array([[1, 1, 1],
[2, 2, 2],
[3, 3, 3]]),
array([[1, 2, 3],
[1, 2, 3],
[1, 2, 3]])]
Otra forma rápida de crear el índice deseado es usar la función np.ix_
:
>>> a[np.ix_(n1, n2)]
array([[ 0, 1, 2, 3, 4],
[10, 11, 12, 13, 14],
[20, 21, 22, 23, 24],
[30, 31, 32, 33, 34],
[40, 41, 42, 43, 44]])
Esto proporciona una forma conveniente de construir una malla abierta a partir de secuencias de índices.
Parece que un caso de uso para su pregunta particular se ocuparía de la manipulación de la imagen. En la medida en que esté usando su ejemplo para editar matrices numpy que surjan de imágenes, puede usar la Biblioteca de imágenes de Python (PIL).
# Import Pillow:
from PIL import Image
# Load the original image:
img = Image.open("flowers.jpg")
# Crop the image
img2 = img.crop((0, 0, 5, 5))
El objeto img2 es una matriz numpy de la imagen recortada resultante.
Puede leer más sobre la manipulación de imágenes aquí con el paquete Pillow (un tenedor fácil de usar en el paquete PIL):
Puede usar np.meshgrid
para dar a las matrices n1
, n2
la forma adecuada para realizar la indexación deseada:
In [104]: a[np.meshgrid(n1,n2, sparse=True, indexing=''ij'')]
Out[104]:
array([[ 0, 1, 2, 3, 4],
[10, 11, 12, 13, 14],
[20, 21, 22, 23, 24],
[30, 31, 32, 33, 34],
[40, 41, 42, 43, 44]])
O bien, sin meshgrid:
In [117]: a[np.array(n1)[:,np.newaxis], np.array(n2)[np.newaxis,:]]
Out[117]:
array([[ 0, 1, 2, 3, 4],
[10, 11, 12, 13, 14],
[20, 21, 22, 23, 24],
[30, 31, 32, 33, 34],
[40, 41, 42, 43, 44]])
Hay un ejemplo similar con una explicación de cómo funciona esta indexación de matriz entera en los documentos.
Véase también la receta del libro de cocina. Selección de filas y columnas .