[python] 파이썬 : 조건에 따라 목록을 나누시겠습니까?

미적 측면과 성능 측면에서 조건부에 따라 항목 목록을 여러 목록으로 분할하는 가장 좋은 방법은 무엇입니까? 다음과 같습니다.

good = [x for x in mylist if x in goodvals]
bad  = [x for x in mylist if x not in goodvals]

더 우아한 방법이 있습니까?

업데이트 : 여기에 내가하려는 일을 더 잘 설명하기위한 실제 사용 사례가 있습니다.

# files looks like: [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ... ]
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f[2].lower() not in IMAGE_TYPES]



답변

good = [x for x in mylist if x in goodvals]
bad  = [x for x in mylist if x not in goodvals]

더 우아한 방법이 있습니까?

이 코드는 완벽하게 읽을 수 있으며 매우 명확합니다!

# files looks like: [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ... ]
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f[2].lower() not in IMAGE_TYPES]

다시, 이것은 괜찮습니다!

세트를 사용하면 약간의 성능 향상이있을 수 있지만 사소한 차이가 있으므로 목록 이해가 훨씬 쉬워지고 순서가 엉망이되어 복제본이 제거되는 것에 대해 걱정할 필요가 없습니다.

사실, 나는 다른 단계 “뒤로”가서 간단한 for 루프를 사용할 수 있습니다.

images, anims = [], []

for f in files:
    if f.lower() in IMAGE_TYPES:
        images.append(f)
    else:
        anims.append(f)

목록 이해 또는 사용 set() 은 다른 검사 또는 다른 비트를 추가해야 할 때까지 괜찮습니다 .0 바이트 jpeg를 모두 제거하려면 다음과 같이 추가하십시오.

if f[1] == 0:
    continue


답변

good, bad = [], []
for x in mylist:
    (bad, good)[x in goodvals].append(x)


답변

게으른 반복자 접근 방식은 다음과 같습니다.

from itertools import tee

def split_on_condition(seq, condition):
    l1, l2 = tee((condition(item), item) for item in seq)
    return (i for p, i in l1 if p), (i for p, i in l2 if not p)

항목 당 한 번 조건을 평가하고 두 개의 생성기를 반환하며, 먼저 조건이 참인 순서에서 값을 산출하고 다른 조건이 거짓 인 값을 산출합니다.

게으 르기 때문에 모든 반복자, 심지어 무한 반복자에서도 사용할 수 있습니다.

from itertools import count, islice

def is_prime(n):
    return n > 1 and all(n % i for i in xrange(2, n))

primes, not_primes = split_on_condition(count(), is_prime)
print("First 10 primes", list(islice(primes, 10)))
print("First 10 non-primes", list(islice(not_primes, 10)))

일반적으로 비 게으른 목록 반환 방법이 더 낫지 만

def split_on_condition(seq, condition):
    a, b = [], []
    for item in seq:
        (a if condition(item) else b).append(item)
    return a, b

편집 : 키를 사용하여 항목을 다른 목록으로 나누는 더 구체적인 유스 케이스의 경우 다음과 같은 일반적인 기능이 있습니다.

DROP_VALUE = lambda _:_
def split_by_key(seq, resultmapping, keyfunc, default=DROP_VALUE):
    """Split a sequence into lists based on a key function.

        seq - input sequence
        resultmapping - a dictionary that maps from target lists to keys that go to that list
        keyfunc - function to calculate the key of an input value
        default - the target where items that don't have a corresponding key go, by default they are dropped
    """
    result_lists = dict((key, []) for key in resultmapping)
    appenders = dict((key, result_lists[target].append) for target, keys in resultmapping.items() for key in keys)

    if default is not DROP_VALUE:
        result_lists.setdefault(default, [])
        default_action = result_lists[default].append
    else:
        default_action = DROP_VALUE

    for item in seq:
        appenders.get(keyfunc(item), default_action)(item)

    return result_lists

용법:

def file_extension(f):
    return f[2].lower()

split_files = split_by_key(files, {'images': IMAGE_TYPES}, keyfunc=file_extension, default='anims')
print split_files['images']
print split_files['anims']


답변

제안 된 모든 솔루션의 문제점은 필터링 기능을 두 번 스캔하고 적용한다는 것입니다. 다음과 같이 간단한 작은 기능을 만들었습니다.

def SplitIntoTwoLists(l, f):
  a = []
  b = []
  for i in l:
    if f(i):
      a.append(i)
    else:
      b.append(i)
 return (a,b)

그렇게하면 아무것도 처리하지 않고 코드를 반복하지 않습니다.


답변

내가 가져가 partition출력 하위 시퀀스에서 상대 순서를 유지 하는 게으른 단일 패스 함수를 제안합니다 .

