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


272

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

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]

5
세트 빌더 문에서 조건을 가질 수있는 방법을 찾고 여기에 착륙, 당신의 질문은 내 질문에 대답 :)
Anuvrat Parashar

5
split 은 파이썬 문자열과 관련하여 이미 특정한 의미를 가지고 있기 때문에이 작업에 대한 불행한 설명입니다. 나는 분할 이이 작업을 설명하는보다 정확하거나 (파이썬 이터 러블의 맥락에서 덜 오버로드) 단어 라고 생각 합니다. 나는 목록에 해당 찾고 여기에 도착 str.split()하기 위해, 분할 연속 하위 목록의 정렬 된 콜렉션으로 목록을. 예를 들어 split([1,2,3,4,5,3,6], 3) -> ([1,2],[4,5],[6]), 목록의 요소를 범주별로 나누는 것과 반대 입니다.
Ste 's

python-list에서 같은 주제에 대한 토론 .
Xiong Chiamiov

IMAGE_TYPES는 튜플 대신 세트 여야합니다 IMAGE_TYPES = set('.jpg','.jpeg','.gif','.bmp','.png'). 가독성의 차이가없는 n (o / 2) 대신 n (1).
ChaimG

답변:


110
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

44
목록을 두 번 반복하지 않고도 목록 이해 방법이 없습니까?
balki

35
문제는 이것이 DRY 원칙을 위반한다는 것입니다. 더 좋은 방법이 있다면 좋을 것입니다.
안티몬

21
발생 함수형 프로그래밍 (하스켈) 또는 기능적인 스타일 (LINQ)에 대한 식욕되면, 우리는 나이 파이썬 냄새 시작 - [x for x in blah if ...]- 자세한, lambda서투른 및 제한 ... 그것은 1995 년 오늘에서 가장 멋진 차를 운전하는 것 같은 느낌이 든다. 그때와 같지 않습니다.
Tomasz Gandor

6
@TomaszGandor FTR 인 Haskell은 Python보다 오래 되었으며 실제로 디자인에 영향을 미쳤습니다. 나는 목록 이해와 람다에 대한 구문이 의도적으로 장황하게 약간 유지되었다고 생각합니다. 하스켈을 좋아하는 한 많은 사람들이 왜 파이썬이 일반적으로 더 읽기 쉬운 지 알 수 있습니다.
leftaroundabout

4
간단한 for 루프가 가장 좋은 방법입니다 ... 단일 루프, 매우 명확하고 읽기 쉬운
Anentropic

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

14
엄청나게 독창적입니다! 그래도 무슨 일이 있었는지 이해하는 데 시간이 걸렸습니다. 다른 사람들이 이것이 읽을 수있는 코드로 간주 될 수 있다고 생각하는지 알고 싶습니다.
jgpaiva

171
good.append(x) if x in goodvals else bad.append(x)더 읽기 쉽습니다.
dansalmo

21
@dansalmo 특히 사이클과 함께 하나의 라이너로 만들 수 있기 때문에보다 복잡한 것을 추가하고 싶다면 하나만 x만들 수 있습니다 append.for x in mylist: (good if isgood(x) else bad).append(x)
yo '

2
@MLister,이 경우 아마도 속성 조회를 포함시켜야합니다(bad.append, good.append)
John La Rooy

11
약간 더 짧은 변형 :(good if x in goodvals else bad).append(x)
Pi Delport

104

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

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']

이것이 YAGNI 원칙을 위반하는 것이 옳습니다. 사물을 분할 할 수있는 다른 목록의 수가 앞으로 늘어날 것이라는 가정을 기반으로합니다.
Ants Aasma 2016 년

17
많은 코드 일 수 있지만 [ x for x in my_list if ExpensiveOperation(x) ]실행하는 데 시간이 오래 걸리면 분명히 두 번하고 싶지 않습니다!
dash-tom-bang

1
반복자 기반 및 특정 "in X"솔루션을 포함한 여러 변형을 제공하는 +1 OP의 "goodvals"는 작을 수 있지만이를 매우 큰 사전이나 비싼 술어로 바꾸면 비용이 많이들 수 있습니다. 또한이 두 번 지능형리스트를 작성할 필요가 감소 사방 이 때문에 오타 / 사용자 오류를 도입하는 가능성을 감소, 필요한합니다. 좋은 해결책. 감사!
cod3monk3y 2016

