programing

두 개의 출력 목록 (즉, 목록의 분할)을 얻는 filter ()와 동등한 파이썬

sourcetip 2021. 1. 14. 23:43
반응형

두 개의 출력 목록 (즉, 목록의 분할)을 얻는 filter ()와 동등한 파이썬


목록과 필터링 기능이 있다고 가정 해 보겠습니다. 같은 것을 사용하여

>>> filter(lambda x: x > 10, [1,4,12,7,42])
[12, 42]

기준과 일치하는 요소를 얻을 수 있습니다. 두 개의 목록, 일치하는 요소 중 하나, 나머지 요소 중 하나를 출력하는 함수가 있습니까? filter()함수를 두 번 호출 할 수 있지만 좀 못 생겼습니다. :)

편집 : 요소의 순서는 보존되어야하며 동일한 요소를 여러 번 가질 수 있습니다.


이 시도:

def partition(pred, iterable):
    trues = []
    falses = []
    for item in iterable:
        if pred(item):
            trues.append(item)
        else:
            falses.append(item)
    return trues, falses

용법:

>>> trues, falses = partition(lambda x: x > 10, [1,4,12,7,42])
>>> trues
[12, 42]
>>> falses
[1, 4, 7]

itertools 레시피 에도 구현 제안이 있습니다 .

from itertools import filterfalse, tee

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)

레시피는 Python 3.x 문서에서 가져옵니다. 파이썬 2.X에서 filterfalse라고합니다 ifilterfalse.


>>> def partition(l, p):
...     return reduce(lambda x, y: (x[0]+[y], x[1]) if p(y) else (x[0], x[1]+[y]), l,  ([], []))
... 
>>> partition([1, 2, 3, 4, 5], lambda x: x < 3)
([1, 2], [3, 4, 5])

위 코드의 좀 더 못 생겼지 만 더 빠른 버전입니다.

def partition(l, p):
    return reduce(lambda x, y: x[0].append(y) or x if p(y) else x[1].append(y) or x, l,  ([], []))

이것은 두 번째 편집이지만 중요하다고 생각합니다.

 def partition(l, p):
     return reduce(lambda x, y: x[not p(y)].append(y) or x, l, ([], []))

두 번째와 세 번째는 반복되는 상위만큼 빠르지 만 코드가 적습니다.


groupby가 여기에 더 관련성이 있다고 생각합니다.

http://docs.python.org/library/itertools.html#itertools.groupby

예를 들어, 목록을 홀수 및 짝수로 분할 (또는 임의의 수의 그룹이 될 수 있음) :

>>> l=range(6)
>>> key=lambda x: x % 2 == 0
>>> from itertools import groupby
>>> {k:list(g) for k,g in groupby(sorted(l,key=key),key=key)}
    {False: [1, 3, 5], True: [0, 2, 4]}

TL; DR

받아, 대부분의 대답은 투표 에 의해 [1]를 마크 바이어스

def partition(pred, iterable):
    trues = []
    falses = []
    for item in iterable:
        if pred(item):
            trues.append(item)
        else:
            falses.append(item)
    return trues, falses

가장 간단하고 빠릅니다.

다양한 접근 방식 벤치마킹

제안 된 다양한 접근 방식은 크게 세 가지 범주로 분류 할 수 있습니다.

  1. 를 통한 간단한 목록 조작 lis.append, 목록 의 2- 튜플 반환,
  2. lis.append 2 튜플의 목록을 반환하는 기능적 접근 방식에 의해 중재되고,
  3. itertools정밀한 문서에 제공된 표준 레시피를 사용하여 느슨하게 말하면 생성기의 2- 튜플을 반환합니다.

다음은 세 가지 기술의 바닐라 구현을 따릅니다. 첫 번째는 기능적 접근 방식이고, 그 다음에 itertools는 직접 목록 조작의 두 가지 다른 구현이 있습니다. 대안은 Falseis 0을 사용하는 True것이 하나의 트릭입니다.

