ti-enxame.com

Converter uma matriz numpy 2D em uma matriz estruturada

Estou tentando converter uma matriz bidimensional em uma matriz estruturada com campos nomeados. Eu quero que cada linha na matriz 2D seja um novo registro na matriz estruturada. Infelizmente, nada do que tentei está funcionando da maneira que esperava.

Estou começando com:

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

Quero converter para algo assim:

>>> 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)]

O que eu tentei:

>>> 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)]]

Ambas as abordagens tentam converter cada entrada em myarray em um registro com o tipo d fornecido, de forma que os zeros extras sejam inseridos. Não consigo descobrir como fazer com que ele converta cada linha em um registro.

Outra tentativa:

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

Desta vez, nenhuma conversão real é realizada. Os dados existentes na memória são apenas reinterpretados como o novo tipo de dados.

A matriz com a qual estou começando está sendo lida de um arquivo de texto. Os tipos de dados não são conhecidos com antecedência, então não posso definir o dtype no momento da criação. Preciso de uma solução elegante e de alto desempenho que funcione bem para casos gerais, já que farei esse tipo de conversão muitas e muitas vezes para uma grande variedade de aplicativos.

Obrigado!

36
Emma

Você pode "criar uma matriz de registro a partir de uma lista (simples) de matrizes" usando numpy.core.records.fromarrays da seguinte maneira:

>>> 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)]

Eu estava tentando fazer algo semelhante. Descobri que quando numpy criou uma matriz estruturada a partir de uma matriz 2D existente (usando np.core.records.fromarrays), considerou cada coluna (em vez de cada linha) na matriz 2-D como um registro. Então você tem que transpor. Esse comportamento de entorpecido não parece muito intuitivo, mas talvez haja uma boa razão para isso.

32
Curious2learn

Eu acho

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

é o que você quer.

11
Ruggero Turra

Se os dados começam como uma lista de tuplas, a criação de uma matriz estruturada é direta:

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')])

A complicação aqui é que a lista de tuplas foi transformada em uma matriz de string 2d:

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

Poderíamos usar o conhecido Zip* abordagem para 'transpor' esta matriz - na verdade, queremos uma transposição dupla:

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

Zip convenientemente nos deu uma lista de tuplas. Agora podemos recriar a matriz com o dtype desejado:

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')])

A resposta aceita usa 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 adota uma abordagem comum de recfunctions: criar um array de destino e copiar valores por nome de campo. Efetivamente, ele faz:

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')])
6
hpaulj

Ok, eu tenho lutado com isso por um tempo agora, mas descobri uma maneira de fazer isso que não exige muito esforço. Peço desculpas se este código estiver "sujo" ....

Vamos começar com uma 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]])

Portanto, acabamos com uma matriz 2D com 4 colunas e 5 linhas:

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

Para usar numpy.core.records.arrays - precisamos fornecer o argumento de entrada como uma lista de matrizes para:

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'))

Isso produz uma matriz separada por linha de dados, MAS, precisamos que as matrizes de entrada sejam por coluna, então o que precisaremos é:

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 fim, precisa ser uma lista de arrays, não uma tupla, então envolvemos o acima em list () como abaixo:

list(Tuple(mydata.transpose()))

Este é o nosso argumento de entrada de dados classificado .... a seguir está o dtype:

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')])

Ok, agora podemos passar isso para numpy.core.records.array ():

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

... e 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! Você pode indexar por nome de coluna como em:

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

Espero que isso ajude, pois perdi muito tempo com numpy.asarray e mydata.astype etc tentando fazer isso funcionar antes de finalmente trabalhar com esse método.

2
Philip Lawrence