Как читать сразу несколько строк? (сценарий практики)

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

Цель программы: взять очень большой .fasta файл с несколькими последовательностями и вернуть обратное дополнение каждой последовательности в новом файле.

У меня есть сценарий, который читает его по одной строке за раз, но каждая строка занимает всего около 50 байт в обычном файле .fasta. Таким образом, в буферизации только одной строки за раз нет необходимости. Есть ли способ установить количество строк для буферизации за раз?

Для людей, которые не знают, что такое fasta: .fasta файл - это текстовый файл с последовательностями ДНК, каждая из которых имеет строку заголовка перед последовательностью ДНК / РНК / белка, отмеченной знаком «› ». Пример:

>Sequence_1
ATG
TATA
>Sequence_2
TATA
GACT
ATG

Обзор моего кода:

Прочтите первый байт каждой строки, чтобы сопоставить расположение последовательностей в файле, найдя символы «› ».

Используйте эту карту, чтобы читать каждую последовательность в обратном порядке построчно (это то, что я хочу изменить, чтобы иметь возможность)

  • Обеспечить регресс
  • дополнять пары оснований в строке (I.E. G- ›C)
  • Запишите эту строку в новый файл

Вот каким должен быть весь соответствующий код, чтобы вы могли видеть функции, которые вызывают строки, которые я пытаюсь изменить: (возможно, можно пропустить)

def get_fasta_seqs(BIGFILE): #Returns returns an object containing all of seq locations
  f = open(BIGFILE)
  cnt = 0
  seq_name = []
  seq_start = []
  seq_end = []
  seqcount = 0
  #print(line)
  #for loop skips first line check for seq name
  if f.readline(1) == '>':
    seq_name.append(cnt)
    seq_start.append(cnt+1)
    seqcount =+ 1
  for line in f:
    cnt += 1
    if f.readline(1) == '>':
      seq_name.append(cnt)
      seq_start.append(cnt+1)
      seqcount += 1
      if seqcount > 1:
        seq_end.append(cnt-1)
  seq_end.append(cnt-1) #add location of final line

  seqs = fileseq(seq_name,seq_start,seq_end,seqcount) #This class only has a __init__ function for these lists
  return seqs

def fasta_rev_compliment(fasta_read,fasta_write = "default",NTtype = "DNA"):
  if fasta_write == 'default':
    fasta_write = fasta_read[:-6] + "_RC.fasta"
  seq_map = get_fasta_seqs(fasta_read)
  print(seq_map.seq_name)
  f = open(fasta_write,'a')
  for i in range(seq_map.seqcount): #THIS IS WHAT I WANT TO CHANGE
    line = getline(fasta_read,seq_map.seq_name[i]+1) #getline is reading it as 1 indexed?
    f.write(line)
    my_fasta_seqs = get_fasta_seqs(fasta_read)
    for seqline in reversed(range(seq_map.seq_start[i],seq_map.seq_end[i]+1)):
      seq = getline(fasta_read,seqline+1)
      seq = seq.replace('\n','')
      seq = reverse_compliment(seq,NTtype = NTtype) #this function just returns the reverse compliment for that line.
      seq = seq + '\n'
      f.write(seq)
  f.close()

fasta_rev_compliment('BIGFILE.fasta')

Основной фрагмент кода, который я хочу изменить, находится здесь:

for i in range(seq_map.seqcount): #THIS IS WHAT I WANT TO CHANGE
  line = getline(fasta_read,seq_map.seq_name[i]+1) #getline is reading it as 1 indexed?
  f.write(line)
  my_fasta_seqs = get_fasta_seqs(fasta_read)
  for seqline in reversed(range(seq_map.seq_start[i],seq_map.seq_end[i]+1)):
    seq = getline(fasta_read,seqline+1)

Я хочу что-то вроде этого:

def fasta_rev_compliment(fasta_read,fasta_write = "default",NTtype = "DNA",lines_to_record_before_flushing = 5):
  ###MORE CODE###
  #i want something like this

  for i in range(seq_map.seqcount): #THIS IS WHAT I WANT TO CHANGE
    #is their a way to load
    line = getline(fasta_read,seq_map.seq_name[i]+1) #getline is reading it as 1 indexed?
    f.write(line)
    my_fasta_seqs = get_fasta_seqs(fasta_read)
    for seqline in reversed(range(seq_map.seq_start[i],seq_map.seq_end[i]+1)):
      seq = getline(fasta_read,seqline+1)
    #Repeat n = 5 (or other specified number) times until flushing ram.

Проблема, с которой я столкнулся, заключается в том, что мне нужно прочитать файл в обратном порядке. Все методы, которые я могу найти, не работают, когда вы пытаетесь применить их для чтения файла в обратном направлении. Есть ли что-то, что может читать файл по частям, но в обратном порядке?

Или: что-нибудь еще, что может сделать это более эффективным при настройке с низким объемом памяти. Сейчас он почти не использует память, но занимает 21 секунду для файла размером 100 КБ, содержащего около 12 000 строк, но обрабатывает файл мгновенно, используя метод file.readlines().


person David William Turnell    schedule 10.07.2020    source источник
comment
пожалуйста, покажите желаемый результат.   -  person acushner    schedule 10.07.2020
comment
В ответ на этот вопрос было бы полезно, если бы вы могли предоставить несколько больший раздел входного файла. Что такое установка «с низким объемом памяти», сколько памяти доступно? А насколько велик «большой» файл? Неужели это абсолютно невозможно прочитать сразу в память?   -  person Ronald    schedule 10.07.2020
comment
Возможно, это не полное решение, которое вы ищете, но оно может направить вас на правильный путь: Как читать файл в обратном порядке. Вы хотите посмотреть на второй ответ, который сначала не считывает весь файл в память.   -  person Ronald    schedule 10.07.2020
comment
@ Рональд Это зависит от файла. Многие из них имеют размер более 20 ГБ. для маленьких у меня есть другой скрипт, который отлично работает   -  person David William Turnell    schedule 11.07.2020


Ответы (1)


Вот пример получения обратного дополнения к файлу fasta. Возможно, вам удастся воспользоваться некоторыми идеями из этого.

import re

file = """\
>Sequence_1  
ATG
TATA
>Sequence_2 
TATA
GACT
ATG""".splitlines()

s = ''
for line in file:
    line = line.rstrip()
    if line.startswith('>'):
        if len(s):
            # complement the sequence of fasta 'TAGC' to 'ATCG'
            # T to A, A to T, G to C, C to G
            s = s.translate(str.maketrans('TAGC', 'ATCG'))
            # reverse the string, 's[::-1]'
            # Also, print up to 50 fasta per line to the end of the sequence
            s = re.sub(r'(.{1,50})', r'\1\n', s[::-1])
            print(s, end='')
            s = ''
        print(line)
    else:
        s += line

# print last sequence
s = s.translate(str.maketrans('TAGC', 'ATCG'))
s = re.sub(r'(.{1,50})', r'\1\n', s[::-1])
print(s, end='')

Печать:

>Sequence_1  
TATACAT
>Sequence_2 
CATAGTCTATA
person Chris Charley    schedule 10.07.2020