Tengo conjuntos de datos muy grandes que se almacenan en archivos binarios en el disco duro. Aquí hay un ejemplo de la estructura del archivo:
Encabezado de archivo
149 Byte ASCII Header
Inicio de grabación
4 Byte Int - Record Timestamp
Ejemplo de inicio
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
Muestra final
Hay 122,880 muestras por registro y 713 registros por archivo. Esto produce un tamaño total de 700,910,521 Bytes. La frecuencia de muestreo y el número de registros varían a veces, así que tengo que codificar para detectar el número de cada uno por archivo.
Actualmente, el código que utilizo para importar estos datos en arreglos funciona así:
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'
Esto toma actualmente unos 250 segundos por archivo de 700 MB, lo que para mí me parece muy alto. ¿Hay una manera más eficiente de hacer esto?
Usando el método de archivo de Npypy con un tipo de dato personalizado, reduzca el tiempo de ejecución a 9 segundos, 27 veces más rápido que el código original anterior. El código final está abajo.
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'
Algunos consejos:
No uses el módulo de estructura. En su lugar, use los tipos de datos estructurados de fromfile
y desde el fromfile
. Consulte aquí: http://scipy-lectures.github.com/advanced/advanced_numpy/index.html#example-reading-wav-files
Puede leer todos los registros a la vez, pasando un conteo adecuado = to fromfile
.
Algo como esto (no probado, pero tienes la idea):
importar numpy como np archivo = abrir (archivo de entrada, 'rb') header = file.read (149) # ... analiza el encabezado como lo hiciste ... record_dtype = np.dtype ([ ('timestamp', '
Una ineficiencia evidente es el uso de hstack
en un bucle:
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 ) )
En cada iteración, esto asigna una matriz un poco más grande para cada una de las series y copia todos los datos leídos hasta ahora. Esto implica una gran cantidad de copias innecesarias y potencialmente puede conducir a una fragmentación de la memoria defectuosa.
Acumularía los valores de time_stamp
en una lista y haría un hstack
al final, y haría exactamente lo mismo para record_t
etc.
Si eso no trae suficientes mejoras de rendimiento, comentaría el cuerpo del bucle y comenzaría a recuperar las cosas de una en una, para ver dónde se gasta exactamente el tiempo.