リストとフィルタリング機能があるとしましょう。のようなものを使用して
_>>> filter(lambda x: x > 10, [1,4,12,7,42])
[12, 42]
_
基準に一致する要素を取得できます。一致する要素の1つ、残りの要素の1つ、2つのリストを出力する関数を使用できますか? filter()
関数を2回呼び出すこともできますが、それは少し醜いです:)
編集:要素の順序を保存する必要があり、同じ要素を複数回使用する場合があります。
これを試して:
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のドキュメントからのものです。Python 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, ([], []))
これは2番目の編集ですが、重要だと思います。
def partition(l, p):
return reduce(lambda x, y: x[not p(y)].append(y) or x, l, ([], []))
2番目と3番目は、反復する1つのアッパーと同じくらい高速ですが、コードは少なくなります。
ここで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]}
リストに重複する要素がない場合は、間違いなく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])
または包括的なリストで行うことができます:
>>> no_b = [i for i in a if i not in b]
注:これは関数ではありませんが、最初のfitler()の結果を知るだけで、フィルター条件をあまり満たさなかった要素を推定できます。
受け入れられ、最も投票された回答 [1] by Mark Byers
_def partition(pred, iterable): trues = [] falses = [] for item in iterable: if pred(item): trues.append(item) else: falses.append(item) return trues, falses
_
最もシンプルで最速です。
提案されたさまざまなアプローチは、大きく3つのカテゴリに分類できます。
lis.append
_による単純なリスト操作、2タプルのリストを返す、lis.append
_関数のアプローチによって仲介され、2タプルのリストを返します。itertools
の詳細なドキュメントに記載されている標準的なレシピを使用して、大まかに言えば2タプルのジェネレータを返します。ここでは、3つの手法のバニラ実装に従います。最初は機能的アプローチ、次にitertools
、最終的には2つの異なる直接リスト操作の実装です。代わりにFalse
を使用する方法はゼロ、True
は1つのトリックです。
これは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
アプローチをテストする際の問題を克服するために、2013年10月31日6:17に joeln によって報告されました。
ナンセンス。
filterfalse
およびfilter
でジェネレーターを構築するのにかかる時間を計算しましたが、入力を反復したことも、pred
を一度呼び出したこともありません。itertools
レシピの利点は、リストを具体化しないか、必要以上に入力を先読みしないことです。pred
を2倍の頻度で呼び出し、Byersの2倍近くの時間がかかります。
私は、異なるパーティション関数によって返された2つのイテラブルの要素のすべてのカップルをインスタンス化するだけのvoidループを考えました。
最初に、2つの固定リストを使用して、暗黙のオーバーロードを理解します(非常に便利な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)]
トリックの使用は、えーと、役に立たない。すべてのステップでデータ構造にインデックスを付ける必要があるため、slightペナルティが与えられる—ただし、パイソン化で減少する文化の生存者を説得したいかどうかを知るのはいいことです。
代替のappend
実装と同等のoperatively機能的なアプローチは、おそらく追加の(w/r(述語評価)の各リスト要素の関数呼び出し。
itertools
アプローチには、(一般的な)利点があります。❶潜在的に大きなリストがインスタンス化されず、❷コンシューマーループから抜け出した場合、入力リストは完全には処理されませんが、適用する必要があるため、使用すると遅くなります。 tee
の両端の述語
私は Marii で公開されたobject.mutate() or object
イディオムに恋に落ちました その答え 問題への機能的アプローチを示しています—恐れています遅かれ早かれ、私はそれを乱用するつもりです。
[1] 2017年9月14日現在、承認され、ほとんどの投票が行われました。もちろん、私はこの私の答えに最大の期待を寄せています!
私はまさにこの要件を持っていました。 itertoolsのレシピは、データを2回別々にパスする必要があるため、あまり熱心ではありません。これが私の実装です:
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])
from itertools import ifilterfalse
def filter2(predicate, iterable):
return filter(predicate, iterable), list(ifilterfalse(predicate, iterable))
Django.utils.functional.partition
ソリューション:
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
私の意見では、これはここで提示される最もエレガントなソリューションです。
この部分は文書化されていません。ソースコードのみが https://docs.djangoproject.com/en/dev/_modules/Django/utils/functional/ にあります。
誰もが彼らのソリューションが最高だと思っているようですので、私はtimeitを使用してそれらすべてをテストすることにしました。私の述語関数として「def is_odd(x):return x&1」を使用し、反復可能関数として「xrange(1000)」を使用しました。これが私の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
そして、これが私のテストの結果です:
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
それらはすべて互いに同等です。ここで、Pythonのドキュメントに記載されている例を使用してみましょう。
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)
これは少し速いようです。
100000 loops, best of 3: 2.58 usec per loop
Itertoolsのサンプルコードは、すべてのコーナーを少なくとも100倍上回っています。道徳は、車輪を再発明し続けないことです。
ターゲットリストに追加するための簡潔なコード
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]
すでに良い答えがたくさんあります。私はこれを使いたいです:
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 ) )