Python File Slurp con conversione endian
-
06-07-2019 - |
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)
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