อ่านจำนวนบรรทัดที่แน่นอนจากไฟล์ txt และแปลงเป็นรายการด้วยวิธี pythonic

สมมติว่าฉันมีไฟล์ 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 ]

นั่นเป็นเพียง Pythonic ที่มากกว่าเล็กน้อย แต่มันจะไม่หยุดอ่านหลังจากถึงจำนวนบรรทัดที่ต้องการ แต่มันเพียงอ่านทั้งหมด คุณสามารถใช้ 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 (Finite State Machine) ที่เข้ารหัสอย่างง่ายเพื่อควบคุมกระบวนการอ่านในบันทึกข้อมูลที่สมบูรณ์ มีรายละเอียดมากกว่าคำตอบอื่น ๆ ที่โพสต์ในปัจจุบัน แต่เป็นวิธีที่ค่อนข้างยืดหยุ่นและขยายได้ในการจัดการงานดังกล่าวและดำเนินการด้วยการตรวจสอบข้อผิดพลาด

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 like ได้

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

จากนั้นแปลงประเภทข้อมูลตามที่คุณต้องการ

หรืออีกวิธีหนึ่ง สามารถใช้ readlines ได้:

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