прочитать определенное количество строк из txt файла и преобразовать в список питоническим способом

Предположим, у меня есть следующий текстовый файл:

0.0163934
6
7.52438e+09
2147483648
6.3002e-06 6.31527e-08 0 0 6 0 0 4.68498e-06 0.00638412 12.6688
6.33438e-06 0 5.99588e-09 0 0 0 0 4.70195e-06 0 12.876
6.36874e-06 0 6.09398e-09 0 0 0 0 4.71894e-06 0 13.0867
6.40329e-06 0 6.19369e-09 0 0 0 0 4.73593e-06 0 13.3009
6.43802e-06 0 6.29503e-09 0 0 0 0 4.75294e-06 0 13.5185
6.47295e-06 0 6.39803e-09 0 0 0 0 4.76996e-06 0 13.7397
0.0163934
3
7.52438e+09
2147483648
6.3002e-06 0 5.89935e-09 0 0 0 0 4.68498e-06 0 12.6688
6.33438e-06 0 5.99588e-09 0 0 0 0 4.70195e-06 0 12.876
6.36874e-06 0 6.09398e-09 0 0 0 0 4.71894e-06 0 13.0867

Я хочу прочитать каждую из первых строк как числа с плавающей запятой или целые числа, а затем, в зависимости от второй строки, я хочу прочитать остальные строки как список списков или массив.

На языке IDL мне просто нужно сделать:

openr, 1, fname
readf, 1, Time
readf, 1, Bins
readf, 1, dummy
readf, 1, dummyLong
da1= fltarr(10, Bins)
readf, 1, da1

Таким образом, весь блок чисел хранится в целом числе da1, которое имеет размер: 10 * Bins. (строки и столбцы противоположны, как в питоне)

И затем я могу прочитать следующие строки таким же образом.

В питоне делаю:

Time=float(filen.readline())
Bins=int(filen.readline())
dummy=float(filen.readline())
dummyLong=long(filen.readline())

lines=[filen.readline() for i in range(Bins)]

arra=[[float(x) for x in lines[i].split()] for i in range(len(lines))]

Поэтому мне нужны две строчки кода и сложные итерации, непонятные новичку.

Есть ли способ сделать это, как в IDL, в одном выражении и на pythonic?

Спасибо!


person Santiago    schedule 01.02.2013    source источник
comment
О, и я хочу сделать это удобным для памяти способом, так как у меня на самом деле тысячи строк. Вот почему я не использую file.readlines()   -  person Santiago    schedule 01.02.2013
comment
filen.readlines() не удобен для памяти, а for line in filen — да.   -  person cha0site    schedule 01.02.2013


Ответы (4)


Однострочные не обязательно лучше двухстрочных.

Но ты можешь сделать это:

arra = [[float(x) for x in filen.readline().split()] for _ in range(Bins)]

Мне больше нравится в двух строчках:

lines = (filen.readline() for _ in range(Bins))
arra = [[float(x) for x in line.split()] for line in lines]
person Tim Pietzcker    schedule 01.02.2013
comment
Спасибо, думаю, возьму этот, достаточно компактный. - person Santiago; 02.02.2013

Time=float(fname.readline())
Bins=int(fname.readline())
dummy=float(fname.readline())
dummyLong=long(fname.readline())
arra = [ [ float(num) for num in line.split() ] for line in filen ]

Это просто чуть более питоновский, но он не прекращает чтение после необходимого количества строк, он просто читает их все. Вы можете использовать islice из itertools, чтобы остановить итерацию, или вы можете просто потом обрезать список.

Вот пример, и поскольку я уже использую islice, я позволил себе пофантазировать с функциональным программированием...

from itertools import islice

CONVERTORS = (float, int, float, long, )
with open(...) as filen:
    Time, Bins, dummy, dummyLong = [ func(value) for func, value in zip(CONVERTORS, islice(filen, 4)) ]
    arra = [ map(float, line.split()) for line in islice(filen, Bins) ]
person cha0site    schedule 01.02.2013
comment
Это не останавливает чтение после необходимого количества строк. - person Tim Pietzcker; 01.02.2013
comment
@Tim: Вы правы, я добавил объяснение, как прекратить чтение по мере необходимости. - person cha0site; 01.02.2013
comment
Эти itertools кажутся интересными, я проверю, какие еще приложения у него есть. - person Santiago; 02.02.2013

