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)
¿Fue útil?

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top