Эффективный способ создания массивных массивов из двоичных файлов

У меня очень большие наборы данных, которые хранятся в двоичных файлах на жестком диске. Ниже приведен пример структуры файла:

Заголовок файла

149 Byte ASCII Header 

Запуск записи

 4 Byte Int - Record Timestamp 

Пример запуска

 2 Byte Int - Data Stream 1 Sample 2 Byte Int - Data Stream 2 Sample 2 Byte Int - Data Stream 3 Sample 2 Byte Int - Data Stream 4 Sample 

Конечный конец

Есть 122,880 образцов на запись и 713 записей на файл. Это дает общий размер 700 910 521 байт. Частота выборки и количество записей иногда меняются, поэтому мне приходится кодировать для определения количества каждого файла.

В настоящее время код, который я использую для импорта этих данных в массивы, работает следующим образом:

 from time import clock from numpy import zeros , int16 , int32 , hstack , array , savez from struct import unpack from os.path import getsize start_time = clock() file_size = getsize(input_file) with open(input_file,'rb') as openfile: input_data = openfile.read() header = input_data[:149] record_size = int(header[23:31]) number_of_records = ( file_size - 149 ) / record_size sample_rate = ( ( record_size - 4 ) / 4 ) / 2 time_series = zeros(0,dtype=int32) t_series = zeros(0,dtype=int16) x_series = zeros(0,dtype=int16) y_series = zeros(0,dtype=int16) z_series = zeros(0,dtype=int16) for record in xrange(number_of_records): time_stamp = array( unpack( '<l' , input_data[ 149 + (record * record_size) : 149 + (record * record_size) + 4 ] ) , dtype = int32 ) unpacked_record = unpack( '<' + str(sample_rate * 4) + 'h' , input_data[ 149 + (record * record_size) + 4 : 149 + ( (record + 1) * record_size ) ] ) record_t = zeros(sample_rate , dtype=int16) record_x = zeros(sample_rate , dtype=int16) record_y = zeros(sample_rate , dtype=int16) record_z = zeros(sample_rate , dtype=int16) for sample in xrange(sample_rate): record_t[sample] = unpacked_record[ ( sample * 4 ) + 0 ] record_x[sample] = unpacked_record[ ( sample * 4 ) + 1 ] record_y[sample] = unpacked_record[ ( sample * 4 ) + 2 ] record_z[sample] = unpacked_record[ ( sample * 4 ) + 3 ] time_series = hstack ( ( time_series , time_stamp ) ) t_series = hstack ( ( t_series , record_t ) ) x_series = hstack ( ( x_series , record_x ) ) y_series = hstack ( ( y_series , record_y ) ) z_series = hstack ( ( z_series , record_z ) ) savez(output_file, t=t_series , x=x_series ,y=y_series, z=z_series, time=time_series) end_time = clock() print 'Total Time',end_time - start_time,'seconds' 

Это в настоящее время занимает около 250 секунд на 700 МБ файла, что для меня кажется очень высоким. Есть ли более эффективный способ сделать это?

Окончательное решение

Использование метода numpy fromfile с настраиваемым dtype сокращает время выполнения до 9 секунд, в 27 раз быстрее, чем исходный код выше. Окончательный код приведен ниже.

 from numpy import savez, dtype , fromfile from os.path import getsize from time import clock start_time = clock() file_size = getsize(input_file) openfile = open(input_file,'rb') header = openfile.read(149) record_size = int(header[23:31]) number_of_records = ( file_size - 149 ) / record_size sample_rate = ( ( record_size - 4 ) / 4 ) / 2 record_dtype = dtype( [ ( 'timestamp' , '<i4' ) , ( 'samples' , '<i2' , ( sample_rate , 4 ) ) ] ) data = fromfile(openfile , dtype = record_dtype , count = number_of_records ) time_series = data['timestamp'] t_series = data['samples'][:,:,0].ravel() x_series = data['samples'][:,:,1].ravel() y_series = data['samples'][:,:,2].ravel() z_series = data['samples'][:,:,3].ravel() savez(output_file, t=t_series , x=x_series ,y=y_series, z=z_series, fid=time_series) end_time = clock() print 'It took',end_time - start_time,'seconds' 

4 Solutions collect form web for “Эффективный способ создания массивных массивов из двоичных файлов”

Некоторые подсказки:

Что-то вроде этого (непроверено, но вы поняли):

 импортировать numpy как np

 file = open (input_file, 'rb')
 header = file.read (149)

 # ... разобрать заголовок, как вы это делали ...

 record_dtype = np.dtype ([
     ('timestamp', '<i4'), 
     ('samples', '<i2', (sample_rate, 4))
 ])

 data = np.fromfile (файл, dtype = record_dtype, count = number_of_records)
 # NB: count может быть опущен - он просто считывает весь файл, затем

 time_series = data ['timestamp']
 t_series = data ['samples'] [:,:, 0] .ravel ()
 x_series = data ['samples'] [:,:, 1] .ravel ()
 y_series = data ['samples'] [:,:, 2] .ravel ()
 z_series = data ['samples'] [:,:, 3] .ravel ()

Одной вопиющей неэффективностью является использование hstack в цикле:

  time_series = hstack ( ( time_series , time_stamp ) ) t_series = hstack ( ( t_series , record_t ) ) x_series = hstack ( ( x_series , record_x ) ) y_series = hstack ( ( y_series , record_y ) ) z_series = hstack ( ( z_series , record_z ) ) 

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

Я бы накапливал значения time_stamp в списке и делал один hstack в конце и делал бы точно то же самое для record_t и т. Д.

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

Numpy поддерживает отображение двоичных данных из данных непосредственно в массив подобно объектам через numpy.memmap . Возможно, вы сможете скопировать файл и извлечь данные, которые вам нужны, с помощью смещений.

Для правильности верности просто используйте numpy.byteswap для того, что вы прочитали. Вы можете использовать условное выражение для проверки консистенции хост-системы:

 if struct.pack('=f', np.pi) == struct.pack('>f', np.pi): # Host is big-endian, in-place conversion arrayName.byteswap(True) 

У меня есть удовлетворительные результаты с аналогичной проблемой (многоканальные файлы двоичных данных с несколькими разрешениями) с помощью array и struct.unpack . В моей проблеме я хотел получить непрерывные данные для каждого канала, но файл имел структуру с интервальной ориентацией, а не структуру, ориентированную на канал.

«Секрет» состоит в том, чтобы сначала прочитать весь файл и только затем распределить срезы известного размера в нужные контейнеры (в приведенном ниже коде self.channel_content[channel]['recording'] является объектом array типов):

 f = open(somefilename, 'rb') fullsamples = array('h') fullsamples.fromfile(f, os.path.getsize(wholefilename)/2 - f.tell()) position = 0 for rec in xrange(int(self.header['nrecs'])): for channel in self.channel_labels: samples = int(self.channel_content[channel]['nsamples']) self.channel_content[channel]['recording'].extend(fullsamples[position:position+samples]) position += samples 

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

Надеюсь, поможет!

  • Решение коэффициентов набора данных с использованием curve_fit из scipy.optimize
  • ValueError: установка элемента массива с последовательностью с использованием numpy
  • Преобразование MATLAB в Python (NumPy, SciPy, MatplotLib?)
  • Сохранение массивов в виде столбцов с помощью np.savetxt
  • `AttributeError: rint` при использовании numpy.round
  • Как центрировать метки в графике гистограммы
  • Как найти ближайшую точку к значениям сетки
  • Создание массива numpy из списка дает неправильную форму
  • Python - лучший язык программирования в мире.