2012-02-27 13 views
16

me encontré con el siguiente rareza en numpy que puede o no puede ser un error:Array de tipo nombrado: ¿Diferencia entre un [0] ['nombre'] y un '' nombre '] [0]?

import numpy as np 
dt = np.dtype([('tuple', (int, 2))]) 
a = np.zeros(3, dt) 
type(a['tuple'][0]) # ndarray 
type(a[0]['tuple']) # ndarray 

a['tuple'][0] = (1,2) # ok 
a[0]['tuple'] = (1,2) # ValueError: shape-mismatch on array construction 

lo que habría esperado que tanto de las siguientes opciones de trabajo. Opiniones?

+1

Consejo: al publicar código en SO, publique los bits que podemos cortar y pegar; en el caso de Python, eso significa usar '#' para los comentarios insertados, no '%'. – DSM

+1

Cosas divertidas, también veo que esto da el mismo resultado de cualquier manera usando 1.6.1 ... –

+0

Un poco extraño, pero 'a [0] ['tuple'] [:] = (1,2)' funciona, tal vez hay una pista allí ... –

Respuesta

1

Esto fue un error de código fuente, corregido desde NumPy PR #5947, con una corrección en 1.9.3.

8

me sale un error diferente de lo que hacen (usando 1.7.0.dev numpy):

ValueError: setting an array element with a sequence. 

por lo que la explicación a continuación pueden no estar correcto para su sistema (o incluso podría ser el mal explicación de lo que veo).

En primer lugar, observe que la indexación de una fila de un structured array le da un objeto numpy.void (ver data type docs)

import numpy as np 
dt = np.dtype([('tuple', (int, 2))]) 
a = np.zeros(3, dt) 
print type(a[0]) # = numpy.void 

Por lo que entiendo, void es algo así como una lista de Python, ya que puede contener objetos de diferentes tipos de datos, lo cual tiene sentido ya que las columnas en una matriz estructurada pueden ser tipos de datos diferentes.

Si, en lugar de indexación, que se mire a cabo la primera fila, se obtiene una ndarray:

print type(a[:1]) # = numpy.ndarray 

Esto es análogo a la forma en las listas de Python funcionan:

b = [1, 2, 3] 
print b[0] # 1 
print b[:1] # [1] 

rebanar devuelve una versión abreviada de la secuencia original, pero la indexación devuelve un elemento (aquí, un int; arriba, un tipo void).

Por lo tanto, cuando corta en las filas de la matriz estructurada, debe esperar que se comporte como su matriz original (solo con menos filas). Continuando con su ejemplo, ahora puede asignar a las columnas de tupla '' de la primera fila:

a[:1]['tuple'] = (1, 2) 

Así, ... ¿Por qué no a[0]['tuple'] = (1, 2) trabajo?

Bien, recuerde que a[0] devuelve un objeto void. Por lo tanto, cuando se llama a

a[0]['tuple'] = (1, 2) # this line fails 

que está asignando un tuple al elemento 'tupla' de ese void objeto. Nota: a pesar del hecho que ha llamado a este índice 'tupla', se almacena como un ndarray:

print type(a[0]['tuple']) # = numpy.ndarray 

lo tanto, esto significa que la tupla debe ser echado en una ndarray. Pero, el objeto void no puede enviar asignaciones (esto es sólo una suposición) porque puede contener tipos de datos arbitrarios, por lo que no sabe de qué tipo se va a convertir.Para evitar esto se puede echar la entrada a sí mismo:

a[0]['tuple'] = np.array((1, 2)) 

El hecho de que tenemos diferentes errores sugiere que la línea anterior podría no funcionar para ti desde la fundición direcciones el error que he recibido --- no la que usted recibió .

Adición:

Así que ¿por qué el siguiente trabajo?

a[0]['tuple'][:] = (1, 2) 

aquí, estás indexar en la matriz cuando se agrega [:], pero sin eso, usted es la indexación en el objeto void. En otras palabras, a[0]['tuple'][:] dice "reemplazar los elementos de la matriz almacenada" (que maneja la matriz), a[0]['tuple'] dice "reemplazar la matriz almacenada" (que es manejada por void).

Epílogo:

Curiosamente, el acceso a la fila (es decir, con la indexación 0) parece caer la matriz de base, pero todavía le permite asignar a la matriz de base.

print a['tuple'].base is a # = True 
print a[0].base is a # = False 
a[0] = ((1, 2),) # `a` is changed 

Tal void no es realmente una matriz por lo que no cuenten con un conjunto de base, pero entonces ... ¿por qué tener un atributo base?

+0

la recompensa para usted – bmu

9

Lo pregunté en la lista de discusión numpy. Travis Oliphant respondió here.

Citando su respuesta:

The short answer is that this is not really a "normal" bug, but it could be considered a "design" bug (although the issues may not be straightforward to resolve). What that means is that it may not be changed in the short term --- and you should just use the first spelling.

Structured arrays can be a confusing area of NumPy for several of reasons. You've constructed an example that touches on several of them. You have a data-type that is a "structure" array with one member ("tuple"). That member contains a 2-vector of integers.

First of all, it is important to remember that with Python, doing

a['tuple'][0] = (1,2)

is equivalent to

b = a['tuple']; b[0] = (1,2)

In like manner,

a[0]['tuple'] = (1,2)

is equivalent to

b = a[0]; b['tuple'] = (1,2)

To understand the behavior, we need to dissect both code paths and what happens. You built a (3,) array of those elements in 'a'. When you write b = a['tuple'] you should probably be getting a (3,) array of (2,)-integers, but as there is currently no formal dtype support for (n,)-integers as a general dtype in NumPy, you get back a (3,2) array of integers which is the closest thing that NumPy can give you. Setting the [0] row of this object via

a['tuple'][0] = (1,2)

works just fine and does what you would expect.

On the other hand, when you type:

b = a[0]

you are getting back an array-scalar which is a particularly interesting kind of array scalar that can hold records. This new object is formally of type numpy.void and it holds a "scalar representation" of anything that fits under the "VOID" basic dtype.

For some reason:

b['tuple'] = [1,2]

is not working. On my system I'm getting a different error: TypeError: object of type 'int' has no len()

I think this should be filed as a bug on the issue tracker which is for the time being here: http://projects.scipy.org/numpy

The problem is ultimately the void->copyswap function being called in voidtype_setfields if someone wants to investigate. I think this behavior should work.

Una explicación para esto se da en a numpy bug report.

Cuestiones relacionadas