Arquivo Python Slurp w / conversão endian
-
06-07-2019 - |
Pergunta
Foi recentemente pediu como fazer um gole arquivo em python , e a resposta aceita sugeriu algo como:
with open('x.txt') as x: f = x.read()
Como eu iria fazer isso para ler o arquivo e converter a representação endian dos dados?
Por exemplo, eu tenho um arquivo binário 1GB que é apenas um monte de precisão simples flutua embalado como um grande endian e eu quero convertê-lo em pouco endian e despejo em uma matriz numpy. Abaixo é a função que eu escrevi para alcançar este objetivo e algum código real que o chama. Eu uso struct.unpack
fazer a conversão endian e tentou velocidade tudo para cima usando mmap
.
A minha pergunta então é, estou usando o gole corretamente com mmap
e struct.unpack
? Existe um produto de limpeza, a maneira mais rápida de fazer isso? Agora o que eu tenho as obras, mas eu realmente gostaria de saber como fazer isso melhor.
Agradecemos antecipadamente!
#!/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)
Solução
with open(fileName, "rb") as f:
arrayName = numpy.fromfile(f, numpy.float32)
arrayName.byteswap(True)
Pretty difícil de bater para a velocidade e concisão ;-). Para byteswap ver aqui (os meios argumento True
, "fazê-lo no lugar"); para FromFile ver aqui .
Isso funciona como é em pequenas máquinas-endian (desde que os dados são big-endian, o byteswap é necessária). Você pode testar se esse for o caso, para fazer o byteswap condicionalmente, altere a última linha de uma chamada incondicional de byteswap em, por exemplo:
if struct.pack('=f', 2.3) == struct.pack('<f', 2.3):
arrayName.byteswap(True)
ou seja., Uma chamada para byteswap condicional em um teste de pouca-ordenação.
Outras dicas
Um pouco modificado de @Alex Martelli resposta :
arr = numpy.fromfile(filename, numpy.dtype('>f4'))
# no byteswap is needed regardless of endianess of the machine
Você poderia Coble juntos uma solução baseada ASM usando CorePy . Pergunto-me, porém, se você pode ser capaz de obter um desempenho suficiente da outra parte de seu algoritmo. I / O e manipulações em pedaços de 1GB de dados vai demorar um pouco que sempre maneira você cortá-lo.
Uma outra coisa que você pode achar útil seria a mudança para C depois de ter um protótipo do algoritmo em python. Eu fiz isso para manipulações em um todo-mundo DEM (altura) conjunto de dados uma vez. A coisa toda foi muito mais tolerável quando cheguei longe do roteiro interpretado.
Eu esperaria algo assim para ser mais rápido
arrayName[0] = unpack('>'+'f'*line_count*sample_count, map.read(arrayName.itemsize*line_count*sample_count))
Por favor, não use map
como um nome de variável