2016-07-29 32 views
2

Ich habe eine input_file.fa Datei wie diese (FASTA-Format):Lesen in der Datei Block für Block unter Verwendung der angegebenen Trennzeichen in Python

> header1 description 
data data 
data 
>header2 description 
more data 
data 
data 

Ich möchte zu einem Zeitpunkt in der Datei ein Brocken lesen, so dass jeder Chunk enthält einen Header und die entsprechenden Daten, z 1 Block:

> header1 description 
data data 
data 

Natürlich habe ich in der Datei wie dies gerade lesen konnte und Split:

with open("1.fa") as f: 
    for block in f.read().split(">"): 
     pass 

Aber Ich möchte das Lesen Sie die gesamte Datei in den Speicher, zu vermeiden, da die Dateien oft groß.

ich in der Datei Zeile für Zeile natürlich lesen kann:

with open("input_file.fa") as f: 
    for line in f: 
     pass 

Aber im Idealfall, was ich will, ist so etwas wie diese:

with open("input_file.fa", newline=">") as f: 
    for block in f: 
     pass 

Aber ich erhalte eine Fehlermeldung:

ValueError: illegal newline value: >

Ich habe auch versucht, die csv module, aber mit keinem Erfolg.

Ich habe this post von 3 Jahren gefunden, die eine Generator-basierte Lösung für dieses Problem bietet, aber es scheint nicht so kompakt, ist das wirklich die einzige/beste Lösung? Es wäre nett, wenn es möglich ist, den Generator mit einer einzigen Zeile zu erstellen, anstatt einer separaten Funktion, so etwas wie dieser Pseudo-Code:

with open("input_file.fa") as f: 
    blocks = magic_generator_split_by_> 
    for block in blocks: 
     pass 

Wenn dies nicht möglich ist, dann denke ich, man könnte meine Frage ein Duplikat betrachten der andere Beitrag, aber wenn das so ist, hoffe ich, dass die Leute mir erklären können, warum die andere Lösung die einzige ist. Danke vielmals.

+1

Haben Sie versucht, http://biopython.org/wiki/Biopython zu verwenden? –

+0

@AshwiniChaudhary Vielen Dank, gute Idee, dass sollte für diesen Fall helfen, aber idealerweise würde ich auch gerne eine generische Lösung, die über biologische Sequenz Datenformate arbeiten würde. –

Antwort

2

Eine allgemeine Lösung wird hier eine Generatorfunktion schreiben, die jeweils eine Gruppe ergibt. In diesem Fall speichern Sie jeweils nur eine Gruppe im Speicher.

def get_groups(seq, group_by): 
    data = [] 
    for line in seq: 
     # Here the `startswith()` logic can be replaced with other 
     # condition(s) depending on the requirement. 
     if line.startswith(group_by): 
      if data: 
       yield data 
       data = [] 
     data.append(line) 

    if data: 
     yield data 

with open('input.txt') as f: 
    for i, group in enumerate(get_groups(f, ">"), start=1): 
     print ("Group #{}".format(i)) 
     print ("".join(group)) 

Ausgang:

Group #1 
> header1 description 
data data 
data 

Group #2 
>header2 description 
more data 
data 
data 

Für FASTA Formate im Allgemeinen würde ich empfehlen, Biopython Paket.

+0

Obwohl dies immer noch nicht meine Idee ist, denke ich, dass es eine gute praktische Lösung für das Problem ist (besser als die in der anderen Post), also werde ich als gelöst markieren, danke für deine Hilfe . –

+0

Sie können 'data = [line]' mit 'data = []' ändern und 'data.append (line)' außerhalb des äußeren 'if' verschieben, wobei Sie die' else' entfernen und den doppelten Aufruf vermeiden. – Bakuriu

0
def read_blocks(file): 
    block = '' 
    for line in file: 
     if line.startswith('>') and len(block)>0: 
      yield block 
      block = '' 
     block += line 
    yield block 


with open('input_file.fa') as f: 
    for block in read_blocks(f): 
     print(block) 

Dies wird Zeile für Zeile in die Datei eingelesen und Sie erhalten die Blöcke mit der Yield-Anweisung zurück. Dies ist faul, so dass Sie sich keine Gedanken über einen großen Speicherbedarf machen müssen.

1

Ein Ansatz, Ich mag ist itertools.groupby zusammen mit einem einfachen key fuction zu verwenden:

from itertools import groupby 


def make_grouper(): 
    counter = 0 
    def key(line): 
     nonlocal counter 
     if line.startswith('>'): 
      counter += 1 
     return counter 
    return key 

Verwenden Sie es als:

with open('filename') as f: 
    for k, group in groupby(f, key=make_grouper()): 
     fasta_section = ''.join(group) # or list(group) 

Sie benötigen die join nur, wenn Sie die Inhalte zu behandeln haben eines ganzen Abschnitts als einzelne Saite. Wenn Sie nur daran interessiert sind, die Zeilen eins nach dem anderen zu lesen, können Sie einfach folgendes tun:

with open('filename') as f: 
    for k, group in groupby(f, key=make_grouper()): 
     # parse >header description 
     header, description = next(group)[1:].split(maxsplit=1) 
     for line in group: 
      # handle the contents of the section line by line 
+0

Vielen Dank für Ihre nachdenkliche Antwort. Ich muss genauer hinschauen, um den Code vollständig zu verstehen. Glauben Sie, dass dieser Ansatz gegenüber den anderen einen besonderen Vorteil hat? –

+0

@Chris_Rands Dieser Ansatz entspricht grundsätzlich der Antwort von Ashwini, verwendet jedoch 'itertools', um eine manuelle Gruppierung der Linien zu vermeiden. Es ist Geschmackssache. – Bakuriu