두 개의 출력 목록 (즉, 목록의 분할)을 얻는 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
가장 간단하고 빠릅니다.
다양한 접근 방식 벤치마킹
제안 된 다양한 접근 방식은 크게 세 가지 범주로 분류 할 수 있습니다.
- 를 통한 간단한 목록 조작
lis.append
, 목록 의 2- 튜플 반환, lis.append
2 튜플의 목록을 반환하는 기능적 접근 방식에 의해 중재되고,itertools
정밀한 문서에 제공된 표준 레시피를 사용하여 느슨하게 말하면 생성기의 2- 튜플을 반환합니다.
다음은 세 가지 기술의 바닐라 구현을 따릅니다. 첫 번째는 기능적 접근 방식이고, 그 다음에 itertools
는 직접 목록 조작의 두 가지 다른 구현이 있습니다. 대안은 False
is 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
'programing' 카테고리의 다른 글
Objective-C에서 연산자를 오버로드 할 수 있습니까? (0) | 2021.01.14 |
---|---|
MySQL "주요 효율성"이란? (0) | 2021.01.14 |
참조 설정 대 출퇴근 대 변경 (0) | 2021.01.14 |
R에서 x 축 눈금으로 플로팅 할 실제 x 축 값을 지정하는 방법 (0) | 2021.01.14 |
두 변수에 동일한 참조가 있는지 확인하는 방법은 무엇입니까? (0) | 2021.01.14 |