我有一个看起来像这样的文本文件:
>组开始
text1
text2
>新组的开始
text3
我一直在尝试使用itertools.groupby
来返回每个组是包含的列表的列表:
1(从">"字符开始的行。
2(以">"字符开头的行之后的文本行,直到下一行以">"字符开头。
所以从上一篇文章中,我想获得:
[['>Start of group', text1, text2], ['>Start of new group', text3]]
我到目前为止写的代码是:
with open(filename) as rfile:
groups = []
for key, group in groupby(rfile, lambda x: x.startswith(">")):
groups.append(list(group))
但是,这会产生文件的每行在其自己列表中的列表,例如:
[['>Start of group'],[text1],[text2],['>Start of new group'],[text3]]
我认为我可能只是不太了解groupby功能,因为这是我第一次尝试实施它,因此任何解释都会受到赞赏。
这是一种无需组函数的数据的方法。
fin = open('fasta.out', 'r')
data = []
for line in fin:
line = line.rstrip()
if line.startswith('>'):
data.append([line])
else:
data[-1].append(line)
groupby
在某些谓词中,将应用于每个元素的某些谓词中。这意味着分组谓词必须能够通过仅查看一个元素来识别要分组的功能。由于您的数据不允许(您必须查看前面的元素以确定分组密钥(,因此这不是使用groupby
的好候选者,而Chris Charley的答案是一个清洁的解决方案。
也就是说,如果您将其视为一个编码挑战而不是解决现实世界中的问题,则可以创建一个存储状态的分组功能,并跟踪所看到的最后一个组标签。一个实现__call__
并存储最后一个组标签的类,并返回,当下一个输入不是组标签时,可以实现您想要的。
关键是用相同的数字标记同一组中的每一行,这可以使用另一个生成器来完成。考虑一下groupby
的工作方式,而不是实际建议;改用Chris Charley的答案。
def number_lines(txt):
i = 0
for line in text:
if line.startswith(">"):
i += 1
yield (1, line)
注意number_lines
产生的元素的顺序自动通过元组的第一个元素进行排序。为了将它们分组,请告诉groupby
将第一个元素用作"组标签"。
from operator import itemgetter
with open(filename) as rfile:
numbered_lines = number(rfile)
groups = [[line for n, line in group]
for number, group in groupby(numbered_lines, itemgetter(0))]