python - numpy
Array dtype nombrado: ¿Diferencia entre un[0][''nombre''] y un[''nombre''][0]? (3)
Me encontré con la siguiente rareza en numpy que puede o no ser un error:
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
Hubiera esperado que las siguientes opciones funcionen. Opiniones?
Este fue un error en sentido ascendente, corregido a partir de NumPy PR # 5947 , con una solución en 1.9.3.
Lo pedí en la lista de discusión de números. Travis Oliphant respondió here .
Citando su respuesta:
La respuesta corta es que esto no es realmente un error "normal", pero podría considerarse un error de "diseño" (aunque los problemas pueden no ser fáciles de resolver). Lo que eso significa es que no se puede cambiar a corto plazo, y solo debe usar la primera ortografía.
Los arreglos estructurados pueden ser un área confusa de NumPy por varias razones. Has construido un ejemplo que toca varios de ellos. Tiene un tipo de datos que es una matriz de "estructura" con un miembro ("tupla"). Ese miembro contiene un 2-vector de enteros.
En primer lugar, es importante recordar que con Python, hacer
a [''tupla''] [0] = (1,2)
es equivalente a
b = a [''tupla'']; b [0] = (1,2)
De la misma manera,
a [0] [''tupla''] = (1,2)
es equivalente a
b = a [0]; b [''tupla''] = (1,2)
Para comprender el comportamiento, necesitamos analizar tanto las rutas de código como lo que sucede. Construiste una (3,) matriz de esos elementos en ''a''. Cuando escribe b = a [''tupla''] probablemente debería obtener una matriz (3,) de (2,) - enteros, pero como actualmente no hay soporte formal de tipo de dtype para (n,) - enteros como un tipo de dty general en NumPy, recuperas una matriz de números enteros (3,2) que es lo más cercano que NumPy puede darte. Configurando la fila [0] de este objeto vía
a [''tupla''] [0] = (1,2)
Funciona bien y hace lo que cabría esperar.
Por otro lado, cuando escribes:
b = a [0]
está recuperando un array-escalar, que es un tipo particularmente interesante de array escalar que puede contener registros. Este nuevo objeto es formalmente de tipo numpy.void y contiene una "representación escalar" de cualquier cosa que se ajuste al tipo básico "VOID".
Por alguna razón:
b [''tupla''] = [1,2]
no está trabajando. En mi sistema recibo un error diferente: TypeError: el objeto de tipo ''int'' no tiene len ()
Creo que esto debería archivarse como un error en el rastreador de problemas que está por el momento aquí: http://projects.scipy.org/numpy
El problema es, en última instancia, la función void-> copyswap que se llama en voidtype_setfields si alguien quiere investigar. Creo que este comportamiento debería funcionar.
Una explicación para esto se da en un informe de error numpy .
Recibo un error diferente al que tú (usando numpy 1.7.0.dev):
ValueError: setting an array element with a sequence.
así que la explicación a continuación puede no ser correcta para su sistema (o incluso podría ser la explicación incorrecta de lo que veo).
Primero, observe que indexar una fila de una matriz estructurada le proporciona un objeto numpy.void
(ver documentos de tipo de datos )
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 que tiene sentido, ya que las columnas en una matriz estructurada pueden ser de diferentes tipos de datos.
Si, en lugar de indexar, recorta la primera fila, obtiene una ndarray
:
print type(a[:1]) # = numpy.ndarray
Esto es análogo a cómo funcionan las listas de Python:
b = [1, 2, 3]
print b[0] # 1
print b[:1] # [1]
La segmentación devuelve una versión abreviada de la secuencia original, pero la indexación devuelve un elemento (aquí, un int
; más arriba, un tipo void
).
Por lo tanto, cuando divide 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 ''tupla'' de la primera fila:
a[:1][''tuple''] = (1, 2)
Entonces, ... ¿por qué a[0][''tuple''] = (1, 2)
funciona?
Bueno, recuerde que a[0]
devuelve un objeto void
. Entonces, cuando llamas
a[0][''tuple''] = (1, 2) # this line fails
estás asignando una tuple
al elemento ''tupla'' de ese objeto void
. Nota: a pesar del hecho de que haya llamado "tupla" a este índice, se almacenó como una ndarray
:
print type(a[0][''tuple'']) # = numpy.ndarray
Entonces, esto significa que la tupla debe ser lanzada en un ndarray
. Pero , el objeto void
no puede emitir asignaciones (esto es solo una conjetura) porque puede contener tipos de datos arbitrarios, por lo que no sabe a qué tipo de conversión. Para evitar esto, puedes lanzar la entrada tú mismo:
a[0][''tuple''] = np.array((1, 2))
El hecho de que obtengamos errores diferentes sugiere que la línea anterior podría no funcionar para usted, ya que la resolución del error que recibí no es la que recibió.
Apéndice:
Entonces, ¿por qué funciona lo siguiente?
a[0][''tuple''][:] = (1, 2)
Aquí, está indexando en la matriz cuando agrega [:]
, pero sin eso, está indexando en el objeto void
. En otras palabras, a[0][''tuple''][:]
dice "reemplazar los elementos de la matriz almacenada" (que es manejada por la matriz), a[0][''tuple'']
dice "reemplazar la matriz almacenada "(que se maneja por void
).
Epílogo:
Por extraño que parezca, el acceso a la fila (es decir, la indexación con 0) parece descartar la matriz base, pero aún así le permite asignar a la matriz base.
print a[''tuple''].base is a # = True
print a[0].base is a # = False
a[0] = ((1, 2),) # `a` is changed
Tal vez void
no sea realmente una matriz, por lo que no tiene una matriz base, ... pero entonces, ¿por qué tiene un atributo base
?