이 Python3 것을 주 - 따라서 reduce에서 온다 functools- 그 OP는 같은 튜플 요청 (positives, negatives)하지만 내 구현 모든 수익을 (negatives, positives)...

$ ipython
Python 3.6.2 |Continuum Analytics, Inc.| (default, Jul 20 2017, 13:51:32) 
Type 'copyright', 'credits' or 'license' for more information
IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import functools
   ...: 
   ...: def partition_fu(p, l, r=functools.reduce):
   ...:     return r(lambda x, y: x[p(y)].append(y) or x, l, ([], []))
   ...: 

In [2]: import itertools
   ...: 
   ...: def partition_it(pred, iterable,
   ...:               filterfalse=itertools.filterfalse,
   ...:               tee=itertools.tee):
   ...:     t1, t2 = tee(iterable)
   ...:     return filterfalse(pred, t1), filter(pred, t2)
   ...: 

In [3]: def partition_li(p, l):
   ...:     a, b = [], []
   ...:     for n in l:
   ...:         if p(n):
   ...:             b.append(n)
   ...:         else:
   ...:             a.append(n)
   ...:     return a, b
   ...: 

In [4]: def partition_li_alt(p, l):
   ...:     x = [], []
   ...:     for n in l: x[p(n)].append(n)
   ...:     return x
   ...: 

우리는 작동 할 목록과 목록에 적용 할 술어가 필요합니다.

In [5]: p = lambda n:n%2

In [6]: five, ten = range(50000), range(100000)

itertools접근 방식 테스트의 문제를 극복하기 위해 joeln이 2013 년 10 월 31 일 6:17 에보 고했습니다.

무의미한 말. filterfalse에서 생성기를 구성하는 데 걸리는 시간을 계산 filter했지만 입력을 반복하거나 pred한 번 호출하지 않았습니다 ! itertools레시피 의 장점은 목록을 구체화하지 않거나 필요 이상으로 입력을 더 앞쪽으로 보지 않는다는 것입니다. 그것은 pred두 배 더 자주 전화 하고 Byers et al.

나는 서로 다른 파티션 함수에 의해 반환 된 두 개의 iterables에서 모든 요소 쌍을 인스턴스화하는 void 루프를 생각했습니다.

먼저 두 개의 고정 된 목록을 사용하여 암시 된 과부하에 대한 아이디어를 얻습니다 (매우 편리한 IPython의 마법 사용 %timeit).

In [7]: %timeit for e, o in zip(five, five): pass
4.21 ms ± 39.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

다음으로 서로 다른 구현을 사용합니다.

In [8]: %timeit for e, o in zip(*partition_fu(p, ten)): pass
53.9 ms ± 112 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [9]: %timeit for e, o in zip(*partition_it(p, ten)): pass
44.5 ms ± 3.84 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [10]: %timeit for e, o in zip(*partition_li(p, ten)): pass
36.3 ms ± 101 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [11]: %timeit for e, o in zip(*partition_li_alt(p, ten)): pass
37.3 ms ± 109 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [12]:

코멘트

가장 단순한 접근 방식도 가장 빠른 접근 방식입니다.

x[p(n)]트릭을 사용하는 것은 쓸모가 없습니다. 모든 단계에서 데이터 구조를 인덱싱해야하므로 약간의 벌금이 부과됩니다. 그러나 파이썬 화에서 쇠퇴하는 문화의 생존자를 설득하고 싶은지 아는 것이 좋습니다.

대체 구현 작동 적으로 동일한 기능적 접근 방식 append은 ~ 50 % 더 느립니다. 아마도 각 목록 요소에 대해 추가 (조건부 평가 포함) 함수 호출이 있기 때문일 수 있습니다.

itertools접근 방식에는 ❶ 잠재적으로 큰 목록이 인스턴스화되지 않고 ❷ 소비자 루프에서 벗어나면 입력 목록이 완전히 처리되지 않는다는 (관습적인) 장점이 있지만, 사용하면 술어를 적용해야하므로 속도가 느립니다. 의 양쪽 끝에tee

