Pythonは、一方のリストで、他方にはない要素を見つけます[重複]


135

1つのリストにあるが他のリストにはない特定の要素の新しいリストを作成するには、2つのリストを比較する必要があります。例えば:

main_list=[]
list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"] 

list_1をループして、list_1にないlist_2のすべての要素をmain_listに追加したいと思います。

結果は次のようになります。

main_list=["f", "m"]

どうすればPythonでそれを行うことができますか?


2
list_2どこにも表示されlist_1ない要素、またはlist_2同じインデックスに存在しない要素を探していlist_1ますか?
Patrick Haugh、2016

回答:


96

TL; DR:
ソリューション(1)

import numpy as np
main_list = np.setdiff1d(list_2,list_1)
# yields the elements in `list_2` that are NOT in `list_1`

ソリューション(2) ソートされたリストが必要です

def setdiff_sorted(array1,array2,assume_unique=False):
    ans = np.setdiff1d(array1,array2,assume_unique).tolist()
    if assume_unique:
        return sorted(ans)
    return ans
main_list = setdiff_sorted(list_2,list_1)




説明:
(1)あなたはnumpyののを使用することができますsetdiff1darray1array2assume_unique= False)。

assume_unique配列がすでに一意であるかどうかをユーザーに尋ねます。
の場合False、一意の要素が最初に決定されます。
の場合True、関数は要素がすでに一意であると想定し、関数は一意の要素の決定をスキップします。

この利回りユニークな値array1それはありませんarray2assume_uniqueFalse、デフォルトで。

Chinny84応答に基づく一意の要素に関心がある場合は、単に(ここで=>デフォルト値)を使用します。assume_unique=False

import numpy as np
list_1 = ["a", "b", "c", "d", "e"]
list_2 = ["a", "f", "c", "m"] 
main_list = np.setdiff1d(list_2,list_1)
# yields the elements in `list_2` that are NOT in `list_1`


(2) 回答をソートしたい人のために、カスタム関数を作成しました:

import numpy as np
def setdiff_sorted(array1,array2,assume_unique=False):
    ans = np.setdiff1d(array1,array2,assume_unique).tolist()
    if assume_unique:
        return sorted(ans)
    return ans

答えを得るには、以下を実行します。

main_list = setdiff_sorted(list_2,list_1)

サイドノート:
(a)ソリューション2(カスタム関数setdiff_sorted)はリストを返します(ソリューション1の配列と比較)。

(b)要素が一意であるかどうかわからない場合はsetdiff1d、ソリューションAとBの両方でNumPyのデフォルト設定を使用してください。複雑さの例は何ですか?注(c)を参照してください。

(c)2つのリストのいずれかが一意でない場合、状況は異なります。 一意ではないと
言いlist_2ます:list2 = ["a", "f", "c", "m", "m"]。現状維持list1:利回りlist_1 = ["a", "b", "c", "d", "e"]
のデフォルト値を設定します(両方のソリューションで)。ただし、設定すると、両方のソリューションが提供します。どうして?これは、要素が一意であるとユーザーが想定したためです。したがって、それは維持する方が良いですassume_unique["f", "m"]assume_unique=True["f", "m", "m"]assume_uniqueデフォルト値に。両方の回答がソートされていることに注意してください。


リストがすでに注文されている場合は、これも注文されたリストを返します。セットに変換してから差分を取得するネイティブソリューション(以下に示すソリューション)は、順序付けされていないリストを返すため、結果を視覚的に調べることが難しくなる場合があります。
ダブルダウン

1
こんにちは、@ Doubledown!あなたの懸念は編集された投稿で対処されました。お役に立てれば!
jcoderepo

182

セットを使用できます。

main_list = list(set(list_2) - set(list_1))

出力:

>>> list_1=["a", "b", "c", "d", "e"]
>>> list_2=["a", "f", "c", "m"]
>>> set(list_2) - set(list_1)
set(['m', 'f'])
>>> list(set(list_2) - set(list_1))
['m', 'f']

@JonClementsのコメントによると、こちらはよりきれいなバージョンです。

