2009-10-27 6 views
5

Il a récemment demandé how to do a file slurp in python, et la réponse acceptée a suggéré quelque chose comme:Python fichier Slurp w/conversion endian

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

Comment pourrais-je faire de manière à lire le fichier et convertir la représentation endian du Les données? Par exemple, j'ai un fichier binaire de 1 Go qui est juste un tas de flottants de précision simple emballés comme un gros boutiste et je veux le convertir en little endian et dump dans un tableau numpy. Voici la fonction que j'ai écrite pour accomplir ceci et un vrai code qui l'appelle. J'utilise struct.unpack faire la conversion endian et essayé d'accélérer tout en utilisant mmap. Puis-je utiliser le slurp correctement avec mmap et struct.unpack? Y a-t-il un moyen plus propre et plus rapide de faire cela? En ce moment j'ai ce que j'ai, mais j'aimerais vraiment apprendre comment faire mieux.

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) 
+0

Je voulais ajouter à cela, pour quelqu'un d'autre qui trouve ce poste utile. Exécution du code d'origine que j'avais avait environ 80 secondes. L'exécution de la solution fournie par Alex Martelli et J F Sebastian est inférieure à une seconde. Le programme qui appelle cette fonction le fait plusieurs fois. En tant que tel, le temps de fonctionnement a considérablement diminué. Merci à vous deux pour l'aide et pour m'apprendre quelque chose =) – Foofy

Répondre

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

assez difficile à battre pour la vitesse et ;-) concision. Pour byteswap, voir here (l'argument True signifie «faites-le en place»); pour fromfile voir here.

Ceci fonctionne comme sur les machines little-endian (puisque les données sont big-endian, le byteswap est nécessaire). Vous pouvez vérifier si tel est le cas pour faire le byteswap sous condition, changer la dernière ligne d'un appel inconditionnel à byteswap dans, par exemple:

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

-à-dire, un appel à byteswap conditionnel à un test de peu boutisme .

+0

qui est remarquablement simple.merci ce qui est bizarre est que j'avais vu ceux-ci en essayant de comprendre comment faire cela, mais il n'a tout simplement pas enregistré pour une raison quelconque. vient avec l'expérience je suppose =) – Foofy

+2

numpy.float32 a l'ordre d'octet natif qui pourrait ne pas être toujours big-endian. http://stackoverflow.com/questions/1632673/python-file-slurp-w-endian-conversion/1633525#1633525 – jfs

+0

En effet, ce sera principalement little-endian, mais si vous utilisez par ex. sur une machine Power PC, ce sera un big endian (si c'est un problème, omettez l'appel byteswap de façon conditionnelle - laissez-moi éditer la réponse pour ajouter ce bit). –

0

Vous pouvez Coble ensemble un ASM based solution en utilisant CorePy. Je me demande cependant, si vous pourriez être en mesure de gagner suffisamment de performance de l'autre partie de votre algorithme. Les E/S et les manipulations sur des blocs de données de 1 Go vont prendre du temps, quelle que soit la façon dont vous les découpez.

Une autre chose que vous pourriez trouver utile serait de passer en C une fois que vous avez prototypé l'algorithme en python. Je l'ai fait pour des manipulations sur un ensemble de données DEM (hauteur) du monde entier une fois. Le tout était beaucoup plus tolérable une fois que je me suis éloigné du script interprété.

0

Je me attends quelque chose comme ça pour être plus rapide

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

S'il vous plaît ne pas utiliser map comme nom de variable

7

légèrement modifié @Alex Martelli's answer:

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

Vous voudrez probablement combiner cela avec .astype pour l'obtenir dans un format natif, par exemple. 'arr = numpy.fromfile (nom de fichier, numpy.dtype ('> f4')). astype (np.float32)' – RolKau

+0

@RolKau pas. Essayez d'exécuter du code avec et sans l'appel et voyez ce qui se passe. – jfs

+0

@JFSebastian Peut-être que je l'ai extrapolé à partir de ce qui est le cas dans la question, mais considérons le code Python: 'b = bytearray ([0, 0, 0, 1]); a = numpy.frombuffer (b, dtype = numpy.dtype ('> i4')); c = a.astype (numpy.int32); (a.tostring(), c.tostring()) '. Sur ma plate-forme (Linux, Python 2.7, x86_64) j'obtiens les résultats a = '\ x00 \ x00 \ x00 \ x01', c =' \ x01 \ x00 \ x00 \ x00', que j'interprète que seul c était stocké en interne comme petit-boutiste. – RolKau