2010-09-01 19 views
22

Estoy tratando de convertir una matriz bidimensional en una matriz estructurada con campos con nombre. Quiero que cada fila en la matriz 2D sea un nuevo registro en la matriz estructurada. Desafortunadamente, nada de lo que he intentado funciona de la manera que esperaba.Convertir una matriz numpy 2D en una matriz estructurada

estoy empezando con:

>>> myarray = numpy.array([("Hello",2.5,3),("World",3.6,2)]) 
>>> print myarray 
[['Hello' '2.5' '3'] 
['World' '3.6' '2']] 

Quiero convertir a algo que se parece a esto:

>>> newarray = numpy.array([("Hello",2.5,3),("World",3.6,2)], dtype=[("Col1","S8"),("Col2","f8"),("Col3","i8")]) 
>>> print newarray 
[('Hello', 2.5, 3L) ('World', 3.6000000000000001, 2L)] 

Lo que he intentado:

>>> newarray = myarray.astype([("Col1","S8"),("Col2","f8"),("Col3","i8")]) 
>>> print newarray 
[[('Hello', 0.0, 0L) ('2.5', 0.0, 0L) ('3', 0.0, 0L)] 
[('World', 0.0, 0L) ('3.6', 0.0, 0L) ('2', 0.0, 0L)]] 

>>> newarray = numpy.array(myarray, dtype=[("Col1","S8"),("Col2","f8"),("Col3","i8")]) 
>>> print newarray 
[[('Hello', 0.0, 0L) ('2.5', 0.0, 0L) ('3', 0.0, 0L)] 
[('World', 0.0, 0L) ('3.6', 0.0, 0L) ('2', 0.0, 0L)]] 

Ambos estos enfoques intentan convertir cada entrada en myarray en un registro con el tipo de letra dado, por lo que se insertan los ceros adicionales. No puedo entender cómo convertir cada fila en un registro.

Otro intento:

>>> newarray = myarray.copy() 
>>> newarray.dtype = [("Col1","S8"),("Col2","f8"),("Col3","i8")] 
>>> print newarray 
[[('Hello', 1.7219343871178711e-317, 51L)] 
[('World', 1.7543139673493688e-317, 50L)]] 

Esta vez no se realiza ninguna conversión real. Los datos existentes en la memoria solo se vuelven a interpretar como el nuevo tipo de datos.

La matriz que estoy comenzando se está leyendo desde un archivo de texto. Los tipos de datos no se conocen con anticipación, por lo que no puedo establecer el tipo en el momento de la creación. Necesito una solución elegante y de alto rendimiento que funcione bien para casos generales ya que haré este tipo de conversión muchas, muchas veces para una gran variedad de aplicaciones.

Gracias!

Respuesta

26

Puede "crear una matriz registro de una lista (plana) de arrays" usando numpy.core.records.fromarrays de la siguiente manera:

>>> import numpy as np 
>>> myarray = np.array([("Hello",2.5,3),("World",3.6,2)]) 
>>> print myarray 
[['Hello' '2.5' '3'] 
['World' '3.6' '2']] 


>>> newrecarray = np.core.records.fromarrays(myarray.transpose(), 
              names='col1, col2, col3', 
              formats = 'S8, f8, i8') 

>>> print newrecarray 
[('Hello', 2.5, 3) ('World', 3.5999999046325684, 2)] 

yo estaba tratando de hacer algo similar. Descubrí que cuando numpy creaba una matriz estructurada a partir de una matriz 2D existente (usando np.core.records.fromarrays), consideraba cada columna (en lugar de cada fila) en la matriz 2-D como un registro. Entonces tienes que transponerlo. Este comportamiento de numpy no parece muy intuitivo, pero tal vez haya una buena razón para ello.

+2

con 'fromrecords' se puede evitar el' transpose() ' –

2

Bien, he estado luchando con esto desde hace un tiempo pero he encontrado una manera de hacer esto que no requiere demasiado esfuerzo. Me disculpo si este código es "sucio" ....

Vamos a empezar con una matriz 2D:

mydata = numpy.array([['text1', 1, 'longertext1', 0.1111], 
        ['text2', 2, 'longertext2', 0.2222], 
        ['text3', 3, 'longertext3', 0.3333], 
        ['text4', 4, 'longertext4', 0.4444], 
        ['text5', 5, 'longertext5', 0.5555]]) 

por lo que terminamos con una matriz 2D con 4 columnas y 5 filas:

mydata.shape 
Out[30]: (5L, 4L) 

Para usar numpy.core.records.arrays - que necesitan para suministrar el argumento de entrada como una lista de matrices de modo:

tuple(mydata) 
Out[31]: 
(array(['text1', '1', 'longertext1', '0.1111'], 
     dtype='|S11'), 
array(['text2', '2', 'longertext2', '0.2222'], 
     dtype='|S11'), 
array(['text3', '3', 'longertext3', '0.3333'], 
     dtype='|S11'), 
array(['text4', '4', 'longertext4', '0.4444'], 
     dtype='|S11'), 
array(['text5', '5', 'longertext5', '0.5555'], 
     dtype='|S11')) 

