Question

On a récemment demandé au comment créer un fichier slurp en python , et la réponse acceptée suggère quelque chose comme:

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

Comment ferais-je pour lire le fichier et convertir la représentation endian des données?

Par exemple, j'ai un fichier binaire de 1 Go qui est juste un groupe de flotteurs simple précision emballés comme un gros endian et je veux le convertir en petit endian et le dump dans un tableau numpy. Vous trouverez ci-dessous la fonction que j'ai écrite pour accomplir ceci et un code réel qui l'appelle. J'utilise struct.unpack et effectue la conversion finale en essayant de tout accélérer en utilisant mmap .

Ma question est la suivante: est-ce que j'utilise correctement le slurp avec mmap et struct.unpack ? Y a-t-il un moyen plus propre et plus rapide de le faire? En ce moment, ce que j’ai fonctionne, mais j’aimerais vraiment apprendre à mieux faire cela.

Merci d'avance!

#!/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)
Était-ce utile?

La solution

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

Assez difficile à battre pour la vitesse ET la concision ;-). Pour byteswap, voir ici (l'argument True signifie "faites-le à la place"); Pour fromfile voir ici .

Ceci fonctionne tel quel sur les machines little-endian (puisque les données sont big-endian, le byteswap est nécessaire). Vous pouvez tester si c'est le cas pour effectuer byteswap de manière conditionnelle, remplacez la dernière ligne d'un appel inconditionnel par byteswap par, par exemple:

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

C'est-à-dire, un appel à byteswap subordonné à un test de petite finalité.

Autres conseils

La réponse de @Alex Martelli :

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

Vous pouvez créer une solution ASM à l'aide de CorePy . Je me demande cependant si vous pourriez être en mesure d'obtenir des performances suffisantes d'une autre partie de votre algorithme. Les entrées / sorties et les manipulations sur des blocs de données de 1 Go vont prendre un certain temps, peu importe la façon dont vous les découpez.

Une autre chose qui pourrait vous être utile serait de passer en C une fois que vous avez prototypé l’algorithme en python. Je l'ai fait pour manipuler une fois un ensemble de données DEM (hauteur) du monde entier. Le tout était beaucoup plus supportable une fois que je me suis éloigné du script interprété.

Je m'attendrais à ce que quelque chose comme celui-ci soit plus rapide

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

Veuillez ne pas utiliser map comme nom de variable

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top