Python生成器表达式if-else
我正在使用Python解析一个大文件。我想做的是Python生成器表达式if-else,python,generator-expression,Python,Generator Expression,我正在使用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)
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)
def为低质量(读取):
lowqual\u bp=(如果bq允许的数量:
返回真值
其他:
返回错误
lowqual=(在SeqIO.parse(r_文件,“fastq”)中读取,如果是低质量(读取)==True)
highqual=(在SeqIO.parse(r_文件,“fastq”)中读取,如果是低质量(读取)==False)
顺序写入(高质量、flt\u out\u句柄、“fastq”)
SeqIO.write(低质量、垃圾处理、“快速处理”)
定义iter_长度(第二代):
返回和(1代表第i列)
您可以将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(序列)
).我认为您正在努力避免重复收集两次。如果是这样,这种方法是有效的:
high, low = [], []
_Nones = [high.append(x) if is_condition_true() else low.append(x) for x in sequences]
这可能比建议的要少,因为它使用列表理解来产生副作用。这通常是反pythonic的。这很难做到优雅。以下是一些有效的方法:
from itertools import tee, ifilter, ifilterfalse
low, high = [f(condition, g) for f, g in zip((ifilter, ifilterfalse), tee(seq))]
请注意,当您从一个结果迭代器(例如低
)中消费项时,tee中的内部数据必须展开以包含您尚未从高
中消费的任何项(不幸的是,包括那些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())
这将从函数的代码域返回一个dict
,并返回给生成器,生成器提供在func
下获取该值的项:
gen = filtertee(condition, seq)
low, high = gen[True], gen[False]
请注意,您有责任确保
条件
只返回codomain
中的值,这只是为了添加一个更一般的答案:如果您主要关心的是内存,您应该使用一个生成器循环整个文件,并尽可能低或高地处理每个项。比如:
for r in sequences:
if condition_true(r):
handle_low(r)
else:
handle_high(r)
如果您需要在使用任何一个元素之前收集所有高/低元素,那么您无法防止潜在的内存命中。原因是,在阅读之前,您无法知道哪些元素是高/低。如果您必须先处理low,而所有元素实际上都是high,那么您别无选择,只能在运行时将它们存储在一个列表中,这将使用内存。使用一个循环可以让您一次处理一个元素,但您必须平衡这一点与其他问题(即,以这种方式进行操作有多麻烦,这完全取决于您试图对数据执行的操作)。作为旁注,不要将其与真/假进行比较。使用
if is\u condition\u true(r)
和if not is\u condition\u true(r)
。delnan是对的,其他事情都可以。这看起来很好。这失败了吗?这就是你问的原因吗?它可能有效,但它既丑陋又低效。如果sequences
是迭代器,它也会中断(不过你可以使用itertools.tee
)。在你创建生成器之后,你如何使用low
和high
?这也会创建一个[None]*len(sequences)列表,这是不可取的,因为它使用了更多的内存作为他的原始建议。您可以使用any(…)
和等效的生成器表达式,而不是使用列表理解。由于每个项都是None
,因此为false,any()
保证使用整个迭代器。(您也可以使用collections.deque(maxlen=0)
来使用迭代器;它可能会更快,因为它不进行真实性测试。)我发现在涉及副作用(特别是追加)时使用for循环通常更简单(也更可读)。所以在这种情况下,我肯定会把它写成一个循环。我想避免列表理解,因为它们很昂贵。好的,让我看看any和deque-我对它们不熟悉他不希望生成列表来节省内存。哦,我必须避免高内存的情况,所以不能使用它。序列是迭代器,而不是生成器。sequences=SeqIO.parse(读取_文件,“fastq”)是否仍会中断?什么类型的迭代器?迭代器是Python中可以迭代的任何东西的通用术语。它来自Biopython包“…Bio.SeqIO.parse(),它接受文件句柄和格式名称,并返回SeqRecord迭代器“OK”。所以现在你的问题有道理了。您有一个包含许多记录的大文件,您希望根据一些过滤器将该文件拆分为两个较小的文件,每个文件包含文件的一半,而无需将整个内容读入内存。是这样吗?在这种情况下,最好的选择是在每个记录从输入迭代器中出来时,一次一个地将记录写入相应的文件。这占用了最少的内存,只迭代一次。很抱歉,我不明白。上面的机器是发电机吗?我想为每个数组使用一个生成器,因为我认为它实际上并没有存储在内存中,对吧,它只是一个表达式?我需要用Bio.SeqIO.write将数据打印出来——如果不在每次循环时调用它,效率会更高。或者,我可以使用简单的打印语句在每个步骤中打印。归根结底,制造发电机真的很贵吗?在哪种情况下,创建2更重要?@Nupur:以上只是一个循环。创建生成器的唯一原因是在循环中使用它。您目前似乎有一个生成器(称为序列)<