2016-02-19 2 views
1

Я использую genfromtxt для загрузки больших файлов csv в структурированные массивы. Мне нужно отсортировать данные (используя несколько полей), выполнить некоторую работу и затем восстановить данные в исходном порядке. Мой план состоит в том, чтобы добавить другое поле в данные и поместить номер строки в это поле до применения первого сортировки. Затем он может использоваться для отмены порядка в конце. Я думал, что может быть элегантный способ добавления этого поля номеров записей, но после нескольких часов попыток и поиска идей у ​​меня нет ничего особого.Как добавить поле «RowNumber» в структурированный массив numpy?

import numpy 
import numpy.lib.recfunctions as rfn 
def main(): 
    csvDataFile = 'C:\\File1.csv' 
    csvData = numpy.genfromtxt(csvDataFile, delimiter=',',names = True, dtype='f8') 
    rowNums = numpy.zeros(len(csvData),dtype=[('RowID','f8')]) 
    #populate and add column for RowID 
    for i in range (0, len(csvData)): 
     rowNums['RowID'][i]=i 
    csvDataWithID = rfn.merge_arrays((csvData, rowNums), asrecarray=True, flatten=True) 

В recfunctions.merge_arrays , в частности, идет очень медленно и добавление номера строк по одному кажется настолько старая школа. Ваши идеи были бы с благодарностью получены.

+1

есть ли причина, что вы загрузка CSV и делать свою работу исключительно в 'numpy' вместо того, чтобы использовать' pandas'? Для меня «pandas» кажется естественным выбором, и все тесты, которые я видел, показали, что «pandas» имеет превосходную производительность для синтаксического анализа csv. – root

+0

Спасибо за подсказку. Раньше я не встречал панд. Я посмотрю ... –

ответ

0

Вам не нужно заполнить rowNums итеративно:

In [93]: rowNums=np.zeros(10,dtype=[('RowID','f8')]) 
In [94]: for i in range(0,10): 
    ....:  rowNums['RowID'][i]=i 
    ....:  
In [95]: rowNums 
Out[95]: 
array([(0.0,), (1.0,), (2.0,), (3.0,), (4.0,), (5.0,), (6.0,), (7.0,), 
     (8.0,), (9.0,)], 
     dtype=[('RowID', '<f8')]) 

Просто назначить диапазон значений в поле:

In [96]: rowNums['RowID']=np.arange(10) 
In [97]: rowNums 
Out[97]: 
array([(0.0,), (1.0,), (2.0,), (3.0,), (4.0,), (5.0,), (6.0,), (7.0,), 
     (8.0,), (9.0,)], 
     dtype=[('RowID', '<f8')]) 

rfn.merge_arrays не должно быть медленным - если csvData.dtype не имеет большой количество полей. Эта функция создает новый dtype, который объединяет поля двух входов, а затем копирует поле данных по полю. Для многих строк и всего нескольких полей это довольно быстро.

Но вы должны иметь возможность получить первоначальный заказ без добавления дополнительного поля.

А 2 поле 1d массив:

In [118]: x = np.array([(4,2),(1, 0), (0, 1),(1,2),(3,1)], dtype=[('x', '<i4'), ('y', '<i4')]) 
In [119]: i = np.argsort(x, order=('y','x')) 
In [120]: i 
Out[120]: array([1, 2, 4, 3, 0], dtype=int32) 
In [121]: x[i] 
Out[121]: 
array([(1, 0), (0, 1), (3, 1), (1, 2), (4, 2)], 
     dtype=[('x', '<i4'), ('y', '<i4')]) 

То же значение теперь сортируется сначала на y, то на x.

In [122]: j=np.argsort(i) 
In [123]: j 
Out[123]: array([4, 0, 1, 3, 2], dtype=int32) 
In [124]: x[i][j] 
Out[124]: 
array([(4, 2), (1, 0), (0, 1), (1, 2), (3, 1)], 
     dtype=[('x', '<i4'), ('y', '<i4')]) 

Назад к исходному порядку

Я мог бы добавить массив индекс строки в x, а затем сделал то по этому вопросу. Но зачем добавлять его; почему бы не просто применять i в отдельный массив:

In [127]: np.arange(5)[i] 
Out[127]: array([1, 2, 4, 3, 0]) 

Но сортировка, что это так же, как сортировка i.


merge_arrays делает по существу следующее:

Союз DTYPE:

In [139]: dt=np.dtype(rowNums.dtype.descr+x.dtype.descr) 
In [140]: y=np.zeros((5,),dtype=dt) 

заполнить значения:

In [141]: y['RowID']=np.arange(5) 
In [143]: for name in x.dtype.names: 
    y[name]=x[name] 
In [144]: y 
Out[144]: 
array([(0.0, 4, 2), (1.0, 1, 0), (2.0, 0, 1), (3.0, 1, 2), (4.0, 3, 1)], 
     dtype=[('RowID', '<f8'), ('x', '<i4'), ('y', '<i4')]) 

И чтобы проверить мой argsort из argsort идеи:

In [145]: y[i] 
Out[145]: 
array([(1.0, 1, 0), (2.0, 0, 1), (4.0, 3, 1), (3.0, 1, 2), (0.0, 4, 2)], 
     dtype=[('RowID', '<f8'), ('x', '<i4'), ('y', '<i4')]) 
In [146]: np.argsort(y[i],order=('RowID')) 
Out[146]: array([4, 0, 1, 3, 2], dtype=int32) 
In [147]: j 
Out[147]: array([4, 0, 1, 3, 2], dtype=int32) 

Сортировка по-умолчанию RowID такая же, как и сортировка по i.


Любопытно merge_arrays совсем немного медленнее, чем моя реконструкция:

In [163]: rfn.merge_arrays([rowNums,x],flatten=True) 
Out[163]: 
array([(0.0, 4, 2), (1.0, 1, 0), (2.0, 0, 1), (3.0, 1, 2), (4.0, 3, 1)], 
     dtype=[('RowID', '<f8'), ('x', '<i4'), ('y', '<i4')]) 
In [164]: timeit rfn.merge_arrays([rowNums,x],flatten=True) 
10000 loops, best of 3: 161 µs per loop 

In [165]: %%timeit 
dt=np.dtype(rowNums.dtype.descr+x.dtype.descr) 
y=np.zeros((5,),dtype=dt) 
y['RowID']=rowNums['RowID'] 
for name in x.dtype.names: 
    y[name]=x[name] 
10000 loops, best of 3: 38.4 µs per loop 
+0

Большое спасибо за ваш подробный ответ. Есть ряд уроков, извлеченных из ваших примеров ... –

0
rowNums = np.zeros(len(csvData),dtype=[('RowID','f8')]) 
rowNums['RowID']=np.arange(len(csvData)) 

выше экономит ок полсекунды на файл с файлами CSV я использую. Очень хорошо.

Однако ключевым моментом было то, как эффективно получить отчет о порядке сортировки. Это наиболее элегантно разрешено с использованием;

sortorder = np.argsort(csvData, 'col_1','col_2','col_3','col_4','col_5') 

дает массив, в котором перечислен порядке элементов в CsvData при сортировке по перевалам 1 до 5. Это исключает необходимость сделать, заселить и слейте RowID колонки, спасая меня по 15s в CSV-файл (более 6hrs по всем моему набору данных.)

Большого спасибо @hpaulj

Смежные вопросы