곁에

I've fallen in love with the object.mutate() or object idiom that was exposed by Marii in their answer showing a functional approach to the problem — I'm afraid that, sooner or later, I'm going to abuse it.

Footnotes

[1] Accepted and most voted as today, Sep 14 2017 — but of course I have the highest hopes for this answer of mine!


If you don't have duplicate element in your list you can definitely use set:

>>> a = [1,4,12,7,42]
>>> b = filter(lambda x: x > 10, [1,4,12,7,42])
>>> no_b = set(a) - set(b)
set([1, 4, 7])

or you can do by a list comprehensible:

>>> no_b = [i for i in a if i not in b]

N.B: it's not a function but just knowing the first fitler() result you can deduce the element that didn't much your filter criterion .


from itertools import ifilterfalse

def filter2(predicate, iterable):
    return filter(predicate, iterable), list(ifilterfalse(predicate, iterable))

I just had exactly this requirement. I'm not keen on the itertools recipe since it involves two separate passes through the data. Here's my implementation:

def filter_twoway(test, data):
    "Like filter(), but returns the passes AND the fails as two separate lists"
    collected = {True: [], False: []}
    for datum in data:
        collected[test(datum)].append(datum)
    return (collected[True], collected[False])

You can look at django.utils.functional.partition solution:

def partition(predicate, values):
    """
    Splits the values into two sets, based on the return value of the function
    (True/False). e.g.:

        >>> partition(lambda x: x > 3, range(5))
        [0, 1, 2, 3], [4]
    """
    results = ([], [])
    for item in values:
        results[predicate(item)].append(item)
    return results

In my opinion it's the most elegant solution presented here.

This part is not documented, only source code can be found on https://docs.djangoproject.com/en/dev/_modules/django/utils/functional/


Everyone seems to think that their solution is the best, so I decided to use timeit to test all of them. I used "def is_odd(x): return x & 1" as my predicate function, and "xrange(1000)" as the iterable. Here is my version of Python:

Python 2.7.3 (v2.7.3:70274d53c1dd, Apr  9 2012, 20:52:43) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin

And here are the results of my testing:

Mark Byers
1000 loops, best of 3: 325 usec per loop

cldy
1000 loops, best of 3: 1.96 msec per loop

Dan S
1000 loops, best of 3: 412 usec per loop

TTimo
1000 loops, best of 3: 503 usec per loop

Those are all comparable to each other. Now, let's try using the example given in the Python documentation.

import itertools

def partition(pred, iterable,
              # Optimized by replacing global lookups with local variables
              # defined as default values.
              filter=itertools.ifilter,
              filterfalse=itertools.ifilterfalse,
              tee=itertools.tee):
    '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)

This seems to be a bit faster.

100000 loops, best of 3: 2.58 usec per loop

The itertools example code beats all comers by a factor of at least 100! The moral is, don't keep re-inventing the wheel.


Plenty of good answers already. I like to use this:

def partition( pred, iterable ):
    def _dispatch( ret, v ):
        if ( pred( v ) ):
            ret[0].append( v )
        else:
            ret[1].append( v )
        return ret
    return reduce( _dispatch, iterable, ( [], [] ) )

if ( __name__ == '__main__' ):
    import random
    seq = range( 20 )
    random.shuffle( seq )
    print( seq )
    print( partition( lambda v : v > 10, seq ) )

Concise code for appending to target list

    def partition(cond,inputList):
        a,b= [],[]
        for item in inputList:
            target = a if cond(item) else b
            target.append(item)
        return a, b


    >>> a, b= partition(lambda x: x > 10,[1,4,12,7,42])
    >>> a
    [12, 42]
    >>> b
    [1, 4, 7]

ReferenceURL : https://stackoverflow.com/questions/4578590/python-equivalent-of-filter-getting-two-output-lists-i-e-partition-of-a-list

반응형