3
tee반환하는 반복자 사이에 모든 값 을 저장하므로 하나의 전체 생성기를 반복하고 다른 생성기를 반복하면 메모리를 실제로 절약하지 않습니다.
John La Rooy

25

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

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

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


나는 동의한다. 나는 목록을 두 번 스캔하지 않고이를 수행하는 "우아한"(즉, 여기서 짧고 내장 / 암시 적 의미) 방법을 찾고 있었지만 (프로파일 링없이) 갈 길입니다. 물론 많은 양의 데이터에만 중요합니다.
Matthew Flaschen

IMHO, CPU 사용량이 적고 (전력 소모가 적은) 방법을 알고 있다면 사용하지 않을 이유가 없습니다.
19시

2
@winden ... 모든 파이썬을 C로 포팅;)
Elliot Cameron

19

내가 가져가 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 ).


15

나는 기본적으로 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

여기에 적용 되는 Python of Zen의 문장을 선택하려고 시도 했지만 의견이 너무 많습니다. =) 멋진 코드입니다.
jpmc26

13

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

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'])

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


4
분할하기 전에 목록이 순서대로 정렬되어 있고 순서대로 유지해야하는 경우 최상의 솔루션이 아닙니다.
Daniyar 2016 년

8
중복을 제거하지 않습니까?
mavnn 2016 년

집합 만들기는 O (n log n)입니다. 목록을 두 번 반복하는 것은 O (n)입니다. 설정된 솔루션은 더 우아 할 수 있지만 (처음에는 정확할 때) n이 증가함에 따라 가장 느립니다.
dash-tom-bang

1
@ dash-tom-bang 목록을 반복하는 것은 O (n * n)입니다. 목록의 각 항목을의 각 항목과 비교해야 할 수 있기 때문입니다 goodvals.
ChaimG

@ChaimG의 좋은 점이지만 교차점 및 차분 연산의 비용도 고려해야합니다 (손으로 알지는 않지만 초 선형이라고 확신합니다).
dash-tom-bang

10

itertools.groupby 는 하나의 연속 범위를 갖도록 항목을 정렬해야한다는 점을 제외하고 원하는 것을 거의 수행하므로 키로 먼저 정렬해야합니다 (그렇지 않으면 각 유형에 대해 여러 개의 인터리브 그룹을 얻습니다). 예.

def is_good(f):
    return f[2].lower() in IMAGE_TYPES

files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ('file3.gif', 123L, '.gif')]

for key, group in itertools.groupby(sorted(files, key=is_good), key=is_good):
    print key, list(group)

제공합니다 :

False [('file2.avi', 999L, '.avi')]
True [('file1.jpg', 33L, '.jpg'), ('file3.gif', 123L, '.gif')]

다른 솔루션과 마찬가지로 주요 기능은 원하는 수의 그룹으로 나누도록 정의 할 수 있습니다.


6
good.append(x) if x in goodvals else bad.append(x)

@dansalmo의 우아하고 간결한 답변은 의견에 묻혀 있으므로 여기에 답변으로 다시 게시하여 특히 새로운 독자에게 가치가 있음을 알 수 있습니다.

완전한 예 :

good, bad = [], []
for x in my_list:
    good.append(x) if x in goodvals else bad.append(x)

5

FP 스타일로 만들려면 :

good, bad = [ sum(x, []) for x in zip(*(([y], []) if y in goodvals else ([], [y])
                                        for y in mylist)) ]

가장 읽기 쉬운 솔루션은 아니지만 최소한 mylist를 한 번만 반복합니다.


1
목록을 한 번만 반복하지만 목록이 추가되어 성능이 좋지 않습니다. 목록에 추가하면 작업 비용이 많이들 수 있습니다 (예 : deque.append와 비교할 때). 실제로이 솔루션은 여기의 다른 솔루션과 비교할 때 매우 느립니다 (100000 임의의 정수에서 21.4 초 및 값 테스트).
rlat

5

개인적으로, 나는 당신이 이미 goodvals매달려 있는 목록을 가지고 있다고 가정하고 당신이 인용 한 버전을 좋아합니다 . 그렇지 않은 경우 :

