2009-10-27 2 views
5

Он был недавно спросил how to do a file slurp in python, и принятый ответ предложил что-то вроде:Python файла Slurp ж/Endian преобразования

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

Как бы я идти об этом, чтобы прочитать файл и преобразовать Endian представление данные?

Например, у меня есть двоичный файл 1 ГБ, который представляет собой всего лишь кучу одиночных прецизионных поплавков, упакованных как большой конечный элемент, и я хочу преобразовать его в маленький endian и dump в массив numpy. Ниже приведена функция, которую я написал для выполнения этого и некоторого реального кода, который его вызывает. Я использую struct.unpack сделать преобразование в конец и попытался ускорить все, используя mmap.

Мой вопрос тогда, я использую slurp правильно с mmap и struct.unpack? Есть ли более чистый и быстрый способ сделать это? Сейчас у меня есть работы, но я бы очень хотел узнать, как это сделать лучше.

Заранее благодарен!

#!/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

Я хотел бы добавить к этому, для кого-то, кто считает этот пост полезным. Запуск исходного кода у меня заняло около 80 секунд или около того. Запуск решения, предоставленного Алексом Мартелли и Дж. Ф. Себастьяном, составляет менее секунды. Программа, вызывающая эту функцию, выполняет много раз. Таким образом, время работы значительно сократилось. Спасибо вам за помощь и за обучение чему-то =) – Foofy

ответ

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

Довольно трудно превзойти по скорости и лаконичности ;-). Для byteswap см. here (аргумент True означает «сделать это на месте»); for fromfile см. here.

Это работает так же, как и на машинах с маленькими концами (поскольку данные являются большими, байты нужны). Вы можете проверить, если это так, чтобы сделать byteswap условно, измените последнюю строку из безусловного вызова byteswap в, например:

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

т.е. вызов byteswap обусловливающим испытание мало-байт ,

+0

это замечательно просто.спасибо Что странно, я видел это при попытке выяснить, как это сделать, но он почему-то не зарегистрировался. приходит с опытом, я полагаю =) – Foofy

+2

numpy.float32 имеет собственный порядок байтов, который может быть не всегда большим. http://stackoverflow.com/questions/1632673/python-file-slurp-w-endian-conversion/1633525#1633525 – jfs

+0

Действительно, в основном это будет little-endian, но если вы работаете, например. на компьютере с Power PC это будет большой endian (если это проблема, просто условно опускайте вызов byteswap - позвольте мне отредактировать ответ, чтобы добавить этот бит). –

0

Вы можете Coble вместе представляют ASM based solution используя CorePy. Интересно, однако, если вы могли бы получить достаточную производительность из какой-либо другой части вашего алгоритма. Ввод/вывод и манипуляции с 1 ГБ фрагментами данных потребуют времени, которое когда-либо вы срезаете.

Еще одна полезная вещь, которая может оказаться полезной, - переключиться на C, как только вы прототипировали алгоритм в python. Я сделал это для манипуляций с данными DEM (высота) всего мира один раз. Все это было гораздо более сносно, когда я ушел от интерпретируемого сценария.

0

Я бы ожидать что-то вроде этого, чтобы быть быстрее

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

Пожалуйста, не используйте map в качестве имени переменной

7

Слегка модифицированный @Alex Martelli's answer:

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

Вы, вероятно, хотели бы объединить это с .astype, чтобы получить его в собственный формат, например. 'arr = numpy.fromfile (filename, numpy.dtype ('> f4')). astype (np.float32)' – RolKau

+0

@RolKau no. Попробуйте запустить код с вызовом и без него и посмотреть, что произойдет. – jfs

+0

@JFSebastian Возможно, я экстраполировал это слишком далеко от того, что является реальным случаем в вопросе, но рассмотрим код Python: 'b = bytearray ([0, 0, 0, 1]); a = numpy.frombuffer (b, dtype = numpy.dtype ('> i4')); c = a.astype (numpy.int32); (a.tostring(), c.tostring()) '. На моей платформе (Linux, Python 2.7, x86_64) я получаю результаты a = '\ x00 \ x00 \ x00 \ x01', c =' \ x01 \ x00 \ x00 \ x00', которые я интерпретирую, что только c хранится внутри как мало-endian. – RolKau

Смежные вопросы