Domanda

Di recente è stato chiesto come eseguire un file in un python e la risposta accettata ha suggerito qualcosa come:

with open('x.txt') as x: f = x.read()

Come farei per fare questo per leggere il file e convertire la rappresentazione endian dei dati?

Ad esempio, ho un file binario da 1 GB che è solo un mucchio di singoli float di precisione impacchettati come un big endian e voglio convertirlo in little endian e scaricare in un array numpy. Di seguito è la funzione che ho scritto per realizzare questo e un po 'di codice reale che lo chiama. Uso struct.unpack per eseguire la conversione endian e ho cercato di velocizzare tutto usando mmap .

La mia domanda allora è: sto usando correttamente lo slurp con mmap e struct.unpack ? C'è un modo più pulito e veloce per farlo? In questo momento quello che ho funziona, ma mi piacerebbe davvero imparare a farlo meglio.

Grazie in anticipo!

#!/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)
È stato utile?

Soluzione

with open(fileName, "rb") as f:
  arrayName = numpy.fromfile(f, numpy.float32)
arrayName.byteswap(True)

Abbastanza difficile da battere per velocità e concisione ;-). Per byteswap vedi qui (l'argomento True significa, "fallo sul posto"); per fromfile vedi qui .

Funziona come su macchine little-endian (poiché i dati sono big-endian, è necessario il byteswap). Puoi verificare se è il caso di eseguire il byteswap in modo condizionale, modificare l'ultima riga da una chiamata incondizionata a byteswap in, ad esempio:

if struct.pack('=f', 2.3) == struct.pack('<f', 2.3):
  arrayName.byteswap(True)

vale a dire, una chiamata a byteswap è subordinata a un test di little-endianness.

Altri suggerimenti

Leggermente modificato Risposta di @Alex Martelli :

arr = numpy.fromfile(filename, numpy.dtype('>f4'))
# no byteswap is needed regardless of endianess of the machine

Potresti mettere insieme una soluzione basata su ASM usando CorePy . Mi chiedo però se potresti essere in grado di ottenere abbastanza prestazioni da qualche altra parte del tuo algoritmo. L'I / O e le manipolazioni su blocchi di dati da 1 GB impiegheranno un po 'di tempo in qualunque modo tu li tagli.

Un'altra cosa che potresti trovare utile sarebbe passare a C dopo aver prototipato l'algoritmo in Python. L'ho fatto per manipolazioni su un set di dati DEM (altezza) di tutto il mondo una volta. Il tutto era molto più tollerabile una volta che mi sono allontanato dalla sceneggiatura interpretata.

Mi aspetto che qualcosa del genere sia più veloce

arrayName[0] = unpack('>'+'f'*line_count*sample_count, map.read(arrayName.itemsize*line_count*sample_count))

Non utilizzare map come nome variabile

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top