Преобразование двумерного массива в структурный массив - PullRequest
27 голосов
/ 02 сентября 2010

Я пытаюсь преобразовать двумерный массив в структурированный массив с именованными полями.Я хочу, чтобы каждая строка в 2D-массиве была новой записью в структурированном массиве.К сожалению, ничего из того, что я пробовал, не работает так, как я ожидаю.

Я начинаю с:

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

Я хочу преобразовать в нечто, похожее на это:

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

Что я пробовал:

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

Оба эти подхода пытаются преобразовать каждую запись в myarray в запись с заданным dtype, поэтому вставляются дополнительные нули.Я не могу понять, как заставить его преобразовать каждую строку в запись.

Другая попытка:

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

На этот раз фактическое преобразование не выполняется.Существующие данные в памяти просто интерпретируются как новый тип данных.

Массив, с которого я начинаю, читается из текстового файла.Типы данных не известны заранее, поэтому я не могу установить dtype во время создания.Мне нужно высокопроизводительное и элегантное решение, которое будет хорошо работать в общих случаях, так как я буду многократно использовать этот тип преобразования для широкого спектра приложений.

Спасибо!

Ответы [ 4 ]

27 голосов
/ 05 марта 2011

Вы можете "создать массив записей из (плоского) списка массивов", используя numpy.core.records.fromarrays следующим образом:

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

Я пытался сделатьчто-то похожее.Я обнаружил, что когда numpy создал структурированный массив из существующего 2D-массива (используя np.core.records.fromarrays), он рассматривал каждый столбец (вместо каждой строки) в 2-D массиве как запись.Таким образом, вы должны транспонировать это.Такое поведение NumPy не кажется интуитивно понятным, но, возможно, есть веская причина для этого.

9 голосов
/ 02 апреля 2014

Я думаю

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

это то, что вы хотите.

2 голосов
/ 03 июня 2017

Если данные начинаются как список кортежей, то создание структурированного массива просто:

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

Сложность заключается в том, что список кортежей был преобразован в двумерный массив строк:

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

Мы могли бы использовать известный zip* подход для «транспонирования» этого массива - на самом деле мы хотим двойную транспонирование:

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

zip удобно дал нам список кортежей. Теперь мы можем воссоздать массив с желаемым dtype:

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

В принятом ответе используется 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')])

Внутренне, fromarrays использует общий подход recfunctions: создание целевого массива и копирование значений по имени поля. Эффективно это делает:

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')])
1 голос
/ 01 марта 2013

Хорошо, я боролся с этим некоторое время, но я нашел способ сделать это, не требуя слишком больших усилий.Я прошу прощения, если этот код "грязный" ....

Давайте начнем с 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]])

Итак, мы в итоге получаем 2D-массив с 4 столбцами и 5 строками:

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

Чтобы использовать numpy.core.records.arrays - нам нужно предоставить входной аргумент в виде списка массивов, так:

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

Это создает отдельный массив для каждой строкиданные, НО, нам нужно, чтобы входные массивы были по столбцам, поэтому нам понадобится:

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

Наконец, это должен быть список массивов, а не кортеж, поэтому мы оборачиваем вышеупомянутое в список (), как показано ниже:

list(tuple(mydata.transpose()))

Это наш отсортированный аргумент ввода данных .... следующий - тип 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')])

Хорошо, теперь мы можем передать это numpy.core.records.array ():

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

... и скрестив пальцы:

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

Вуаля!Вы можете индексировать по имени столбца следующим образом:

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

Надеюсь, это поможет, поскольку я потратил столько времени на numpy.asarray, mydata.astype и т. Д., Пытаясь заставить это работать, прежде чем окончательно отработать этот метод.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...