baca sejumlah baris tertentu dari file txt dan konversikan ke daftar dengan cara pythonic

Misalkan saya memiliki file txt berikut:

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

Saya ingin membaca setiap baris pertama sebagai float atau bilangan bulat dan kemudian bergantung pada baris kedua saya ingin membaca baris lainnya sebagai daftar daftar atau array.

Dalam bahasa IDL saya hanya perlu melakukan:

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

Sehingga seluruh blok angka disimpan dalam bilangan bulat da1 yang berukuran: 10*Bins. (baris dan kolom adalah kebalikan dari python)

Dan kemudian saya bisa membaca baris berikut dengan cara yang sama.

Dengan python yang saya lakukan:

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))]

Jadi saya memerlukan dua baris kode dan iterasi rumit yang tidak dapat dimengerti oleh pemula.

Apakah ada cara untuk melakukannya seperti di IDL, dalam satu pernyataan dan pythonic?

Terima kasih!


person Santiago    schedule 01.02.2013    source sumber
comment
Oh dan saya ingin melakukannya dengan cara yang ramah memori, karena saya sebenarnya punya ribuan baris. Itu sebabnya saya tidak menggunakan file.readlines()   -  person Santiago    schedule 01.02.2013
comment
filen.readlines() tidak ramah memori, tetapi for line in filen ramah.   -  person cha0site    schedule 01.02.2013


Jawaban (4)


Satu baris belum tentu lebih baik dari dua baris.

Tapi Anda bisa melakukannya:

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

Saya lebih menyukainya dalam dua baris:

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
Terima kasih, saya rasa saya akan mengadopsi yang ini, cukup kompak. - 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 ]

Itu hanya sedikit lebih Pythonic, tetapi tidak berhenti membaca setelah jumlah baris yang diperlukan, ia hanya membaca semuanya. Anda dapat menggunakan islice dari itertools untuk menghentikan iterasi, atau Anda dapat memotong daftar setelahnya.

Ini contohnya, dan karena saya sudah menggunakan islice, saya mengambil kebebasan untuk menyukai pemrograman fungsional...

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
Ini tidak berhenti membaca setelah jumlah baris yang diperlukan. - person Tim Pietzcker; 01.02.2013
comment
@Tim: Anda benar, saya menambahkan penjelasan tentang cara berhenti membaca sesuai kebutuhan. - person cha0site; 01.02.2013
comment
Itertools tersebut sepertinya menarik, saya akan memeriksa aplikasi lain yang dimilikinya. - person Santiago; 02.02.2013

Berikut cara yang lebih berorientasi objek untuk melakukannya menggunakan FSM (Finite State Machine) berkode sederhana untuk mengontrol proses pembacaan catatan data lengkap. Ini lebih bertele-tele dari jawaban lain yang diposting saat ini, tetapi ini merupakan cara yang cukup fleksibel dan dapat diperluas untuk menangani tugas-tugas tersebut dan melakukannya dengan pemeriksaan kesalahan.

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

Keluaran:

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
Ini adalah solusi yang sangat menarik. Memang bisa sangat berguna untuk melakukan beberapa pemeriksaan dan menangani data saya, saya akan mengingatnya, meskipun dalam hal ini saya memerlukan kode yang sekompak dan sesederhana mungkin, karena orang lain juga harus membacanya. - person Santiago; 02.02.2013

Anda juga dapat menggunakan numpy loadtxt seperti

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

lalu ubah tipe datanya sesuai keinginan

Sebagai alternatif, readline juga dapat digunakan:

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