4

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)
4

4 に答える 4

6

および とitertools.tee組み合わせて使用​​できます。itertools.ifilteritertools.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は、それ自体が ( size のリストまで) かなりの量のメモリを使用する可能性があることに注意してください。len(sequences)lowhighlowhigh

于 2012-08-24T16:30:50.080 に答える
1

より一般的な答えを追加するだけです。主な関心事がメモリである場合は、ファイル全体をループする1 つのジェネレーターを使用し、各項目をできるだけ低くまたは高く処理する必要があります。何かのようなもの:

for r in sequences:
    if condition_true(r):
        handle_low(r)
    else:
        handle_high(r)

いずれかを使用する前にすべての高/低要素を収集する必要がある場合、メモリ ヒットの可能性を防ぐことはできません。その理由は、読んでみないとどの要素が高い/低いか分からないからです。最初に low を処理する必要があり、すべての要素が実際に high であることが判明した場合は、それらをリストに格納する以外に選択肢がなく、メモリを使用します。1 つのループでそれを行うと、各要素を一度に 1 つずつ処理できますが、これを他の懸念事項と比較する必要があります (つまり、この方法で行うのがどれほど面倒かは、何をしようとしているのかによって異なります)。データ付き)。

于 2012-08-24T16:46:59.830 に答える
1

コレクションを 2 回反復しないように努力していると思います。その場合、このタイプのアプローチが機能します。

high, low = [], []
_Nones = [high.append(x) if is_condition_true() else low.append(x) for x in sequences]

副作用のためにリスト内包表記を使用しているため、これはおそらく推奨されるほどではありません。それは一般的にアンチパイソンです。

于 2012-08-24T16:31:07.267 に答える
0

これを優雅に行うのは驚くほど難しい。これが機能するものです:

from itertools import tee, ifilter, ifilterfalse
low, high = [f(condition, g) for f, g in zip((ifilter, ifilterfalse), tee(seq))]

low1 つの結果のイテレータ (たとえば) からアイテムを消費すると、まだ消費していないアイテムhigh(残念ながらifilterfalse拒否されるアイテムを含む) を含むように、tee の内部両端キューを拡張する必要があることに注意してください。そのため、これは期待したほど多くのメモリを節約しない可能性があります。

可能な限り追加のメモリを使用しない実装を次に示します。

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]

conditionが の値のみを返すようにするのは、ユーザーの責任であることに注意してくださいcodomain

于 2012-08-24T16:31:58.840 に答える