Вот более объектно-ориентированный способ сделать это, используя простой FSM (конечный автомат) для управления процессом чтения полных записей данных. Он более подробный, чем другие ответы, опубликованные в настоящее время, но это довольно гибкий и расширяемый способ решения таких задач и делает это с проверкой ошибок.

class Record(object):
    def __init__(self, time=None, bins=None, fltarr=None):
        self.time = time
        self.bins = bins
        self.fltarr = fltarr

    def read(self, file):
        """ Read complete record from file into self and return True,
            otherwise return False if EOF encountered """
        START, STOP, EOF = 0, -1, -99

        state = START
        while state not in (EOF, STOP):
            line = file.readline()
            if not line: state = EOF; break
            # process line depending on read state
            if state == 0:
                self.time = float(line)
                state = 1
            elif state == 1:
                self.bins = int(line)
                state = 2
            elif state in (2, 3):
                # ignore line
                state += 1
            elif state == 4:
                self.fltarr = []
                last_bin = self.bins-1
                for bin in xrange(self.bins):
                    self.fltarr.append([float(x) for x in line.split()])
                    if bin == last_bin: break
                    line = file.readline()
                    if not line: state = EOF; break
                if state != EOF:
                    state = STOP

        return state == STOP

    def __str__(self):
        result = 'Record(time={}, bins={}, fltarr=[\n'.format(self.time, self.bins)
        for floats in self.fltarr:
            result += '  {}\n'.format(floats)
        return result + '])'

fname = 'sample_data.txt'
with open(fname, 'r') as input:
    data = []
    while True:
        record = Record()
        if not record.read(input):
            break
        else:
            data.append(record)

for record in data:
    print record

Выход:

Record(time=0.0163934, bins=6, fltarr=[
  [6.3002e-06, 6.31527e-08, 0.0, 0.0, 6.0, 0.0, 0.0, 4.68498e-06, 0.00638412, 12.6688]
  [6.33438e-06, 0.0, 5.99588e-09, 0.0, 0.0, 0.0, 0.0, 4.70195e-06, 0.0, 12.876]
  [6.36874e-06, 0.0, 6.09398e-09, 0.0, 0.0, 0.0, 0.0, 4.71894e-06, 0.0, 13.0867]
  [6.40329e-06, 0.0, 6.19369e-09, 0.0, 0.0, 0.0, 0.0, 4.73593e-06, 0.0, 13.3009]
  [6.43802e-06, 0.0, 6.29503e-09, 0.0, 0.0, 0.0, 0.0, 4.75294e-06, 0.0, 13.5185]
  [6.47295e-06, 0.0, 6.39803e-09, 0.0, 0.0, 0.0, 0.0, 4.76996e-06, 0.0, 13.7397]
])
Record(time=0.0163934, bins=3, fltarr=[
  [6.3002e-06, 0.0, 5.89935e-09, 0.0, 0.0, 0.0, 0.0, 4.68498e-06, 0.0, 12.6688]
  [6.33438e-06, 0.0, 5.99588e-09, 0.0, 0.0, 0.0, 0.0, 4.70195e-06, 0.0, 12.876]
  [6.36874e-06, 0.0, 6.09398e-09, 0.0, 0.0, 0.0, 0.0, 4.71894e-06, 0.0, 13.0867]
])
person martineau    schedule 01.02.2013
comment
Это действительно интересное решение. Действительно, может быть очень полезно для выполнения некоторых проверок и обработки моих данных, я буду иметь это в виду, хотя в этом случае мне нужно, чтобы код был максимально компактным и простым, потому что другие люди тоже должны его читать. - person Santiago; 02.02.2013

Вы также можете использовать numpy loadtxt, например

from numpy import loadtxt
data = loadtxt("input.txt", unpack=False)

затем преобразуйте тип данных по своему усмотрению

В качестве альтернативы можно также использовать строки чтения:

from numpy import fromstring
fin = open("filename.dat")
data = fin.readlines()
Bins = -3
for record range(no_of_records):
    i = record + 3 + Bins
    Time = float(data[i])
    Bins = int(data[i+1])
    dummy, dummylong = (float(data[i+2]),float(data[i+3]))
    Bins = [fromstring(data(i+4+j), dtype=float, sep=" ") for j in range(Bins)]
person Thiru    schedule 04.02.2013