>>> list_1=["a", "b", "c", "d", "e"]
>>> list_2=["a", "f", "c", "m"]
>>> list(set(list_2).difference(list_1))
['m', 'f']

2
これは、unique要素のみを考慮している場合は適切ですがm's、たとえば複数の要素がある場合、これは取得されません。
Chinny84 16

それは本当だ。ポスターはユニークな要素を探していると思いました。それは彼が「特定」で何を意味するかによると思います。
nrlakin 16

確かに私はあなたの答えに反対票を投じなかったps、特に明確でない元の質問に対して。
Chinny84

13
list(set(list_2).difference(list_1))これを明示的なset変換を回避するように書くことができます...
Jon Clements

心配ない!書式設定を支援してくれた@leafに感謝します。
nrlakin 2016

60

ネイティブメソッドを使用できる場合に、上記の説明がなぜそれほど複雑かわからない:

main_list = list(set(list_2)-set(list_1))

6
順序を維持することが理由かもしれません
キース

57

次のようなリスト内包表記を使用します

main_list = [item for item in list_2 if item not in list_1]

出力:

>>> list_1 = ["a", "b", "c", "d", "e"]
>>> list_2 = ["a", "f", "c", "m"] 
>>> 
>>> main_list = [item for item in list_2 if item not in list_1]
>>> main_list
['f', 'm']

編集:

以下のコメントで述べたように、大きなリストでは、上記は理想的なソリューションではありません。その場合、より良いオプションは最初のものに変換するlist_1ことsetです:

set_1 = set(list_1)  # this reduces the lookup time from O(n) to O(1)
main_list = [item for item in list_2 if item not in set_1]

3
注:大規模についてlist_1、あなたはにpreconvertにしたいと思いますset/ frozenset例えば、set_1 = frozenset(list_1)その後、main_list = [item for item in list_2 if item not in set_1]からチェック時間を削減する、O(n)項目ごとに(およそ)へO(1)
ShadowRanger

@ettanany ettananyが投稿した解決策を試す場合は注意してください。私はエタナニーの解決策をそのまま試してみましたが、実際のところ、リストが大きい場合は非常に遅いです。シャドウレンジャーの提案を組み込むために回答を更新できますか?
ダブルダウン

文字列の代わりにインデックスを取得することは可能でしょうか?
JareBear

@JareBear使用できますenumerate()[index for (index, item) in enumerate(list_2) if item not in list_1]
ettanany

@ettananyさん、ありがとうございました!! できるだけ早く実装します。しかし、あなたのコードはとてもきれいに見えます。
JareBear

5

あなたが唯一必要とワンライナー溶液(輸入を無視して)したい場合はO(max(n, m))長さの入力のための作業nm、ないO(n * m)仕事を、あなたがそうすることができるモジュールitertools

from itertools import filterfalse

main_list = list(filterfalse(set(list_1).__contains__, list_2))

これは、関数関数が構築時にコールバック関数を利用することを利用して、コールバックを一度作成し、どこかに保存する必要なく(すべての要素filterfalseに内部的に保存するため)再利用できるようにします。リスト内包表記とジェネレータ式はこれを行うことができますが、醜いです。†

これは、1行で同じ結果になります。

main_list = [x for x in list_2 if x not in list_1]

の速度で:

set_1 = set(list_1)
main_list = [x for x in list_2 if x not in set_1]

もちろん、比較が位置的であることが意図されている場合は、次のようになります。

list_1 = [1, 2, 3]
list_2 = [2, 3, 4]

生成する必要があります:

main_list = [2, 3, 4]

(の値はlist_2の同じインデックスで一致するためlist_1)一時的なsやs を含まないPatrickの答えに間違いなく行く必要があります(sがおおよその場合でも、単純な等式チェックよりもチェックごとの「定数」係数が高くなります) )そして、他のどの答えよりも少ない作業を含み、問題が位置に敏感である場合、一致する要素が不一致のオフセットに表示されるときの唯一の正しい解決策です。listsetsetO(1)O(min(n, m))

