2012-08-24 2 views
4

큰 파일을 구문 분석하기 위해 파이썬을 사용하고 있습니다. 내가 뭘하고 싶은지파이썬 생성기 표현식 if-else

나는 이것을 위해 메모리를 절약하기 위해 표현을 사용하고 싶다. 나는 실제 코드를 넣고있다.

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) 
+2

부수적으로 true/false와 비교하지 마십시오. 'if is_condition_true (r)'과'not is_condition_true (r)'을 사용하십시오. – delnan

+1

델 넌이 맞습니다. 다른 것은 괜찮습니다. – MostafaR

+0

괜찮아 보인다. 실패 했습니까? 그게 당신이 묻는 이유인가요? – inspectorG4dget

답변

6

당신은 itertools.ifilteritertools.ifilterfalse와 함께 itertools.tee를 사용할 수 있습니다

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 자체가 서로 다른 속도로 소비 low 경우 high (크기 len(sequences)의 목록까지) 꽤 많은 메모리를 사용할 수있다 (lowhigh 전에 소진 예를 들어, 경우에 사용됩니다).

+0

아, 높은 메모리 상황을 피해야하므로 사용할 수 없습니다. 시퀀스는 반복자이며 생성자는 아닙니다. sequences = SeqIO.parse (read_file, "fastq") 여전히 중단되어야합니까? – Nupur

+0

어떤 종류의 이터레이터입니까? 반복자는 파이썬에서 반복 할 수있는 모든 것을 나타내는 일반적인 용어입니다. – nneonneo

+0

Biopython 패키지에서 가져온 것입니다. ".. Bio.SeqIO.parse() 파일 핸들과 형식 이름을 사용하고 SeqRecord 반복자" – Nupur

0

나는 컬렉션을 두 번 반복하는 것을 피하려고 노력하고 있다고 생각합니다. 그렇다면이 접근 방식이 효과적입니다.

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

이것은 아마도 부작용에 대한 목록 이해력을 사용하기 때문에 알려지지 않았을 것입니다. 그것은 일반적으로 안티 - 파이톤 스입니다.

+1

글쎄, [None] * len (sequences)의 목록을 생성합니다. 이것은 원래 제안으로 더 많은 메모리를 사용하기 때문에 바람직하지 않습니다. – nneonneo

+1

목록 이해력을 사용하는 대신 동등한 생성자 표현식에'any (...) '를 사용할 수 있습니다. 각 항목이'None'이고 false 인 경우,'any()'는 전체 이터레이터를 사용할 수 있습니다. (당신은 또한 반복자를 소비하기 위해'collections.deque (maxlen = 0) '을 사용할 수 있으며, 사실 테스트가 없기 때문에 더 빨라질 것입니다.) – kindall

+0

나는 더 간단하고 읽기 쉽도록 부작용이 수반 될 때 for-loop (특히 append). 그래서이 경우에는 분명히 루프로 작성합니다. – nneonneo

0

이것은 놀랍게도 우아하게하기가 어렵습니다. 하나의 결과 반복자에서 항목을 (low를 말) 소비로, 티의 내부 양단 큐가 아직 high에서 소비되지 않은 항목을 포함하도록 확장 할 것

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

주 (포함 : 여기에 작동하는 무언가이다 불행하게도, ifilterfalse가 거부 할 것입니다.) 이와 같이 이것은 당신이 원하는만큼의 메모리를 절약하지 못할 수도 있습니다. 이 func 따라 그 값을 항목을 제공하는 발생기 함수의 공역에서 dict를 반환

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

: 여기서

는 가능한 한 적은 메모리를 추가로 사용하는 구현의

gen = filtertee(condition, seq) 
low, high = gen[True], gen[False] 

conditioncodomain의 값만 반환하도록하는 것은 사용자의 책임입니다.

+0

그는 메모리를 절약하기 위해리스트를 생성하는 것을 원하지 않습니다. – nneonneo

+0

@nneonneo 덕분에 간단 해졌습니다! – ecatmur

1

더 일반적인 대답을 추가하기 만하면됩니다. 주된 관심사가 메모리 인 경우 전체 파일을 반복하는 하나의 생성기를 사용하고 각 항목을 낮은 값 또는 높은 값으로 처리해야합니다. 예 :

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

둘 중 하나를 사용하기 전에 모든 고/저 요소를 수집해야하는 경우 잠재적 인 메모리 적중을 방지 할 수 없습니다. 그 이유는 읽을 때까지 어느 요소가 고/저인지 알 수 없기 때문입니다. 낮은 우선 순위로 처리해야하고 모든 요소가 실제로 높다는 것을 알게되면 메모리를 사용하게 될 때 목록에 저장할 수 밖에 없습니다.하나의 루프로 처리하면 각 요소를 한 번에 하나씩 처리 할 수 ​​있지만 다른 고려 사항과 균형을 맞추어야합니다 (즉, 이렇게하는 것이 얼마나 귀찮은 지, 즉 수행하려는 작업에 따라 달라집니다) 데이터와 함께).

+0

죄송합니다. 이해가 안됩니다. 위는 발전기입니까? 나는 실제로 메모리에 저장하지 않는다고 생각하기 때문에 각 배열마다 생성기를 사용하고 싶다. 그냥 표현식 일 뿐이다. 데이터로 처리해야하는 것은 Bio.SeqIO.write를 사용하여 인쇄하는 것입니다. 반복 할 때마다 호출하지 않으면 더 효율적입니다. 또는 간단한 인쇄 문을 사용하여 각 단계에서 인쇄 할 수도 있습니다. 이것은 정말로 비싼 발전기를 만드는 것입니까? 어떤 경우에는 2를 만드는 것이 훨씬 더? – Nupur

+0

@Nupur : 위의 내용은 단지 루프 일뿐입니다. 생성기를 만드는 유일한 이유는 루프에서 사용하는 것입니다. 현재 하나의 생성기 ('sequences'라고 불림)가있는 것 같습니다. 제가 말하고자하는 것은, 실제로 메모리를 절약하고 싶다면, 두 개의 생성기를 생성하는 대신 원래 생성기를 직접 루프하는 것입니다. 당신이하고있는 모든 일이 그것을 쓰는 것이라면, 나의 해결책은 잘 작동 할 것이다. 자세한 내용이 필요하면 질문을 편집하여 데이터를 사용하는 코드에 대한 세부 정보를 제공해야합니다. – BrenBarn