good = filter(lambda x: is_good(x), mylist)
bad = filter(lambda x: not is_good(x), mylist)

물론 이는 원래처럼 목록 이해를 사용하는 것과 매우 유사하지만 조회 대신 함수를 사용합니다.

good = [x for x in mylist if is_good(x)]
bad  = [x for x in mylist if not is_good(x)]

일반적으로 나는 목록 이해의 미학이 매우 기쁘게 생각합니다. 물론 실제로 순서를 유지할 필요가없고 복제본이 필요하지 않은 경우 세트 에서 intersectionand difference메소드를 사용하면 효과가 있습니다.


물론 filter(lambda x: is_good(x), mylist)감소 될 수 있습니다filter(is_good, mylist)
robru

여분의 함수 호출을 추가하면 프로파일 링에서 본 것보다 목록 이해와 비교하여 실제로 실행 시간이 두 배가됩니다 (!). 대부분의 경우 목록 이해력을이기는 것은 어렵습니다.
Corley Brigman

4

N 조건에 따라 iterable을 분할하는 일반화가 편리하다고 생각합니다.

from collections import OrderedDict
def partition(iterable,*conditions):
    '''Returns a list with the elements that satisfy each of condition.
       Conditions are assumed to be exclusive'''
    d= OrderedDict((i,list())for i in range(len(conditions)))        
    for e in iterable:
        for i,condition in enumerate(conditions):
            if condition(e):
                d[i].append(e)
                break                    
    return d.values()

예를 들어 :

ints,floats,other = partition([2, 3.14, 1, 1.69, [], None],
                              lambda x: isinstance(x, int), 
                              lambda x: isinstance(x, float),
                              lambda x: True)

print " ints: {}\n floats:{}\n other:{}".format(ints,floats,other)

 ints: [2, 1]
 floats:[3.14, 1.69]
 other:[[], None]

요소가 여러 조건을 만족할 수 있으면 분리를 제거하십시오.


3
def partition(pred, iterable):
    'Use a predicate to partition entries into false entries and true entries'
    # partition(is_odd, range(10)) --> 0 2 4 6 8   and  1 3 5 7 9
    t1, t2 = tee(iterable)
    return filterfalse(pred, t1), filter(pred, t2)

이것을 확인 하십시오


3

때로는 목록 이해가 최선의 방법이 아닌 것처럼 보입니다!

사람들 이이 주제에 대한 답변을 기반으로 작은 테스트를 수행하여 무작위로 생성 된 목록에서 테스트했습니다. 다음은 목록 생성입니다 (아마도 더 좋은 방법이 있지만 요점이 아닙니다).

good_list = ('.jpg','.jpeg','.gif','.bmp','.png')

import random
import string
my_origin_list = []
for i in xrange(10000):
    fname = ''.join(random.choice(string.lowercase) for i in range(random.randrange(10)))
    if random.getrandbits(1):
        fext = random.choice(good_list)
    else:
        fext = "." + ''.join(random.choice(string.lowercase) for i in range(3))

    my_origin_list.append((fname + fext, random.randrange(1000), fext))

우리가 간다

# Parand
def f1():
    return [e for e in my_origin_list if e[2] in good_list], [e for e in my_origin_list if not e[2] in good_list]

# dbr
def f2():
    a, b = list(), list()
    for e in my_origin_list:
        if e[2] in good_list:
            a.append(e)
        else:
            b.append(e)
    return a, b

# John La Rooy
def f3():
    a, b = list(), list()
    for e in my_origin_list:
        (b, a)[e[2] in good_list].append(e)
    return a, b

# Ants Aasma
def f4():
    l1, l2 = tee((e[2] in good_list, e) for e in my_origin_list)
    return [i for p, i in l1 if p], [i for p, i in l2 if not p]

# My personal way to do
def f5():
    a, b = zip(*[(e, None) if e[2] in good_list else (None, e) for e in my_origin_list])
    return list(filter(None, a)), list(filter(None, b))

# BJ Homer
def f6():
    return filter(lambda e: e[2] in good_list, my_origin_list), filter(lambda e: not e[2] in good_list, my_origin_list)

cmpthese 함수를 사용하면 최상의 결과는 dbr answer입니다.