†:ワンライナーと同じようにリスト内包で同じことを行う方法は、ネストされたループを悪用して、「最も外側の」ループで値を作成およびキャッシュすることです。例:

main_list = [x for set_1 in (set(list_1),) for x in list_2 if x not in set_1]

これはまた、Python 3でパフォーマンスのマイナーな利点を提供します(現在set_1、各チェックのネストされたスコープから検索されるのではなく、内包コードでローカルにスコープされているためです。Python2では、Python 2はクロージャーを使用しないため、重要ではありません。内包表記をリストします。それらは、使用されているのと同じスコープで動作します)。


4
main_list=[]
list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"]

for i in list_2:
    if i not in list_1:
        main_list.append(i)

print(main_list)

出力:

['f', 'm']

同等のリスト内包に基づくソリューションと同様に、のスキャンとそれを作成する(とはそれぞれとの長さです)ため、これlist_1が大きい場合はサイズが大きく、list_2自明ではないサイズになると遅くなります。前もって/に変換すると、containsのチェックをで行うことができ、(を作成するために作業を行うため、技術的には)の長さで全体の作業が行われます。len(list_2) O(n)list_1O(n * m)nmlist_2list_1list_1setfrozensetO(1)O(n)list_2O(max(n, m))O(m)set
ShadowRanger

1

私は考えzipリストは一緒要素により、これらの要素を比較します。

main_list = [b for a, b in zip(list1, list2) if a!= b]

OPが要素ごとに比較したい場合(不明確、例はどちらの方法でもかまいません)、これは他の回答よりもはるかに効率的です。これは、両方listのsを1回の安価なパスで1つの新しいパスlistが作成され、追加の一時的なものがないためです。 、高価な封じ込めチェックなどは
不要

1
@ShadowRangerこれは、要点である要素ごとの違いに対してのみ機能します
フォードの

@fordprefect:うん。私自身の答えは、位置に依存しない違いをカバーしています。
ShadowRanger 2017

1

私は2つの方法を使用しましたが、1つの方法が他の方法よりも有用であることに気付きました。これが私の答えです:

私の入力データ:

crkmod_mpp = ['M13','M18','M19','M24']
testmod_mpp = ['M13','M14','M15','M16','M17','M18','M19','M20','M21','M22','M23','M24']

方法1:np.setdiff1d位置が保持されるため、他の方法よりもこの方法が好きです

test= list(np.setdiff1d(testmod_mpp,crkmod_mpp))
print(test)
['M15', 'M16', 'M22', 'M23', 'M20', 'M14', 'M17', 'M21']

方法2:方法1と同じ答えを出しますが、順序を乱します

test = list(set(testmod_mpp).difference(set(crkmod_mpp)))
print(test)
['POA23', 'POA15', 'POA17', 'POA16', 'POA22', 'POA18', 'POA24', 'POA21']

Method1 np.setdiff1dは私の要件を完全に満たしています。情報のためのこの答え。


0

発生回数を考慮する必要がある場合は、おそらく次のようなものを使用する必要がありますcollections.Counter

list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"] 
from collections import Counter
cnt1 = Counter(list_1)
cnt2 = Counter(list_2)
final = [key for key, counts in cnt2.items() if cnt1.get(key, 0) != counts]

>>> final
['f', 'm']

約束したように、これは異なる数の発生を「差異」として処理することもできます。

list_1=["a", "b", "c", "d", "e", 'a']
cnt1 = Counter(list_1)
cnt2 = Counter(list_2)
final = [key for key, counts in cnt2.items() if cnt1.get(key, 0) != counts]

>>> final
['a', 'f', 'm']

-1

ser1から、ser2にあるアイテムを削除します。

入力

ser1 = pd.Series([1、2、3、4、5])ser2 = pd.Series([4、5、6、7、8])

解決

ser1 [〜ser1.isin(ser2)]


Stack Overflowへようこそ。この質問には他に8つの回答があり、そのうちの1つは元の投稿者に受け入れられました。すでに提示されている内容に対して、回答がどのように改善されるかを説明してください。
CHB
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.