我使用Python来解析一个大文件。我想做的是
If condition =True
append to list A
else
append to list B
我想为此使用生成器表达式 - 以节省内存。我正在加入实际的代码。
def is_low_qual(read):
lowqual_bp=(bq for bq in phred_quals(read) if bq < qual_threshold)
if iter_length(lowqual_bp) > num_allowed:
return True
else:
return False
lowqual=(read for read in SeqIO.parse(r_file,"fastq") if is_low_qual(read)==True)
highqual=(read for read in SeqIO.parse(r_file,"fastq") if is_low_qual(read)==False)
SeqIO.write(highqual,flt_out_handle,"fastq")
SeqIO.write(lowqual,junk_out_handle,"fastq")
def iter_length(the_gen):
return sum(1 for i in the_gen)
答案 0 :(得分:6)
您可以将itertools.tee
与itertools.ifilter
和itertools.ifilterfalse
结合使用:
import itertools
def is_condition_true(x):
...
gen1, gen2 = itertools.tee(sequences)
low = itertools.ifilter(is_condition_true, gen1)
high = itertools.ifilterfalse(is_condition_true, gen2)
使用tee
可确保函数正常工作,即使序列本身就是一个生成器。
但请注意,tee
本身可以使用相当大的内存(最多为大小len(sequences)
的列表),如果low
和high
在不同时使用费率(例如,如果在使用low
之前high
已用尽)。
答案 1 :(得分:1)
只是添加一个更一般的答案:如果你主要关心的是内存,你应该使用一个生成器循环遍历整个文件,并处理每个项目的低或高。类似的东西:
for r in sequences:
if condition_true(r):
handle_low(r)
else:
handle_high(r)
如果您需要在使用之前收集所有高/低元素,那么您无法防止潜在的内存命中。原因是在阅读之前,您无法知道哪些元素是高/低。如果你必须首先处理低,并且事实证明所有元素实际上都很高,你别无选择,只能将它们存储在列表中,这将使用内存。使用一个循环执行此操作允许您一次处理一个元素,但是您必须与其他问题进行平衡(即,这样做有多麻烦,这将取决于您正在尝试做什么与数据)。
答案 2 :(得分:0)
我认为你正在努力避免两次迭代你的收藏。如果是这样,这种方法有效:
high, low = [], []
_Nones = [high.append(x) if is_condition_true() else low.append(x) for x in sequences]
这可能比建议少,因为它使用列表理解来产生副作用。这通常是反pythonic。
答案 3 :(得分:0)
这很难做到优雅。这是有用的:
from itertools import tee, ifilter, ifilterfalse
low, high = [f(condition, g) for f, g in zip((ifilter, ifilterfalse), tee(seq))]
请注意,当您从一个生成的迭代器(例如low
)中使用项目时,tee中的内部双端队列必须展开以包含您尚未从high
中消耗的任何项目(包括,不幸的是,ifilterfalse
将拒绝的那些人。因此,这可能不会像您希望的那样节省大量内存。
这是一个使用尽可能少的额外内存的实现:
def filtertee(func, iterable, codomain=(False, True)):
it = iter(iterable)
deques = dict((r, deque()) for r in codomain)
def gen(mydeque):
while True:
while not mydeque: # as long as the local deque is empty
newval = next(it) # fetch a new value,
result = func(newval) # find its image under `func`,
try:
d = deques[result] # find the appropriate deque, and
except KeyError:
raise ValueError("func returned value outside codomain")
d.append(newval) # add it.
yield mydeque.popleft()
return dict((r, gen(d)) for r, d in deques.items())
这会将函数的codomain中的dict
返回给生成器,该生成器提供在func
下获取该值的项:
gen = filtertee(condition, seq)
low, high = gen[True], gen[False]
请注意,您有责任确保condition
仅返回codomain
中的值。