Python 3.x - 如何高效地将 objects 的数组拆分成更小的批处理文件?

Python 3.x - How to efficiently split an array of objects into smaller batch files?

我是 Python 的新手,我正在尝试将一个文本文件拆分为最多 2 行的文本文件。 400objects.

我正在处理的数据是 FASTA 格式的数千个序列(带有 header 的纯文本,用于生物信息学),其中条目如下所示:

>HORVU6Hr1G000325.5

PIPPPASHFHPHHQNPSAATQPLCAAMAPAAKKPPLKSSSSHNSAAGDAA

>HORVU6Hr1G000326.1

MVKFTAEELRGIMDKKNNIRNMSVIAHVD

...

在 Biopython 中,有一个解析器 SeqIO.parse 允许将它们作为 objects 的数组进行访问,其中包含 ID 和字符串,我需要在代码的后面部分使用它们,并且因为我需要提高内存效率,所以我想避免 reading/parsing 源文件的次数超过必要的次数。

在 Biopython 手册中,推荐了一种通过生成器执行此操作的方法,我正在使用它:https://biopython.org/wiki/Split_large_file

但是,我使用的是 Python 3.7,而代码在 Python 2.x 中,因此肯定需要进行一些更改。我已经更改了

entry = iterator.next()

进入

entry = next(iterator)

但我不确定这是否是我需要更改的全部内容。

代码如下:

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 之间有什么区别吗?问题出在其他地方吗?这种做法是完全错误的吗?提前致谢!

我无法检查你的全部代码,因为你省略了一部分,但我可以在这里看到两个错误的地方:

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'): 中重复此模式,因此也更改它。

进行更改并再次尝试 运行 代码。

说明

第一期:在Python中,一个文件对象(即什么open('filename.txt', 'r')returns)是一个生成器,也就是说它只能迭代一次。乍一看这似乎有点奇怪,但这就是使用生成器的全部意义所在。作为文件对象的生成器允许文件逐行循环,而不必一次加载整个文件内容——生成器只跟踪下一行。

不利的一面是它们不能倒退,因此当您编写 for line in fastafile 块时,您会耗尽生成器。当您稍后尝试调用 batch_iterator(record_iter, 400) 时,record_iter 中的生成器已经耗尽,这就是您稍后会遇到错误的原因 - 如果没有任何内容,batch_iterator 无法解析 fasta 序列留在那里进行解析。

第 2 期:对于带有布尔运算符的条件,例如 if (decision == 'Y' or 'y'):,Python 将始终单独评估双方。所以 Python 实际上看到的是 if (bool(decision == 'Y') or bool('y')):.

由于 bool('y') 的计算结果为 True(就像任何非空字符串一样),您的表达式变为 if (bool(decision == 'Y') or True):,这显然总是正确的。

使用我建议的方法之一在条件中将一个变量与多个值进行比较。