f1     204/s  --    -5%   -14%   -15%   -20%   -26%
f6     215/s     6%  --    -9%   -11%   -16%   -22%
f3     237/s    16%    10%  --    -2%    -7%   -14%
f4     240/s    18%    12%     2%  --    -6%   -13%
f5     255/s    25%    18%     8%     6%  --    -8%
f2     277/s    36%    29%    17%    15%     9%  --

업데이트 된 벤치 마크로 더 빠른 기능을 제공 합니다 .
ChaimG

2

이 문제에 대한 또 다른 해결책. 가능한 빠른 솔루션이 필요했습니다. 즉, 목록을 한 번만 반복하고 결과 목록 중 하나에 데이터를 추가하기위한 O (1)이 바람직합니다. 이것은 훨씬 짧은 것을 제외하고 sastanin이 제공하는 솔루션과 매우 유사합니다 .

from collections import deque

def split(iterable, function):
    dq_true = deque()
    dq_false = deque()

    # deque - the fastest way to consume an iterator and append items
    deque((
      (dq_true if function(item) else dq_false).append(item) for item in iterable
    ), maxlen=0)

    return dq_true, dq_false

그런 다음 다음과 같은 방식으로 기능을 사용할 수 있습니다.

lower, higher = split([0,1,2,3,4,5,6,7,8,9], lambda x: x < 5)

selected, other = split([0,1,2,3,4,5,6,7,8,9], lambda x: x in {0,4,9})

당신이 결과하지 좋은 경우 deque개체를 쉽게로 변환 할 수 있습니다 list, set무엇이든 당신처럼 (예를 들어 list(lower)). 변환은 목록의 구성보다 훨씬 빠릅니다.

이 방법은 항목의 순서와 복제본을 유지합니다.


2

예를 들어, 짝수와 홀수로 목록 나누기

arr = range(20)
even, odd = reduce(lambda res, next: res[next % 2].append(next) or res, arr, ([], []))

또는 일반적으로 :

def split(predicate, iterable):
    return reduce(lambda res, e: res[predicate(e)].append(e) or res, iterable, ([], []))

장점 :

  • 가능한 가장 짧은 방법
  • 술어는 각 요소에 한 번만 적용됩니다.

단점

  • 기능적 프로그래밍 패러다임에 대한 지식 필요

2

@gnibbler의 위대한 (그러나 간결한!) 답변 에서 영감을 얻은 우리는 여러 파티션에 매핑하는 방법을 적용 할 수 있습니다.

from collections import defaultdict

def splitter(l, mapper):
    """Split an iterable into multiple partitions generated by a callable mapper."""

    results = defaultdict(list)

    for x in l:
        results[mapper(x)] += [x]

    return results

그런 splitter다음 다음과 같이 사용할 수 있습니다.

>>> l = [1, 2, 3, 4, 2, 3, 4, 5, 6, 4, 3, 2, 3]
>>> split = splitter(l, lambda x: x % 2 == 0)  # partition l into odds and evens
>>> split.items()
>>> [(False, [1, 3, 3, 5, 3, 3]), (True, [2, 4, 2, 4, 6, 4, 2])]

이것은 더 복잡한 매핑 (및 반복자)이있는 두 개 이상의 파티션에서 작동합니다.

>>> import math
>>> l = xrange(1, 23)
>>> split = splitter(l, lambda x: int(math.log10(x) * 5))
>>> split.items()
[(0, [1]),
 (1, [2]),
 (2, [3]),
 (3, [4, 5, 6]),
 (4, [7, 8, 9]),
 (5, [10, 11, 12, 13, 14, 15]),
 (6, [16, 17, 18, 19, 20, 21, 22])]

또는 사전을 사용하여 매핑 :

>>> map = {'A': 1, 'X': 2, 'B': 3, 'Y': 1, 'C': 2, 'Z': 3}
>>> l = ['A', 'B', 'C', 'C', 'X', 'Y', 'Z', 'A', 'Z']
>>> split = splitter(l, map.get)
>>> split.items()
(1, ['A', 'Y', 'A']), (2, ['C', 'C', 'X']), (3, ['B', 'Z', 'Z'])]

