2012-08-13 10 views
6

Estoy trabajando con matrices multidimensionales Numpy. He notado un comportamiento incoherente al acceder a estas matrices con otras matrices de índice. Por ejemplo:Numeración de matrices multidimensionales indexando swaps eje orden

import numpy as np 
start = np.zeros((7,5,3)) 
a  = start[:,:,np.arange(2)] 
b  = start[0,:,np.arange(2)] 
c  = start[0,:,:2] 
print 'a:', a.shape 
print 'b:', b.shape 
print 'c:', c.shape 

En este ejemplo, me sale el resultado:

a: (7, 5, 2) 
b: (2, 5) 
c: (5, 2) 

Esto me confunde. ¿Por qué "b" y "c" no tienen las mismas dimensiones? ¿Por qué "b" intercambia el orden del eje, pero no "a"?

He podido diseñar mi código alrededor de estas incoherencias gracias a muchas pruebas unitarias, pero sería de agradecer comprender lo que está pasando.

Como referencia, estoy usando Python 2.7.3 y Numpy 1.6.2 a través de MacPorts.

Respuesta

8

Sintácticamente, esto parece una incoherencia, pero semánticamente, usted está haciendo dos cosas muy diferentes aquí. En su definición de a y b, está haciendo advanced indexing, a veces llamado fancy indexing, que devuelve una copia de los datos. En su definición de c, está haciendo basic slicing, que devuelve una vista de los datos.

Para ver la diferencia, ayuda a comprender cómo se pasan los índices a los objetos de Python. He aquí algunos ejemplos:

>>> class ShowIndex(object): 
...  def __getitem__(self, index): 
...   print index 
... 
>>> ShowIndex()[:,:] 
(slice(None, None, None), slice(None, None, None)) 
>>> ShowIndex()[...,:] 
(Ellipsis, slice(None, None, None)) 
>>> ShowIndex()[0:5:2,::-1] 
(slice(0, 5, 2), slice(None, None, -1)) 
>>> ShowIndex()[0:5:2,np.arange(3)] 
(slice(0, 5, 2), array([0, 1, 2])) 
>>> ShowIndex()[0:5:2] 
slice(0, 5, 2) 
>>> ShowIndex()[5, 5] 
(5, 5) 
>>> ShowIndex()[5] 
5 
>>> ShowIndex()[np.arange(3)] 
[0 1 2] 

Como se puede ver, hay muchas diferentes configuraciones posibles. Primero, se pueden pasar elementos individuales o se pueden pasar tuplas de elementos. En segundo lugar, las tuplas pueden contener objetos slice, objetos Ellipsis, enteros simples o matrices numpy.

rebanar básico se activa cuando se pasa sólo objetos como int, slice o Ellipsis objetos, o None (que es la misma que numpy.newaxis). Estos se pueden pasar solos o en una tupla. Esto es lo que los doctores tienen que decir acerca de cómo rebanar básica se activa:

rebanar básico se produce cuando obj es un objeto de división (construido por inicio: STOP: paso de la notación dentro de corchetes), un entero, o una tupla de cortar objetos y enteros. Los puntos suspensivos y los objetos de eje nuevo se pueden intercalar con estos también. Para seguir siendo compatible con un uso común en Numérico, el corte básico también se inicia si el objeto de selección es cualquier secuencia (como una lista) que contenga objetos de corte, el objeto Ellipsis o el objeto newaxis, pero no arrays de enteros u otros secuencias incrustadas.

indexación avanzada se activa cuando se pasa una matriz numpy, una secuencia no-tupla que contiene sólo números enteros o que contienen subsecuencias de cualquier tipo, o una tupla que contiene una matriz o subsecuencia.

Para obtener más información acerca de cómo difieren la indexación avanzada y el corte básico, consulte los documentos (vinculados a más arriba). Pero en este caso particular, está claro para mí lo que está sucediendo.Tiene que ver con el comportamiento siguiente cuando se utiliza la indexación parcial:

La regla para la indexación parcial es que la forma del resultado (o la forma interpretado del objeto para ser utilizado en la configuración) es la forma de x con el subespacio indexado reemplazado con el subespacio de indexación emitido. Si los subespacios de índice están uno al lado del otro, el espacio de indexación transmitido reemplaza directamente a todos los subespacios indexados en x. Si los subespacios de indexación están separados (por objetos de división), entonces el espacio de indexación emitido es el primero, seguido del subespacio cortado de x.

En su definición de a, que utiliza la indexación avanzada, se pasa efectivamente la secuencia [0, 1] en que el tercer elemento de la tupla, y puesto que no hay difusión sucede (porque no hay otra secuencia), todo sucede como se esperaba .

En su definición de b, utilizando también la indexación avanzada, se pasa efectivamente dos secuencias, [0], el primer tema (que se convierte en una matriz intp), y [0, 1], el tercer punto. Estos dos elementos se transmiten juntos, y el resultado tiene la misma forma que el tercer elemento. Sin embargo, dado que la transmisión ha sucedido, nos enfrentamos a un problema: ¿dónde en la nueva tupla de forma insertamos la forma transmitida? Como dicen los documentos,

no hay un lugar inequívoco para colocar en el subespacio de indexación, por lo tanto, está añadido al principio.

Así que la 2 que resulta de radiodifusión se mueve hasta el comienzo de la tupla forma, la producción de una transposición aparente.

+0

Gracias por la explicación detallada. Eso fue bastante útil. El extraño comportamiento resultante de la indexación slicing + broadcast todavía es lo suficientemente inesperado como para dificultar la codificación. Por ejemplo: start [0,:, np.arange (2)] = np.ones ((5,2)) parece ser legal, pero debido al reordenamiento del eje no es – gbarter

+0

@gbarter Para mantener el forma original tienes que usar rebanadas. Es decir, esto funcionará: 'start [: 1,:, np.arange (2)] = np.ones ((5,2))' – jorgeca

Cuestiones relacionadas