2009-10-27 13 views
5

został niedawno poproszony how to do a file slurp in python i zaakceptowane odpowiedź zasugerował coś takiego:Python Slurp pliku w/endian konwersji

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

Jak bym to zabrać, aby odczytać plik i konwertować endian reprezentację dane?

Na przykład mam plik binarny o pojemności 1 GB, który jest po prostu spławem pojedynczej precyzji spakowanym jako duży endian i chcę go przekonwertować na mały endian i zrzut do tablicy numpy. Poniżej znajduje się funkcja, którą napisałem, aby to osiągnąć i jakiś prawdziwy kod, który ją wywołuje. Używam konwersji z końcówką i próbuję przyspieszyć wszystko, używając mmap.

Moje pytanie brzmi, czy używam slurp poprawnie z mmap i struct.unpack? Czy istnieje czyściejszy i szybszy sposób na zrobienie tego? W tej chwili, co mam działa, ale naprawdę chciałbym nauczyć się, jak to zrobić lepiej.

Z góry dziękuję!

#!/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) 
+0

Chciałem dodać do tego, dla każdego, kto znajdzie ten post użyteczne. Uruchomienie oryginalnego kodu zajęło mi około 80 sekund. Uruchomienie rozwiązania zapewnionego przez Alexa Martelliego i JF Sebastiana trwa mniej niż sekundę. Program wywołujący tę funkcję robi to wiele razy. W związku z tym czas działania znacznie się zmniejszył. Dziękuję wam zarówno za pomoc, jak i za nauczenie mnie czegoś =) – Foofy

Odpowiedz

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

Dość trudne do pokonania dla szybkości i zwięzłość ;-). Dla bajtów patrz here (argument True oznacza "zrób to na miejscu"); for fromfile zobacz here.

Działa tak, jak na maszynach little-endian (ponieważ dane są wielko-endianowe, wymagana jest bajt). Można sprawdzić, czy tak jest w istocie zrobić byteswap warunkowo zmienić ostatni wiersz z bezwarunkowego wezwania do byteswap do, na przykład:

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

czyli wezwanie do byteswap uwarunkowane teście little-endianness .

+0

, która jest zadziwiająco prosta.dziękuję co dziwne, widziałem je, próbując dowiedzieć się, jak to zrobić, ale po prostu się nie zarejestrowało z jakiegoś powodu. pochodzi z doświadczeniem Przypuszczam, że =) – Foofy

+2

numpy.float32 ma natywną kolejność bajtów, która może nie zawsze być big-endian. http://stackoverflow.com/questions/1632673/python-file-slurp-w-endian-conversion/1633525#1633525 – jfs

+0

Rzeczywiście będzie to w większości mało-endian, ale jeśli używasz np. na komputerze Power PC będzie to big endian (jeśli jest to problem, po prostu warunkowo pomiń wywołanie bajtów - pozwól mi edytować odpowiedź, aby dodać ten bit). –

0

Można Coble razem ASM based solution użyciu CorePy. Zastanawiam się jednak, czy możesz uzyskać wystarczającą wydajność z innej części swojego algorytmu. Operacje we/wy i manipulacje na porcjach danych o wielkości 1 GB zajmą trochę czasu, ale zawsze można je podzielić.

Jeszcze jedna rzecz, która może okazać się pomocna, to przełączyć się na C po prototypowaniu algorytmu w pythonie. Zrobiłem to dla manipulacji na całym świecie zestaw danych DEM (wysokość) jeden raz. Po odejściu od scenariusza zinterpretowanego wszystko było znacznie bardziej znośne.

0

będę oczekiwać coś takiego się szybciej

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

Proszę nie używać map jako nazwy zmiennej

7

nieco zmodyfikowany @Alex Martelli's answer:

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

Prawdopodobnie chcesz połączyć to z .astype, aby pobrać go w macierzystym formacie, np. 'arr = numpy.fromfile (nazwa pliku, numpy.dtype ('> f4')). astype (np.float32)' – RolKau

+0

@RolKau no. Spróbuj uruchomić kod z i bez połączenia i zobacz, co się stanie. – jfs

+0

@JFSebastian Może ekstrapolowałem z tego zbyt daleko od faktycznego przypadku w pytaniu, ale rozważ kod Pythona: 'b = bytearray ([0, 0, 0, 1]); a = numpy.frombuffer (b, dtype = numpy.dtype ('> i4')); c = a.astype (numpy.int32); (a.tostring(), c.tostring()) '. Na mojej platformie (Linux, Python 2.7, x86_64) otrzymuję wyniki a = '\ x00 \ x00 \ x00 \ x01', c =' \ x01 \ x00 \ x00 \ x00', co tłumaczę, że tylko c było przechowywane wewnętrznie jako mały-endian. – RolKau

Powiązane problemy