2017-09-27 164 views
0

我有一個文本文件,其中相關數據(行x列)僅出現在「開始」和「結束」關鍵字之間。見下文。我想編寫一個可以提取這些數據子集的代碼。如果一行以「開始」開始,然後是數據,但沒有後續的「結束」關鍵字,那麼我想忽略這些數據。在我的例子中,data1和data3是相關的,但data2並不是因爲它沒有被「開始」和「結束」關鍵字所包圍。如何從Python中的文本數據文件中提取數據子集

start 
data1 (matrix of data) /relevant because data1 is enclosed by "start" and "end" 
end 
start 
data2 (matrix of data) /not relevant because there is no "end" 
. 
start 
data3 (matrix of data) /relevant for same reason as for data1 
end 
. 
. 
and so on 

我以爲我可以這樣開始:

with open(file_path,'r') as file: 

    text = file.readlines() 
    start_indexes = [] 
    end_indexes = [] 

    for i, line in enumerate(text): 
     if line.startswith('start'): 
      start_indexes.append(i) 
     elif line.startswith('end'): 
      end_indexes.append(i) 

    for i in range(len(start_indexes)): 
     for j in range(len(end_indexes)): 
      if (start_indexes[i] < end_indexes[j] < start_indexes[i+1]): 
       print(start_indexes[i],end_indexes[j]) 

上面的代碼給了我兩個起始行號和結束行號在有相關數據。這是我有點卡住的地方。我現在如何提取相關數據?在我下面的例子中,它將是data1,data3。我是否以「正確」的方式處理問題?我應該使用熊貓嗎?有沒有更高效和直接的方法?

+0

行格式始終爲ether:'start;數據;開始「或」開始「;數據; end'?請發佈您的示例輸入的預期輸出。 – wwii

回答

0

嵌套循環?

您正在經歷開始和結束範圍的每種組合。你只需要那些對應於相同數據的那些數據。

像這樣的東西替換你的for循環:

for start, end in zip(start_indexes, end_indexes): 
    print(text[start + 1:end]) 

zip(a, b, ...)返回與a, b, ...列,根本上給[(a[0], b[0], ...), (a[1], b[1], ...), ...]一個新的列表。通過start_indexes, end_indexes的每一列,給出相應的開始和結束值,然後使用列表片段訪問來獲取這些行的數據。

+0

謝謝。我需要更好地理解zip,但我確實想提一下,start_indexes和end_indexes是具有不同長度的列表。一大塊數據不總是被「開始」和「結束」包圍。只有在測試成功的情況下,我們纔會有「開始」和「結束」所包含的數據。如果測試不成功,在數據文件中,我們會得到「開始」,然後是一些不完整的數據,然後是NOT,然後是「結束」,但是通過另一個「開始」,第二次測試嘗試開始。如果第二次嘗試成功,我們會在最後得到「結束」。 – Murchak

+0

@Murchak請注意,'zip'總是返回一個矩形矩陣。 – HyperNeutrino

0

我會通過只讀順序讀取文件來使用另一種方式(假設「開始」 - 「結束」 - 數據塊中的數據不會太大)。我將創建一個data變量來收集當前塊的數據(無論是否相關)以及具有狀態轉換的變量。

一些僞的Python:

if state == OUTSIDE_BLOCK and line.startswith('start'): 
    state = INSIDE_BLOCK 
elif state == INSIDE_BLOCK and <line contains data>: 
    <Add to data variable> 
elif state == INSIDE_BLOCK and line.startswith('end'): 
    state = OUTSIDE_BLOCK 
    <Process collected data> 
elif state == INSIDE_BLOCK and line.startswith('start'): 
    <Throw away possibly collected data because it was irrelevant> 
0

我個人覺得正則表達式是處理這種情況的最好辦法:

import re woof0='''start data1 (matrix of data) /relevant because data1 is enclosed by "start" and "end" end start data2 (matrix of data) /not relevant because there is no "end" . start data3 (matrix of data) /relevant for same reason as for data1 end . . and so on ''' re.findall(r'start(\sdata.*|\Sdata.*)\nend',woof0)

輸出:

['\ndata1 (matrix of data) /relevant because data1 is enclosed by "start" and "end"', '\ndata3 (matrix of data) /relevant for same reason as for data1']

+0

OP指定'start'後跟'data'後面跟'end'不是被捕獲的文本*。 – wwii

+0

謝謝Gaurav。我想你可能誤解了數據的結構。數據部分中沒有單詞數據。每行(行)是一個觀察值,每行包含許多列(屬性)。我不確定Regex如何提供幫助。 – Murchak

+0

明白了!數據是否以一致的形式出現,即csv或空格分隔? –

0

設置:

s = '''start 
data1 (matrix of data) /relevant because data1 is enclosed by "start" and "end" 
end 
start 
data2 (matrix of data) /not relevant because there is no "end" 
start 
data3 (matrix of data) /relevant for same reason as for data1 
end 
start 
data4 blah 
''' 
import io 
f = io.StringIO(s) 

對文件進行迭代一次,測試每行的開頭;找出將有效數據塊放入子列表並將它們附加到結果列表中所需的邏輯...

result = [] 
sub = [] 

for line in f: 
    if line.startswith('start'): 
     # possible new data block 
     if sub: 
      # if it isn't empty it must contain 
      # a start --> data block with no end 
      result.append(sub) 
      sub = [] 
     sub = [line] 
    elif line.startswith('end'): 
     # start over 
     sub = [] 
    elif line.startswith('data'): 
     sub.append(line) 
    else: 
     # for lines that don't startwith data, start or end - if any 
     sub.append(line) 

if sub: 
    # start --> data --> EOF or end of string 
    result.append(sub)