Esto produce una matriz separada por cada fila de datos, pero, necesitamos las matrices de entrada para estar a la columna así que lo que se necesita es:

tuple(mydata.transpose()) 
Out[32]: 
(array(['text1', 'text2', 'text3', 'text4', 'text5'], 
     dtype='|S11'), 
array(['1', '2', '3', '4', '5'], 
     dtype='|S11'), 
array(['longertext1', 'longertext2', 'longertext3', 'longertext4', 
     'longertext5'], 
     dtype='|S11'), 
array(['0.1111', '0.2222', '0.3333', '0.4444', '0.5555'], 
     dtype='|S11')) 

por último, es necesario que haya una lista de matrices, no una tupla, así que envuelven lo anterior en la lista() de la siguiente manera:

list(tuple(mydata.transpose())) 

que se ordena nuestro argumento de entrada de datos ... El siguiente es el tipo:

mydtype = numpy.dtype([('My short text Column', 'S5'), 
         ('My integer Column', numpy.int16), 
         ('My long text Column', 'S11'), 
         ('My float Column', numpy.float32)]) 
mydtype 
Out[37]: dtype([('My short text Column', '|S5'), ('My integer Column', '<i2'), ('My long text Column', '|S11'), ('My float Column', '<f4')]) 

Bien, ahora podemos pasar a la que numpy.core.records.array():

myRecord = numpy.core.records.array(list(tuple(mydata.transpose())), dtype=mydtype) 

... y los dedos cruzados:

myRecord 
Out[36]: 
rec.array([('text1', 1, 'longertext1', 0.11110000312328339), 
     ('text2', 2, 'longertext2', 0.22220000624656677), 
     ('text3', 3, 'longertext3', 0.33329999446868896), 
     ('text4', 4, 'longertext4', 0.44440001249313354), 
     ('text5', 5, 'longertext5', 0.5554999709129333)], 
     dtype=[('My short text Column', '|S5'), ('My integer Column', '<i2'), ('My long text Column', '|S11'), ('My float Column', '<f4')]) 

Voila! Puede indexar por nombre de columna como en:

myRecord['My float Column'] 
Out[39]: array([ 0.1111 , 0.22220001, 0.33329999, 0.44440001, 0.55549997], dtype=float32) 

Espero que esto ayude como yo perdido tanto tiempo con numpy.asarray y mydata.astype etc tratando de conseguir que esto funcione antes de que finalmente la elaboración de este método.

8

supongo

new_array = np.core.records.fromrecords([("Hello",2.5,3),("World",3.6,2)], 
             names='Col1,Col2,Col3', 
             formats='S8,f8,i8') 

es lo que desea.

1

Si los datos se inicia como una lista de tuplas, a continuación, crear una matriz estructurada es sencillo:

In [228]: alist = [("Hello",2.5,3),("World",3.6,2)] 
In [229]: dt = [("Col1","S8"),("Col2","f8"),("Col3","i8")] 
In [230]: np.array(alist, dtype=dt) 
Out[230]: 
array([(b'Hello', 2.5, 3), (b'World', 3.6, 2)], 
     dtype=[('Col1', 'S8'), ('Col2', '<f8'), ('Col3', '<i8')]) 

La complicación aquí es que la lista de tuplas se ha convertido en una matriz de cadenas 2d:

In [231]: arr = np.array(alist) 
In [232]: arr 
Out[232]: 
array([['Hello', '2.5', '3'], 
     ['World', '3.6', '2']], 
     dtype='<U5') 

podríamos usar el conocido zip* enfoque de 'transposición' esta matriz - en realidad queremos un doble transposición:

In [234]: list(zip(*arr.T)) 
Out[234]: [('Hello', '2.5', '3'), ('World', '3.6', '2')] 

zip nos ha proporcionado convenientemente una lista de tuplas. Ahora podemos volver a crear la matriz con dtype deseada:

In [235]: np.array(_, dtype=dt) 
Out[235]: 
array([(b'Hello', 2.5, 3), (b'World', 3.6, 2)], 
     dtype=[('Col1', 'S8'), ('Col2', '<f8'), ('Col3', '<i8')]) 

La respuesta aceptada utiliza fromarrays:

In [236]: np.rec.fromarrays(arr.T, dtype=dt) 
Out[236]: 
rec.array([(b'Hello', 2.5, 3), (b'World', 3.6, 2)], 
      dtype=[('Col1', 'S8'), ('Col2', '<f8'), ('Col3', '<i8')]) 

Internamente, fromarrays toma un recfunctions enfoque común: crear matriz de destino, y copiar los valores de nombre de campo. Efectivamente lo hace:

In [237]: newarr = np.empty(arr.shape[0], dtype=dt) 
In [238]: for n, v in zip(newarr.dtype.names, arr.T): 
    ...:  newarr[n] = v 
    ...:  
In [239]: newarr 
Out[239]: 
array([(b'Hello', 2.5, 3), (b'World', 3.6, 2)], 
     dtype=[('Col1', 'S8'), ('Col2', '<f8'), ('Col3', '<i8')]) 
Cuestiones relacionadas