Python 3.x - Как эффективно разделить массив объектов на меньшие пакетные файлы? - PullRequest
3 голосов
/ 25 апреля 2019

Я довольно новичок в Python и пытаюсь разбить текстовый файл, где записи состоят из двух строк в пакетах по макс.400 объектов.

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

>HORVU6Hr1G000325.5

PIPPPASHFHPHHQNPSAATQPLCAAMAPAAKKPPLKSSSSHNSAAGDAA

> HORVU6Hr1G000326.1

*1011* *1011* 101K * RH2NHDсинтаксический анализатор SeqIO.parse, который позволяет обращаться к ним как к массиву объектов, состоящему из идентификаторов и строк, которые мне нужно использовать в последующих частях моего кода, и, поскольку мне нужно эффективно использовать память, я бы хотел избежать чтения /Разбор исходного файла больше, чем необходимо.

В руководстве по Biopython есть рекомендуемый способ сделать это с помощью генератора, который я использую: https://biopython.org/wiki/Split_large_file

Однако я использую Python 3.7, хотя код вPython 2.x, поэтому, безусловно, необходимы некоторые изменения.Я изменил

entry = iterator.next ()

на

entry = next (итератор)

но я не уверен, что это все, что мне нужно изменить.

Вот код:

def batch_iterator(iterator, batch_size=400):
    """Returns lists of length batch_size."""
    entry = True  # Make sure we loop once
    while entry:
        batch = []
        while len(batch) < batch_size:
            try:
                entry = next(iterator)
            except StopIteration:
                entry = None

            if entry is None:
                # End of file
                break
            batch.append(entry)
        if batch:
            yield batch

while True:
    bsequence = input("Please enter the full path to your FASTA file(e.g. c:\\folder1\\folder2\\protein.fasta):\n")
    try:
        fastafile = open(bsequence)
        break
    except:
        print("File not found!\n")            


record_iter = SeqIO.parse(fastafile,"fasta")
num = 0
for line in fastafile:
    if line.startswith(">"):
        num += 1

print("num=%i" % (num,))
if num > 400:
    print("The specified file contains %i sequences. It's recommended to split the FASTA file into batches of max. 400 sequences.\n" % (num,))
    while True:
        decision = input("Do you wish to create batch files? (Original file will not be overwritten)\n(Y/N):")
        if (decision == 'Y' or 'y'):
            for i, batch in enumerate(batch_iterator(record_iter, 400), 1):
                filename = "group_%i.fasta" % (i + 1)
                with open(filename, "w") as handle:
                    count = SeqIO.write(batch, handle, "fasta")
                print("Wrote %i records to %s" % (count, filename))
            break
        elif (decision == 'N' or 'n'):
            break
        else:
            print('Invalid input\n')

...next part of the code

Когда я запускаю это, после запроса Y / N,даже если я наберу Y, программа просто перейдет к следующей части моего кода без создания какого-либо нового файла.Отладчик показывает следующее:

Do you wish to create batch files? (Original file will not be overwritten)
(Y/N):Y
Traceback (most recent call last):
  File "\Biopython\mainscript.py", line 32, in batch_iterator
    entry = next(iterator)
StopIteration

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\backend.py", line 1569, in _trace
    return self._trace_and_catch(frame, event, arg)

  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\backend.py", line 1611, in _trace_and_catch
    frame.f_back, event, marker_function_args, node

  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\backend.py", line 1656, in _handle_progress_event
    self._save_current_state(frame, event, args, node)

  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\backend.py", line 1738, in _save_current_state
    exception_info = self._export_exception_info()

  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\backend.py", line 1371, in _export_exception_info
    "affected_frame_ids": exc[1]._affected_frame_ids_,

AttributeError: 'StopIteration' object has no attribute '_affected_frame_ids_'

Есть ли какая-то разница между Python 2.x и 3.x, которую я пропускаю?Проблема где-то еще?Является ли этот подход совершенно неправильным?Заранее спасибо!

1 Ответ

2 голосов
/ 25 апреля 2019

Я не могу проверить весь ваш код, так как вы пропустили его часть, но я вижу здесь две неправильные вещи:

num = 0
for line in fastafile:
    if line.startswith(">"):
        num += 1

Эти строки исчерпывают ваш файловый объект fastafile. Полностью удалите эти строки (и не забудьте исправить отступы ниже, снимите флажок if num > 400: и т. Д.).

if (decision == 'Y' or 'y'):

Это не делает то, что вы думаете, что делает. Измените его на if decision in ('Y', 'y'): или if decision.lower() == 'y':. Вы повторяете эту схему ниже в строке if (decision == 'N' or 'n'):, поэтому измените и ее.

Внесите изменения и попробуйте снова запустить код.

Объяснение

1-й выпуск : в Python файловый объект (то есть, что возвращает open('filename.txt', 'r')) является генератором, что означает, что он может быть повторен только один раз. Поначалу это может показаться немного странным, но в этом весь смысл использования генераторов. Генератор как файловый объект позволяет зацикливать файл за строкой, без необходимости загружать все содержимое файла сразу - генератор просто отслеживает, какая строка будет следующей.

Обратной стороной является то, что они не могут двигаться назад, поэтому, когда вы пишете свой блок for line in fastafile, вы исчерпываете генератор. Когда вы позже попытаетесь вызвать batch_iterator(record_iter, 400), генератор в record_iter уже исчерпан, поэтому позже вы столкнетесь с ошибкой - batch_iterator не сможет проанализировать последовательности fastta, если там ничего не осталось для анализа.

2-й выпуск : для условий с булевыми операторами, такими как if (decision == 'Y' or 'y'):, Python всегда будет оценивать обе стороны по отдельности. Таким образом, Python на самом деле видит if (bool(decision == 'Y') or bool('y')):.

Поскольку bool('y') оценивается как True (как и любая непустая строка), ваше выражение становится if (bool(decision == 'Y') or True):, что, очевидно, всегда верно.

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

...