Python File Slurp con conversión endian
-
06-07-2019 - |
Pregunta
Recientemente se le preguntó cómo hacer un sorbo de archivos en python , y la respuesta aceptada sugirió algo como:
with open('x.txt') as x: f = x.read()
¿Cómo haría esto para leer el archivo y convertir la representación endiana de los datos?
Por ejemplo, tengo un archivo binario de 1GB que es solo un grupo de flotadores de precisión individuales empaquetados como un gran endian y quiero convertirlo en little endian y volcarlo en una matriz numpy. A continuación se muestra la función que escribí para lograr esto y un código real que lo llama. Utilizo struct.unpack
para hacer la conversión endian e intenté acelerar todo usando mmap
.
Mi pregunta es, ¿estoy usando el sorbo correctamente con mmap
y struct.unpack
? ¿Hay una forma más limpia y rápida de hacer esto? En este momento lo que tengo funciona, pero realmente me gustaría aprender cómo hacerlo mejor.
Gracias de antemano!
#!/usr/bin/python
from struct import unpack
import mmap
import numpy as np
def mmapChannel(arrayName, fileName, channelNo, line_count, sample_count):
"""
We need to read in the asf internal file and convert it into a numpy array.
It is stored as a single row, and is binary. Thenumber of lines (rows), samples (columns),
and channels all come from the .meta text file
Also, internal format files are packed big endian, but most systems use little endian, so we need
to make that conversion as well.
Memory mapping seemed to improve the ingestion speed a bit
"""
# memory-map the file, size 0 means whole file
# length = line_count * sample_count * arrayName.itemsize
print "\tMemory Mapping..."
with open(fileName, "rb") as f:
map = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
map.seek(channelNo*line_count*sample_count*arrayName.itemsize)
for i in xrange(line_count*sample_count):
arrayName[0, i] = unpack('>f', map.read(arrayName.itemsize) )[0]
# Same method as above, just more verbose for the maintenance programmer.
# for i in xrange(line_count*sample_count): #row
# be_float = map.read(arrayName.itemsize) # arrayName.itemsize should be 4 for float32
# le_float = unpack('>f', be_float)[0] # > for big endian, < for little endian
# arrayName[0, i]= le_float
map.close()
return arrayName
print "Initializing the Amp HH HV, and Phase HH HV arrays..."
HHamp = np.ones((1, line_count*sample_count), dtype='float32')
HHphase = np.ones((1, line_count*sample_count), dtype='float32')
HVamp = np.ones((1, line_count*sample_count), dtype='float32')
HVphase = np.ones((1, line_count*sample_count), dtype='float32')
print "Ingesting HH_Amp..."
HHamp = mmapChannel(HHamp, 'ALPSRP042301700-P1.1__A.img', 0, line_count, sample_count)
print "Ingesting HH_phase..."
HHphase = mmapChannel(HHphase, 'ALPSRP042301700-P1.1__A.img', 1, line_count, sample_count)
print "Ingesting HV_AMP..."
HVamp = mmapChannel(HVamp, 'ALPSRP042301700-P1.1__A.img', 2, line_count, sample_count)
print "Ingesting HV_phase..."
HVphase = mmapChannel(HVphase, 'ALPSRP042301700-P1.1__A.img', 3, line_count, sample_count)
print "Reshaping...."
HHamp_orig = HHamp.reshape(line_count, -1)
HHphase_orig = HHphase.reshape(line_count, -1)
HVamp_orig = HVamp.reshape(line_count, -1)
HVphase_orig = HVphase.reshape(line_count, -1)
Solución
with open(fileName, "rb") as f:
arrayName = numpy.fromfile(f, numpy.float32)
arrayName.byteswap(True)
Bastante difícil de superar por velocidad Y concisión ;-). Para byteswap ver aquí (el argumento True
significa, "hazlo en el lugar"); para fromfile ver aquí .
Esto funciona como está en las máquinas little-endian (dado que los datos son big-endian, se necesita el intercambio de bytes). Puede probar si ese es el caso para hacer el intercambio de bytes condicionalmente, cambiar la última línea de una llamada incondicional a byteswap en, por ejemplo:
if struct.pack('=f', 2.3) == struct.pack('<f', 2.3):
arrayName.byteswap(True)
es decir, una llamada a byteswap condicional en una prueba de little-endianness.
Otros consejos
Ligeramente modificado @Alex Martelli respuesta :
arr = numpy.fromfile(filename, numpy.dtype('>f4'))
# no byteswap is needed regardless of endianess of the machine
Puede combinar una solución basada en ASM utilizando CorePy . Sin embargo, me pregunto si podría obtener suficiente rendimiento de alguna otra parte de su algoritmo. La E / S y las manipulaciones en fragmentos de datos de 1 GB llevarán un tiempo, independientemente de cómo lo corte.
Otra cosa que puede resultarle útil sería cambiar a C una vez que haya realizado un prototipo del algoritmo en Python. Hice esto para manipulaciones en un conjunto de datos DEM (altura) de todo el mundo una vez. Todo fue mucho más tolerable una vez que me alejé del guión interpretado.
Esperaría que algo como esto sea más rápido
arrayName[0] = unpack('>'+'f'*line_count*sample_count, map.read(arrayName.itemsize*line_count*sample_count))
No utilice map
como nombre de variable