私はこれに似た何かをしたいです:
>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> x
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> y = [1,3,5,7,9]
>>> y
[1, 3, 5, 7, 9]
>>> y - x # (should return [2,4,6,8,0])
しかし、これはpythonリストではサポートされていません。それを行うための最良の方法は何ですか?
私はこれに似た何かをしたいです:
>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> x
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> y = [1,3,5,7,9]
>>> y
[1, 3, 5, 7, 9]
>>> y - x # (should return [2,4,6,8,0])
しかし、これはpythonリストではサポートされていません。それを行うための最良の方法は何ですか?
回答:
リスト内包表記を使用します。
[item for item in x if item not in y]
-
中置構文を使用したい場合は、次のようにします。
class MyList(list):
def __init__(self, *args):
super(MyList, self).__init__(args)
def __sub__(self, other):
return self.__class__(*[item for item in self if item not in other])
その後、次のように使用できます。
x = MyList(1, 2, 3, 4)
y = MyList(2, 5, 2)
z = x - y
ただし、リストのプロパティ(順序など)が絶対に必要でない場合は、他の回答が推奨するように、セットを使用してください。
list
シャドウするため、変数名には使用しないでくださいlist
。「リスト」を使用する場合は、その前にアンダースコアを付けてください。また、をドロップすると*
、私のコードが壊れました...
[1,1,2,2] - [1,2]
、あなたは空のリストを取得します。[1,1,2,2] - [2]
与え[1,1]
、それが本当にリストsubstractionではありませんので、それはより多くのようなものです「一覧から一覧Xセットからの要素のないY」。
y
れるため(元の作業と同様のコストです)、それは事を節約しません。あなたは何のいずれかに必要があると思いますテスト、その後、listcomp外で行う、または悪質なハックとしてキャッシュするためにどの虐待、ネストされたlistcompsをワンライナーとして。への引数は1回しか作成されないため、適切に実行される少し見苦しくないワンライナーソリューションを使用することになります。set
yset = set(y)
if item not in yset
[item for yset in [set(y)] for item in x if item not in yset]
yset
list(itertools.filterfalse(set(y).__contains__, x))
filterfalse
TypeError: unhashable type: 'dict'
これが「セット減算」演算です。そのためにセットデータ構造を使用します。
Python 2.7の場合:
x = {1,2,3,4,5,6,7,8,9,0}
y = {1,3,5,7,9}
print x - y
出力:
>>> print x - y
set([0, 8, 2, 4, 6])
アイテムの重複と注文に問題がある場合:
[i for i in a if not i in b or b.remove(i)]
a = [1,2,3,3,3,3,4]
b = [1,3]
result: [2, 3, 3, 3, 4]
O(m * n)
実行時です(そして、listcompに副作用が含まれるときはいつでも私はうんざりします)。ランタイムを取得するために使用collections.Counter
して、それを改善できO(m + n)
ます。
多くのユースケースで必要な答えは次のとおりです。
ys = set(y)
[item for item in x if item not in ys]
これはaaronasterlingの回答とquantumSoupの回答のハイブリッドです。
aaronasterlingのバージョンはlen(y)
、の各要素のアイテム比較を行うx
ため、2次時間がかかります。quantumSoupのバージョンが使用するセット、それは内の各要素のための単一の一定時間設定のルックアップして、x
それが変換されるため、ブタの両方を x
とy
セットに、それはあなたの要素の順序を失います。
y
セットのみに変換し、x
順番に反復することにより、両方の世界の最高の線形時間と順序保存が得られます。*
ただし、quantumSoupのバージョンにはまだ問題があります。要素をハッシュ可能にする必要があります。それはセットの性質にかなり組み込まれています。**たとえば、別の辞書リストから辞書リストを減算しようとしているが、減算するリストが大きい場合、どうしますか?
ハッシュ可能になるように値を装飾できる場合は、問題が解決します。たとえば、値自体がハッシュ可能であるフラットディクショナリの場合:
ys = {tuple(item.items()) for item in y}
[item for item in x if tuple(item.items()) not in ys]
型がもう少し複雑な場合(たとえば、ハッシュ可能なJSON互換の値、または値が再帰的に同じ型であるリストまたはディクショナリを扱う場合が多い)でも、このソリューションを使用できます。ただし、一部のタイプはハッシュ可能なものに変換できません。
アイテムがハッシュ可能ではなく、ハッシュ可能でも作成できない場合は、比較可能であれば、少なくとも対数線形時間を取得できます(O(N*log M)
これはO(N*M)
、リストソリューションの時間よりもはるかに優れていますが、O(N+M)
ソートや使用して設定されたソリューションの時間)bisect
:
ys = sorted(y)
def bisect_contains(seq, item):
index = bisect.bisect(seq, item)
return index < len(seq) and seq[index] == item
[item for item in x if bisect_contains(ys, item)]
アイテムがハッシュ可能でも比較可能でもない場合は、2次解で立ち往生しています。
* OrderedSet
オブジェクトのペアを使用してこれを行うこともできます。オブジェクトのペアには、レシピとサードパーティのモジュールが含まれています。しかし、これはもっと簡単だと思います。
**セットの検索が一定の時間で行われる理由は、値をハッシュしてそのハッシュのエントリがあるかどうかを確認するだけでよいためです。値をハッシュできない場合、これは機能しません。
セットでの値の検索は、リストでの値の検索よりも高速です。
[item for item in x if item not in set(y)]
これは次の場合よりもわずかに優れていると思います。
[item for item in x if item not in y]
どちらもリストの順序を保持します。
set(y)
、y
各ループで新しいセットに変換しませんか?それ以外の場合は、禁忌の答えが必要ですys = set(y); [i for i in x if i not in ys]
。
if i not in set(y)
より25%長くかかることが示唆されています。セットの事前変換にかかる時間は55%少なくなります。かなり短く、でテストされていますが、長さがあると、違いがより顕著になります。if i not in y
y
x
y
y
のためのすべてのの要素をx
。等価性の比較がハッシュ計算に比べて非常に高価でない限り、これは常にplainに負けitem not in y
ます。
リストで重複する要素が許可されている場合は、コレクションのCounterを使用できます。
from collections import Counter
result = list((Counter(x)-Counter(y)).elements())
xの要素の順序を保持する必要がある場合:
result = [ v for c in [Counter(y)] for v in x if not c[v] or c.subtract([v]) ]
Counter.subtract
はゼロ値の要素を削除しません(-
そして削除しますが削除しませ-=
んsubtract
)ので、要素の削除を停止することは決してありません。置換not v in c
したいnot c[v]
(存在しない要素に対してゼロを返すので、を使用して「ゼロ性」の戻りを安全にテストできますnot
)。
他のソリューションには、いくつかの問題の1つがあります。
x = [1, 2, 2, 2]
、y = [2, 2]
に変換y
し、に変換します。適切な動作が2回削除される場合は、set
一致するすべての要素を削除する(残す[1]
のみ)か、各一意の要素の1つを削除する(を残す)か、出発、または[1, 2, 2]
2
[1, 2]
O(m * n)
最適解を行うことができます仕事、O(m + n)
仕事をアランはCounter
#2と#3を解決するために正しい方向に進んでいましたが、その解決策は順序を失います。順序を維持するソリューション(削除するn
値のn
繰り返しで各値の最初のコピーlist
を削除する)は次のとおりです。
from collections import Counter
x = [1,2,3,4,3,2,1]
y = [1,2,2]
remaining = Counter(y)
out = []
for val in x:
if remaining[val]:
remaining[val] -= 1
else:
out.append(val)
# out is now [3, 4, 3, 1], having removed the first 1 and both 2s.
各要素の最後のコピーを削除するには、for
ループをに変更し、ループを終了した直後にfor val in reversed(x):
追加out.reverse()
しfor
ます。
構築Counter
さO(n)
の面でy
の長さは、反復がx
あるO(n)
という点でx
長さs 'は、およびCounter
メンバーシップのテストと変異があるO(1)
一方で、list.append
償却されてO(1)
与えられた(append
することができO(n)
、多くのためappend
の、総合的なビッグOの平均値O(1)
少なくなりため、それらのうちの再割り当てが必要なため)、全体的な作業はO(m + n)
です。
をテストして、テストによってy
削除されなかった要素があったかどうかを確認することもできx
ます。
remaining = +remaining # Removes all keys with zero counts from Counter
if remaining:
# remaining contained elements with non-zero counts
int
、固定長配列にsをカウントできる)か、機能以上のものを実行する必要がありO(m + n)
ます(たとえば、次善の大きさ) -Oはlist
、一意の値/カウントのペアを並べ替えて、O(1)
dict
ルックアップをO(log n)
バイナリ検索に変更します。並べ替えられた非一意の値だけでなく、カウントを含む一意の値が必要になります。それ以外の場合はO(n)
、削除するためのコストがかかるためです。ソート済みの要素list
)。
これを試して。
def subtract_lists(a, b):
""" Subtracts two lists. Throws ValueError if b contains items not in a """
# Terminate if b is empty, otherwise remove b[0] from a and recurse
return a if len(b) == 0 else [a[:i] + subtract_lists(a[i+1:], b[1:])
for i in [a.index(b[0])]][0]
>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> y = [1,3,5,7,9]
>>> subtract_lists(x,y)
[2, 4, 6, 8, 0]
>>> x = [1,2,3,4,5,6,7,8,9,0,9]
>>> subtract_lists(x,y)
[2, 4, 6, 8, 0, 9] #9 is only deleted once
>>>
@aaronasterlingが提供する答えは良さそうですが、リストのデフォルトのインターフェースx = MyList(1, 2, 3, 4)
vs とは互換性がありませんx = MyList([1, 2, 3, 4])
。したがって、以下のコードは、よりpythonリストに適したものとして使用できます。
class MyList(list):
def __init__(self, *args):
super(MyList, self).__init__(*args)
def __sub__(self, other):
return self.__class__([item for item in self if item not in other])
例:
x = MyList([1, 2, 3, 4])
y = MyList([2, 5, 2])
z = x - y
In [1]: a = [1,2,3,4,5]
In [2]: b = [2,3,4,5]
In [3]: c = set(a) ^ set(b)
In [4]: c
Out[4]: {1}
この例では、2つのリストを差し引きます。
# List of pairs of points
list = []
list.append([(602, 336), (624, 365)])
list.append([(635, 336), (654, 365)])
list.append([(642, 342), (648, 358)])
list.append([(644, 344), (646, 356)])
list.append([(653, 337), (671, 365)])
list.append([(728, 13), (739, 32)])
list.append([(756, 59), (767, 79)])
itens_to_remove = []
itens_to_remove.append([(642, 342), (648, 358)])
itens_to_remove.append([(644, 344), (646, 356)])
print("Initial List Size: ", len(list))
for a in itens_to_remove:
for b in list:
if a == b :
list.remove(b)
print("Final List Size: ", len(list))