1. 요구 사항

요구 사항은 다음과 같다고 가정합니다.

  • 요소의 상대적 순서 유지 (따라서 세트와 사전 없음)
  • 모든 요소에 대해 조건을 한 번만 평가하십시오 (따라서 ( i) filter또는 groupby)
  • 두 시퀀스 중 하나의 지연 소비를 허용합니다 (사전 계산할 여유가 있다면 순진한 구현도 수용 가능할 것입니다)

2. split도서관

partition기능 (아래 소개) 및 기타 유사한 기능으로 작은 라이브러리로 만들었습니다.

PyPI를 통해 정상적으로 설치 가능합니다 :

pip install --user split

조건에 따라 목록 기반을 분할하려면 partitionfunction을 사용하십시오 .

>>> from split import partition
>>> files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi') ]
>>> image_types = ('.jpg','.jpeg','.gif','.bmp','.png')
>>> images, other = partition(lambda f: f[-1] in image_types, files)
>>> list(images)
[('file1.jpg', 33L, '.jpg')]
>>> list(other)
[('file2.avi', 999L, '.avi')]

3. partition기능 설명

내부적으로 우리는 한 번에 두 개의 서브 시퀀스를 구축해야하므로, 하나의 출력 시퀀스 만 소비하면 다른 하나의 시퀀스도 계산됩니다. 또한 사용자 요청 (상점 처리되었지만 아직 요청되지 않은 요소)간에 상태를 유지해야합니다. 상태를 유지하기 위해 두 개의 이중 끝 큐 ( deques)를 사용합니다.

from collections import deque

SplitSeq 수업은 하우스 키핑을 담당합니다.

class SplitSeq:
    def __init__(self, condition, sequence):
        self.cond = condition
        self.goods = deque([])
        self.bads = deque([])
        self.seq = iter(sequence)

마술은 그 .getNext()방법으로 일어난다 . 그것은 거의 .next()
반복자와 비슷하지만 이번에는 원하는 요소를 지정할 수 있습니다. 장면 뒤에서 거부 된 요소를 버리지 않고 대신 두 개의 대기열 중 하나에 넣습니다.

    def getNext(self, getGood=True):
        if getGood:
            these, those, cond = self.goods, self.bads, self.cond
        else:
            these, those, cond = self.bads, self.goods, lambda x: not self.cond(x)
        if these:
            return these.popleft()
        else:
            while 1: # exit on StopIteration
                n = self.seq.next()
                if cond(n):
                    return n
                else:
                    those.append(n)

최종 사용자는 partition기능 을 사용해야 합니다. 조건 함수와 시퀀스 ( map또는 처럼 filter)를 취하고 두 개의 생성기를 반환합니다. 첫 번째 생성기는 조건이 유지되는 요소의 하위 시퀀스를 작성하고 두 번째 생성기는 상보 하위 시퀀스를 작성합니다. 반복자와 생성기는 길거나 무한한 시퀀스의 지연 분할을 허용합니다.

def partition(condition, sequence):
    cond = condition if condition else bool  # evaluate as bool if condition == None
    ss = SplitSeq(cond, sequence)
    def goods():
        while 1:
            yield ss.getNext(getGood=True)
    def bads():
        while 1:
            yield ss.getNext(getGood=False)
    return goods(), bads()

미래에 부분 적용을 용이하게하기 위해 테스트 함수를 첫 번째 인수로 선택했습니다 (테스트 함수를 첫 번째 인수로 사용하는 방법 map과 유사 함 filter
).


답변

나는 기본적으로 Anders의 접근 방식을 매우 좋아합니다. 다음은 분류기를 가장 먼저 (필터 구문과 일치시키기 위해) 배치하고 defaultdict (가져온 것으로 가정)를 사용하는 버전입니다.

def categorize(func, seq):
    """Return mapping from categories to lists
    of categorized items.
    """
    d = defaultdict(list)
    for item in seq:
        d[func(item)].append(item)
    return d


답변

첫 번째 이동 (사전 편집) : 세트 사용 :

mylist = [1,2,3,4,5,6,7]
goodvals = [1,3,7,8,9]

myset = set(mylist)
goodset = set(goodvals)

print list(myset.intersection(goodset))  # [1, 3, 7]
print list(myset.difference(goodset))    # [2, 4, 5, 6]

가독성 (IMHO)과 성능 모두에 좋습니다.

두번째 이동 (post-OP-edit) :

좋은 확장명 목록을 세트로 작성하십시오.

IMAGE_TYPES = set(['.jpg','.jpeg','.gif','.bmp','.png'])

그러면 성능이 향상됩니다. 그렇지 않으면, 당신이 나에게 잘 보이는 것.