リストから同じアイテムを削除するにはどうすればよいですか? 言う
A= [1, 2, 3, 8, 7, 8, 8, 7, 6]
そして、8つすべてを削除したいのですが、どうすればよいですか? 番号が順番になっていない場合は?
これを行う簡単な方法は、そうでないすべてのアイテムを含む新しいリストを作成すること8
です。例えば:
B=[item for item in A if item != 8]
本当にその場で実行したい場合 (たとえば、他のオブジェクトが と同じリストへの参照を持っていて、A
他のオブジェクトに変更を表示したい場合など) は可能です。しかし、それはよりトリッキーです。たとえば、インデックスで削除する場合、ある時点で戻る必要があります (最初の を削除すると、後8
のすべての に新しいインデックスがあるため、常に最後のものを最初に削除する必要があるため): 8
indices = [index for index, item in enumerate(A) if item==8]
for index in reversed(indices):
del A[index]
remove
または、失敗するまで試行し続けることもできますが、これは醜く遅いです。
while True:
try:
A.remove(8)
except ValueError:
break
実際、新しいリストを作成してから、その新しいリストのコピーに変更する方がよい場合がよくあります。A
A[:]=[item for item in A if item != 8]
パフォーマンス テストのために、提案されたすべての回答に対してこのコードを実行しました。テストされたアルゴリズムは次のとおりです。
実際に変更する必要がない場合A
(通常は変更する必要がない場合は、名前を再バインドするだけで済みます)、コピーしてから変更するアルゴリズムはさらに単純で高速になることに注意してください。しかし、ここではテストしませんでした。
また、「while True: remove until error」アルゴリズム、または Chris Barker が提案したコメントのバリエーションも完全にはテストしませんでした。ただし、非常に簡単なテストでは、予想どおり、変動が約 2 倍遅く、ほとんどすべてのテスト ケースで他の何よりも桁違いに遅いことが示されています。
いずれにせよ、このテストでは、0 から 16、256、または 65536 までの範囲の値を持つ、長さ 100K または 1M (担当者数の 1/10) のランダム リストから 0 を削除しています (個別の値が少ないほど、削除するヒット)。
CPython を使用している場合、特に N が大きい場合は、listcomp バージョンが常に最速です。PyPy を使用している場合、N が巨大で M が小さい場合、インプレース アルゴリズムはそれを打ち負かすことができますが、その場合、それらはすべて非常に高速です。ファンシー スライド アルゴリズムは、他のインプレース アルゴリズムの 2 次動作を排除しますが、単純なケースでは動作が遅くなるため、明確な勝者となる場所は実際にはありません。(これは最も複雑な方法でもあります。最初の試行で唯一正しくなかったのはおそらくそのためです。) 絶対に削除するコピーの数が少ない場合は、 PyPy を使用している場合は、whiledel ソリューションを検討してください。他のユースケース、またはユースケースがよくわからない場合は、listcomp を使用します。
64-bit python CPython 3.3.0
16 values 256 values 65536 values
100K 1000K 100K 1000K 100K 1000K
revind 0.188 17.3 0.085 1.23 0.074 0.080
whiledel 0.324 19.3 0.206 1.36 0.199 0.203
slide 0.091 0.54 0.097 0.54 0.095 0.538
genepxr 0.094 0.11 0.100 0.11 0.099 0.108
listcomp 0.070 0.08 0.073 0.08 0.071 0.079
filterer 0.081 0.09 0.080 0.09 0.835 0.088
64-bit python CPython 2.7.2
16 values 256 values 65536 values
100K 1000K 100K 1000K 100K 1000K
revind 0.198 17.1 0.089 1.23 0.088 0.955
whiledel 0.345 19.8 0.233 1.36 0.234 0.243
slide 0.095 0.54 0.099 0.55 0.095 0.551
genepxr 0.092 0.11 0.097 0.11 0.107 0.116
listcomp 0.091 0.09 0.099 0.08 0.105 0.114
filterer 0.122 0.23 0.132 0.09 0.135 0.150
64-bit python PyPy 1.9.0 (Python 2.7.2)
16 values 256 values 65536 values
100K 1000K 100K 1000K 100K 1000K
revind 0.266 28.5 0.027 1.97 0.018 0.013
whiledel 0.281 30.2 0.023 1.94 0.034 0.009
slide 0.022 0.39 0.015 0.022 0.006 0.018
genepxr 0.089 0.13 0.087 0.154 0.089 0.147
listcomp 0.052 0.08 0.057 0.073 0.052 0.073
filterer 0.054 0.07 0.053 0.078 0.048 0.074
スライドのパフォーマンスを予測するのは少し難しいです。N が大きく M が小さい場合、whiledel で吹き飛ばされると予想されますが、実際には遅くなります。しかし、よく考えてみると、このアルゴリズムは、M 個の線形移動を N 個の単純なコピーに効果的に置き換えます。前者は O(NM) で後者は O(N) ですが、C でのループ (または、さらに良いのは の内部memmove
) での乗数は、Python でのループよりもはるかに小さいため、N/M でない限り無視できません。巨大です(その時点では、すべてのソリューションが非常に高速であるため、ほとんど問題になりません). したがって、M Python ループと NM C ループを実行すると、N Python ループを簡単に打ち負かすことができます。
これらの回答のほとんどは、データのコピーを提案しています。リストが十分に大きい場合、これは望ましくない場合があります。簡単に使用できます
while 8 in A: A.remove(8)
データをコピーせずに実行します。ただし、これは二次時間で実行されるため、リストが大きい場合には望ましくありません。データをコピーせずに線形時間で実行するには、次を使用します。
def remove_all_from_list(L, n):
i = 0
while i < len(L):
if L[i] == n:
del L[i] # Do not increment i here, because L[i] will change
else:
i += 1
>>> A = [1, 2, 3, 8, 7, 8, 8, 7, 6]
>>> remove_all_from_list(A, 8)
>>> A
[1, 2, 3, 7, 7, 6]
編集: @abarnert は、del L[i] が O(N) であることを思い出させたので、これは実際には 2 次です。これは、O(N) インプレース ソリューションの別の試みです...
def remove_all_from_list(L, n):
# indices takes *worse-case* O(N) space, but typical-case much less
indices = [i for i, x in enumerate(L) if x==n]
indices_seen = 0
num_indices = len(indices)
for i in xrange(len(L)):
while (indices_seen < num_indices and
i + indices_seen == indices[indices_seen]):
indices_seen += 1
if i+indices_seen >= len(L):
break
L[i] = L[i+indices_seen]
L[-indices_seen:] = []
これにより、最後にすべてのシャッフルが行われるため、各要素は最大で 1 回移動されます。これは、abarnert のコピー方法とまったく同じくらい時間がかかることを認識しています。非常に大きなリストがある場合に備えて、メモリ使用量を減らす方法を考えているところです。
最終編集: 速度テスト (@abarnert のものほど包括的ではありません)
import random
L = range(30)*10000
random.shuffle(L)
from copy import copy
for fn in [remove_1, remove_2, remove_3, remove_4, remove_5, remove_6]:
print fn.__name__
%timeit fn(copy(L), 8)
remove_1 # listcomp
10 loops, best of 3: 39.1 ms per loop
remove_2 # revind
1 loops, best of 3: 1.7 s per loop
remove_3 # try: remove; except: break
1 loops, best of 3: 65.7 s per loop
remove_4 # while n in L: L.remove(n)
1 loops, best of 3: 129 s per loop
remove_5 # whiledel
1 loops, best of 3: 1.87 s per loop
remove_6 # slide
1 loops, best of 3: 227 ms per loop
In [13]: A= [1, 2, 3, 8, 7, 8, 8, 7, 6]
In [14]: [i for i in A if i!=8]
Out[14]: [1, 2, 3, 7, 7, 6]
In [15]: filter(lambda i: i!=8, A)
Out[15]: [1, 2, 3, 7, 7, 6]