... 이것은 @ alan-isaac가 이미 대답 한 것과 기본적으로 동일하다는 것을 알았습니다.
Josh Bode

2
bad = []
good = [x for x in mylist if x in goodvals or bad.append(x)]

추가는 없음을 반환하므로 작동합니다.


1

성능을 위해을 시도하십시오 itertools.

itertools의 모듈은 빠른 일련의 핵심, 그 자체로 또는 조합에 유용한 메모리 효율적인 도구를 표준화. 이들은 함께 "반복 대수"를 형성하여 순수 Python에서 간결하고 효율적으로 특수 도구를 구성 할 수 있습니다.

itertools.ifilter 또는 imap을 참조하십시오 .

itertools.ifilter (조건 자, 반복 가능)

술어가 True 인 요소 만 리턴하여 반복 가능한 요소를 필터링하는 반복자를 작성하십시오.


ifilter / imap (및 일반적으로 생성기)은 꽤 느립니다 ... 일반적으로 프로파일 링에서 [x for x in a if x > 50000]100000 정수의 간단한 배열 (random.shuffle을 통해) 과 같은 목록 이해를 취하면 filter(lambda x: x> 50000, a)2 배의 시간이 ifilter(lambda x: x> 50000, a); list(result)걸리고 걸립니다. 약 2.3 배 이상하지만 사실입니다.
Corley Brigman

1

때로는 목록의 다른 절반이 필요하지 않을 수도 있습니다. 예를 들면 다음과 같습니다.

import sys
from itertools import ifilter

trustedPeople = sys.argv[1].split(',')
newName = sys.argv[2]

myFriends = ifilter(lambda x: x.startswith('Shi'), trustedPeople)

print '%s is %smy friend.' % (newName, newName not in myFriends 'not ' or '')

1

이것이 가장 빠른 방법입니다.

if elsedbr의 답변과 같은을 사용 하지만 먼저 세트를 만듭니다. 세트는 O (m * n)에서 O (log m) + O (n)까지 작업 수를 줄여서 속도가 45 % 이상 증가합니다.

good_list_set = set(good_list)  # 45% faster than a tuple.

good, bad = [], []
for item in my_origin_list:
    if item in good_list_set:
        good.append(item)
    else:
        bad.append(item)

조금 더 짧습니다 :

good_list_set = set(good_list)  # 45% faster than a tuple.

good, bad = [], []
for item in my_origin_list:
    out = good if item in good_list_set else bad
    out.append(item)

벤치 마크 결과 :

filter_BJHomer                  80/s       --   -3265%   -5312%   -5900%   -6262%   -7273%   -7363%   -8051%   -8162%   -8244%
zip_Funky                       118/s    4848%       --   -3040%   -3913%   -4450%   -5951%   -6085%   -7106%   -7271%   -7393%
two_lst_tuple_JohnLaRoy         170/s   11332%    4367%       --   -1254%   -2026%   -4182%   -4375%   -5842%   -6079%   -6254%
if_else_DBR                     195/s   14392%    6428%    1434%       --    -882%   -3348%   -3568%   -5246%   -5516%   -5717%
two_lst_compr_Parand            213/s   16750%    8016%    2540%     967%       --   -2705%   -2946%   -4786%   -5083%   -5303%
if_else_1_line_DanSalmo         292/s   26668%   14696%    7189%    5033%    3707%       --    -331%   -2853%   -3260%   -3562%
tuple_if_else                   302/s   27923%   15542%    7778%    5548%    4177%     343%       --   -2609%   -3029%   -3341%
set_1_line                      409/s   41308%   24556%   14053%   11035%    9181%    3993%    3529%       --    -569%    -991%
set_shorter                     434/s   44401%   26640%   15503%   12303%   10337%    4836%    4345%     603%       --    -448%
set_if_else                     454/s   46952%   28358%   16699%   13349%   11290%    5532%    5018%    1100%     469%       --

Python 3.7의 전체 벤치 마크 코드 (FunkySayu에서 수정) :

good_list = ['.jpg','.jpeg','.gif','.bmp','.png']

