2つのリストがあるl1
としましょうl2
。not inのl1 - l2
すべての要素を返す、実行したい。l1
l2
これを行うための単純なループのアプローチを考えることができますが、それは本当に非効率的です。これを行うためのpythonicで効率的な方法は何ですか?
例として、私が持っている場合l1 = [1,2,6,8] and l2 = [2,3,5,8]
、l1 - l2
返す必要があります[1,6]
2つのリストがあるl1
としましょうl2
。not inのl1 - l2
すべての要素を返す、実行したい。l1
l2
これを行うための単純なループのアプローチを考えることができますが、それは本当に非効率的です。これを行うためのpythonicで効率的な方法は何ですか?
例として、私が持っている場合l1 = [1,2,6,8] and l2 = [2,3,5,8]
、l1 - l2
返す必要があります[1,6]
回答:
Pythonには、List Comprehensionsと呼ばれる言語機能があり、このようなことを非常に簡単にするのに最適です。次のステートメントは、必要な処理を正確に実行し、結果をに格納しますl3
。
l3 = [x for x in l1 if x not in l2]
l3
含まれます[1, 6]
。
in
演算子はリスト上ではそれほど効率的ではありません。 in
リストではO(n)、in
セットではO(1)です。ただし、数千以上の要素に到達するまで、違いに気付くことはほとんどありません。
l3 = [x for x in l1 if x not in set(l2)]
?set(l2)
一度以上呼ばれると確信しています。
l2s = set(l2)
してから発声することもできますl3 = [x for x in l1 if x not in l2s]
。少し簡単です。
1つの方法は、セットを使用することです。
>>> set([1,2,6,8]) - set([2,3,5,8])
set([1, 6])
l1
、からの重複も削除されます。これは、望ましくない副作用となる場合があります。
timeit.timeit('a = [1,2,3,4]; b = [1,3]; c = [i for i in a if a not in b]', number=100000) -> 0.12061533199999985
timeit.timeit('a = {1,2,3,4}; b = {1,3}; c = a - b', number=100000) -> 0.04106225999998969
。したがって、パフォーマンスが重要な要素である場合、この回答の方が適切な場合があります(重複や順序を気にしない場合も同様)
別の方法として、ラムダ式を使用filter
して目的の結果を取得することもできます。例えば:
>>> l1 = [1,2,6,8]
>>> l2 = set([2,3,5,8])
# v `filter` returns the a iterator object. Here I'm type-casting
# v it to `list` in order to display the resultant value
>>> list(filter(lambda x: x not in l2, l1))
[1, 6]
性能比較
ここで、ここで述べたすべての回答のパフォーマンスを比較しています。予想通り、Arkkuの set
ベースの操作は最速です。
Arkkuのセットの違い -最初(ループあたり0.124 usec)
mquadri$ python -m timeit -s "l1 = set([1,2,6,8]); l2 = set([2,3,5,8]);" "l1 - l2"
10000000 loops, best of 3: 0.124 usec per loop
Daniel Prydenのset
ルックアップ付きリスト理解-2番目(ループあたり0.302 usec)
mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "[x for x in l1 if x not in l2]"
1000000 loops, best of 3: 0.302 usec per loop
プレーンリストでのドーナツのリストの理解 -3番目(ループあたり0.552 usec)
mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "[x for x in l1 if x not in l2]"
1000000 loops, best of 3: 0.552 usec per loop
Moinuddin Quadriの使用filter
-4番目(ループあたり0.972 usec)
mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "filter(lambda x: x not in l2, l1)"
1000000 loops, best of 3: 0.972 usec per loop
Akshay Hazariのreduce
+filter
-5番目の組み合わせ(ループあたり3.97 usec)
mquadri$ python -m timeit "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "reduce(lambda x,y : filter(lambda z: z!=y,x) ,l1,l2)"
100000 loops, best of 3: 3.97 usec per loop
PS: set
順序を維持せず、重複する要素をリストから削除します。したがって、これらのいずれかが必要な場合は、セット差分を使用しないでください。
ここでドーナツの答えと他の答えを拡張すると、リスト内包表記の代わりにジェネレータ内包表記を使用し、set
データ構造を使用することでさらに良い結果を得ることができます(in
演算子はリストのO(n)ですがO(1)であるため)セット)。
だからここにあなたのために働く関数があります:
def filter_list(full_list, excludes):
s = set(excludes)
return (x for x in full_list if x not in s)
結果は、フィルタリングされたリストを遅延フェッチする反復可能オブジェクトになります。実際のリストオブジェクトが必要な場合(たとえばlen()
、結果に対してaを実行する必要がある場合)、次のようにリストを簡単に作成できます。
filtered_list = list(filter_list(full_list, excludes))
Pythonセットタイプを使用します。それが最もPythonicです。:)
また、ネイティブであるため、最も最適化された方法でもあります。
見る:
http://docs.python.org/library/stdtypes.html#set
http://docs.python.org/library/sets.htm(古いpythonの場合)
# Using Python 2.7 set literal format.
# Otherwise, use: l1 = set([1,2,6,8])
#
l1 = {1,2,6,8}
l2 = {2,3,5,8}
l3 = l1 - l2
l1
に繰り返し要素が含まれている場合、このメソッドは機能しません。
使用 セット内包 {xのxのL2}またはセット(L2)のセットを取得するために、次に使用リスト内包のリストを取得します
l2set = set(l2)
l3 = [x for x in l1 if x not in l2set]
ベンチマークテストコード:
import time
l1 = list(range(1000*10 * 3))
l2 = list(range(1000*10 * 2))
l2set = {x for x in l2}
tic = time.time()
l3 = [x for x in l1 if x not in l2set]
toc = time.time()
diffset = toc-tic
print(diffset)
tic = time.time()
l3 = [x for x in l1 if x not in l2]
toc = time.time()
difflist = toc-tic
print(difflist)
print("speedup %fx"%(difflist/diffset))
ベンチマークテスト結果:
0.0015058517456054688
3.968189239501953
speedup 2635.179227x
l2set = set( l2 )
代わりにl2set = { x for x in l2 }
代替ソリューション:
reduce(lambda x,y : filter(lambda z: z!=y,x) ,[2,3,5,8],[1,2,6,8])