import random
import string
my_origin_list = []
for i in range(10000):
    fname = ''.join(random.choice(string.ascii_lowercase) for i in range(random.randrange(10)))
    if random.getrandbits(1):
        fext = random.choice(list(good_list))
    else:
        fext = "." + ''.join(random.choice(string.ascii_lowercase) for i in range(3))

    my_origin_list.append((fname + fext, random.randrange(1000), fext))

# Parand
def two_lst_compr_Parand(*_):
    return [e for e in my_origin_list if e[2] in good_list], [e for e in my_origin_list if not e[2] in good_list]

# dbr
def if_else_DBR(*_):
    a, b = list(), list()
    for e in my_origin_list:
        if e[2] in good_list:
            a.append(e)
        else:
            b.append(e)
    return a, b

# John La Rooy
def two_lst_tuple_JohnLaRoy(*_):
    a, b = list(), list()
    for e in my_origin_list:
        (b, a)[e[2] in good_list].append(e)
    return a, b

# # Ants Aasma
# def f4():
#     l1, l2 = tee((e[2] in good_list, e) for e in my_origin_list)
#     return [i for p, i in l1 if p], [i for p, i in l2 if not p]

# My personal way to do
def zip_Funky(*_):
    a, b = zip(*[(e, None) if e[2] in good_list else (None, e) for e in my_origin_list])
    return list(filter(None, a)), list(filter(None, b))

# BJ Homer
def filter_BJHomer(*_):
    return list(filter(lambda e: e[2] in good_list, my_origin_list)), list(filter(lambda e: not e[2] in good_list,                                                                             my_origin_list))

# ChaimG's answer; as a list.
def if_else_1_line_DanSalmo(*_):
    good, bad = [], []
    for e in my_origin_list:
        _ = good.append(e) if e[2] in good_list else bad.append(e)
    return good, bad

# ChaimG's answer; as a set.
def set_1_line(*_):
    good_list_set = set(good_list)
    good, bad = [], []
    for e in my_origin_list:
        _ = good.append(e) if e[2] in good_list_set else bad.append(e)
    return good, bad

# ChaimG set and if else list.
def set_shorter(*_):
    good_list_set = set(good_list)
    good, bad = [], []
    for e in my_origin_list:
        out = good if e[2] in good_list_set else bad
        out.append(e)
    return good, bad

# ChaimG's best answer; if else as a set.
def set_if_else(*_):
    good_list_set = set(good_list)
    good, bad = [], []
    for e in my_origin_list:
        if e[2] in good_list_set:
            good.append(e)
        else:
            bad.append(e)
    return good, bad

# ChaimG's best answer; if else as a set.
def tuple_if_else(*_):
    good_list_tuple = tuple(good_list)
    good, bad = [], []
    for e in my_origin_list:
        if e[2] in good_list_tuple:
            good.append(e)
        else:
            bad.append(e)
    return good, bad

def cmpthese(n=0, functions=None):
    results = {}
    for func_name in functions:
        args = ['%s(range(256))' % func_name, 'from __main__ import %s' % func_name]
        t = Timer(*args)
        results[func_name] = 1 / (t.timeit(number=n) / n) # passes/sec

    functions_sorted = sorted(functions, key=results.__getitem__)
    for f in functions_sorted:
        diff = []
        for func in functions_sorted:
            if func == f:
                diff.append("--")
            else:
                diff.append(f"{results[f]/results[func]*100 - 100:5.0%}")
        diffs = " ".join(f'{x:>8s}' for x in diff)

        print(f"{f:27s} \t{results[f]:,.0f}/s {diffs}")


if __name__=='__main__':
    from timeit import Timer
cmpthese(1000, 'two_lst_compr_Parand if_else_DBR two_lst_tuple_JohnLaRoy zip_Funky filter_BJHomer if_else_1_line_DanSalmo set_1_line set_if_else tuple_if_else set_shorter'.split(" "))

0

영리한 것을 고집한다면 Winden의 솔루션과 약간의 가짜 영리함을 취할 수 있습니다.

def splay(l, f, d=None):
  d = d or {}
  for x in l: d.setdefault(f(x), []).append(x)
  return d

3
"d 또는 {}"는 약간 위험합니다. 빈 받아쓰기가 전달되면 변경되지 않습니다.
Brian

사실, 그러나 반환됩니다. 실제로, 이것은 코드에 더 영리한 것을 추가하고 싶지 않은 이유의 완벽한 예입니다 . - P
앤더스 Eurenius

0

여기에 이미 몇 가지 해결책이 있지만 또 다른 방법은 다음과 같습니다.

anims = []
images = [f for f in files if (lambda t: True if f[2].lower() in IMAGE_TYPES else anims.append(t) and False)(f)]

목록을 한 번만 반복하면 좀 더 파이썬처럼 보이므로 읽을 수 있습니다.

>>> files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ('file1.bmp', 33L, '.bmp')]
>>> IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
>>> anims = []
>>> images = [f for f in files if (lambda t: True if f[2].lower() in IMAGE_TYPES else anims.append(t) and False)(f)]
>>> print '\n'.join([str(anims), str(images)])
[('file2.avi', 999L, '.avi')]
[('file1.jpg', 33L, '.jpg'), ('file1.bmp', 33L, '.bmp')]
>>>

0

목록을 필터링하는 것과 술어의 평가를 분리하는 2 패스 접근법을 사용합니다.

def partition(pred, iterable):
    xs = list(zip(map(pred, iterable), iterable))
    return [x[1] for x in xs if x[0]], [x[1] for x in xs if not x[0]]

성능면에서 (의 pred각 멤버에 대해 한 번만 평가하는 것 외에도) 이것에 대해 좋은 iterable점은 많은 논리를 인터프리터에서 고도로 최적화 된 반복 및 매핑 코드로 이동한다는 것입니다. 이 답변에 설명 것처럼 긴 반복 가능 항목에 대한 반복 속도를 높일 수 있습니다 .

표현 측면에서는 이해력 및 매핑과 같은 표현 관용구를 활용합니다.


0

해결책

from itertools import tee

def unpack_args(fn):
    return lambda t: fn(*t)

def separate(fn, lx):
    return map(
        unpack_args(
            lambda i, ly: filter(
                lambda el: bool(i) == fn(el),
                ly)),
        enumerate(tee(lx, 2)))

테스트

[even, odd] = separate(
    lambda x: bool(x % 2),
    [1, 2, 3, 4, 5])
print(list(even) == [2, 4])
print(list(odd) == [1, 3, 5])

0

외부 라이브러리를 사용하는 것이 마음에 들지 않으면이 작업을 적극적으로 구현한다는 것을 알고 있습니다.

>>> files = [ ('file1.jpg', 33, '.jpg'), ('file2.avi', 999, '.avi')]
>>> IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
  • iteration_utilities.partition:

    >>> from iteration_utilities import partition
    >>> notimages, images = partition(files, lambda x: x[2].lower() in IMAGE_TYPES)
    >>> notimages
    [('file2.avi', 999, '.avi')]
    >>> images
    [('file1.jpg', 33, '.jpg')]
  • more_itertools.partition

    >>> from more_itertools import partition
    >>> notimages, images = partition(lambda x: x[2].lower() in IMAGE_TYPES, files)
    >>> list(notimages)  # returns a generator so you need to explicitly convert to list.
    [('file2.avi', 999, '.avi')]
    >>> list(images)
    [('file1.jpg', 33, '.jpg')]

0

이것이 좋은 접근 방법인지 확실하지 않지만이 방법으로도 수행 할 수 있습니다

IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi')]
images, anims = reduce(lambda (i, a), f: (i + [f], a) if f[2] in IMAGE_TYPES else (i, a + [f]), files, ([], []))

0

목록이 그룹 및 간헐적 구분자로 구성된 경우 다음을 사용할 수 있습니다.

def split(items, p):
    groups = [[]]
    for i in items:
        if p(i):
            groups.append([])
        groups[-1].append(i)
    return groups

용법:

split(range(1,11), lambda x: x % 3 == 0)
# gives [[1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

0
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f not in images]

예를 들어 조건이 더 길면 좋습니다. 독자는 부정적인 조건과 다른 모든 사례를 포착하는지 여부를 파악할 필요가 없습니다.


0

또 다른 대답은 짧지 만 "악"입니다 (목록 이해 부작용).

digits = list(range(10))
odd = [x.pop(i) for i, x in enumerate(digits) if x % 2]

>>> odd
[1, 3, 5, 7, 9]

>>> digits
[0, 2, 4, 6, 8]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.