あるリストが別のリストのサブセットであるかどうかを確認するにはどうすればよいですか?


185

リストが別のリストのサブセットであるかどうかを確認する必要があります-私が求めるのはブール値の戻りだけです。

交差後に小さなリストで同等性をテストすることがこれを行う最も速い方法ですか?比較する必要のあるデータセットの数を考えると、パフォーマンスは最も重要です。

議論に基づいてさらに事実を追加する:

  1. 多くのテストでどちらのリストも同じですか?それらの1つが静的なルックアップテーブルであるため、これが行われます。

  2. リストにする必要はありますか?それはしません-静的ルックアップテーブルは、最高のパフォーマンスを発揮するものであれば何でもかまいません。動的なものは、静的な検索を実行するためのキーを抽出する辞書です。

シナリオを考えると、最適なソリューションは何ですか?


あなたは速度に言及します、あなたの用途に応じて、おそらくnumpyは有用でしょう。
ninMonkey 2013年

2
リスト項目はハッシュ可能ですか?
2013年

2
順序が重要である場合、これは良いスタートかもしれません

適切なサブセットが必要ですか、それとも同じですか?
törzsmókus

2
なぜset(list_a).issubset(set(list_b))しないのですか?
SeF

回答:


127

Pythonがこのために提供するパフォーマンス関数はset.issubsetです。ただし、質問に対する回答であるかどうかが明確にならないいくつかの制限があります。

リストには複数のアイテムが含まれる場合があり、特定の順序があります。セットにはありません。さらに、セットはハッシュ可能なオブジェクトでのみ機能します。

サブセットまたはサブシーケンスについて質問していますか(つまり、文字列検索アルゴリズムが必要になります)。多くのテストでどちらのリストも同じですか?リストに含まれるデータ型は何ですか?そしてそのことについては、それはリストである必要がありますか?

他の投稿はdictと交差し、リストはタイプをより明確にし、セットのような機能のためにディクショナリキービューを使用するように推奨されました。その場合、辞書のキーはセットのように動作するため、動作することがわかっていました(Pythonでセットを作成する前は、辞書を使用していたため)。問題が3時間でどのように特定されなくなったのか不思議に思います。


私はサブセットのみを参照していますが、issubsetは問題なく動作します-ありがとう。しかし、私はここで2つの質問に興味があります。1.多くのテストでどちらのリストも同じですか?それらの1つは静的ルックアップテーブルであるため、これは実行されます2.リストである必要がありますか?それはしません-静的ルックアップテーブルは、最高のパフォーマンスを発揮するものであれば何でもかまいません。動的なものは、静的な検索を実行するためのキーを抽出する辞書です。この事実は解決策を変えますか?
IUnknown 2013年

あまりない。辞書のキーはセットのようであり、すでにハッシュテーブルに配置されているため、静的な部分にセットを使用しても、さらに複雑になることはありません。基本的に、1つがdictであるという事実は、静的部分をセットに変換する必要がない場合があることを意味します(O(n)パフォーマンスでall(itertools.imap(dict.has_key、mylist)をチェックできます)。
Yann Vernier 2013年

これ(またはセットに依存する他のソリューション)がここで受け入れられる答えになる方法はわかりません。問題はリストに関するものであり、率直に言って「1つのリストが他のリストのサブセットであるかどうかを確認する」のサブセットは文字どおりに解釈されるべきではないと思います。セットに変換すると、重複する要素に関する情報は失われますが、最初のリストにそれらが含まれている可能性がある場合は、それらが2番目のリストに表示されるかどうかを確認し、1つのリストのすべての要素が見つかることを本当に伝えることが重要になる場合があります他の中で。セットはそれをしません!
inVader

コンテキストが重要です。これは質問者を助けるために受け入れられ、その違いを説明しました。候補者は集合として表現できると言われたので、それは集合課題でした。あなたのケースは異なるかもしれません、そしてあなたが言及する違いはcollections.Counterのようなマルチセットを使用して解決されるでしょう。
Yann Vernier

141
>>> a = [1, 3, 5]
>>> b = [1, 3, 5, 8]
>>> c = [3, 5, 9]
>>> set(a) <= set(b)
True
>>> set(c) <= set(b)
False

>>> a = ['yes', 'no', 'hmm']
>>> b = ['yes', 'no', 'hmm', 'well']
>>> c = ['sorry', 'no', 'hmm']
>>> 
>>> set(a) <= set(b)
True
>>> set(c) <= set(b)
False

21
これは見栄えがよく、書き込みも簡単ですが、set(a).issubset(b) この場合はaセットに変換するだけでなくに変換するため、最速になるはず ですb。これにより時間を節約できます。を使用timeitして、2つのコマンドで消費された時間を比較できます。例えば、timeit.repeat('set(a)<set(b)', 'a = [1,3,5]; b = [1,3,5,7]', number=1000) および timeit.repeat('set(a).issubset(b)', 'a = [1,3,5]; b = [1,3,5,7]', number=1000)
ハクモクレン劉

8
@YulanLiu:それを壊すのは嫌いですが、最初に行うことissubsetは、引数がset/ frozensetであるかどうかをチェックし、そうでない場合は、それsetを比較のために一時set変数に変換し、チェックを実行して、一時変数を破棄しますタイミングの違い(存在する場合)は、LEGBルックアップコストのわずかな違いの要因になります(set2回目を見つけることは、既存のの属性ルックアップよりもコストがかかりますset)。
ShadowRanger 2016年

3
両方のリストに同じ値が含まれている場合、これはfalseを返します。条件は代わりにset(a)<= set(b)にする必要があります
ssi-anik

2
この答えはどのようにして正しいのでしょうか。彼はセットではなくリストを求めました。それらは完全に異なります。a = [1、3、3、5、5]およびb = [1、3、3、3、5]の場合はどうでしょうか。集合論は重複には不適切です。
Eamonnケニー

1
また、a = [1,3,5]およびb = [1,3,5]の場合、set(a)<set(b)はFalseを返すことも指摘しておきます。次の場合を処理するために、equals演算子を追加できます。つまり、set(a)<= set(b)。
Jon

37
one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

all(x in two for x in one)

説明:ジェネレーターoneは、その項目がリストにあるかどうかをチェックするリストをループしてブール値を作成しますtwo。 すべてのアイテムが真実である場合はを返し、そうでない場合all()はを返しTrueますFalse

また、allすべてのアイテムを処理する必要がなく、不足している要素の最初のインスタンスでFalse を返すという利点もあります。


私は読みやすさのために、そしてあなたが達成しようとしていることを明確にすることはset(one).issubset(set(two))素晴らしい解決策だと思います。私が投稿したソリューションでは、適切な比較演算子が定義されていれば、どのオブジェクトでもそれを使用できるはずです。
voidnologo 2016年

4
リスト内包表記ではなく、ジェネレータ式を使用してください。前者はall正しく短絡することを可能にし、後者は最初のチェックからテストが失敗することが明らかであっても、すべてのチェックを実行します。角括弧をドロップするだけで取得できall(x in two for x in one)ます。
ShadowRanger 2016年

私は間違っていますか、それともこのメソッドをローカルで使用できないのですか?
Homper

22

アイテムがハッシュ可能であると仮定

>>> from collections import Counter
>>> not Counter([1, 2]) - Counter([1])
False
>>> not Counter([1, 2]) - Counter([1, 2])
True
>>> not Counter([1, 2, 2]) - Counter([1, 2])
False

重複アイテムを気にしない場合など。[1, 2, 2]そして[1, 2]その後、ちょうど使用します。

>>> set([1, 2, 2]).issubset([1, 2])
True

交差後に小さなリストで同等性をテストすることは、これを行う最も速い方法ですか?

.issubsetそれを行うための最速の方法になります。テストの前に長さをチェックしても、issubset反復してチェックするO(N + M)のアイテムがあるため、速度は向上しません。


6

もう1つの解決策は、を使用することintersectionです。

one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

set(one).intersection(set(two)) == set(one)

セットの交差には以下が含まれます set one

(または)

one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

set(one) & (set(two)) == set(one)

2
one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

set(x in two for x in one) == set([True])

list1がリスト2にある場合:

  • (x in two for x in one)のリストを生成しますTrue

  • 私たちが行う場合、set(x in two for x in one)要素は1つだけです(True)。


2

セット理論はリストに不適切です。重複すると、セット理論を使用して誤った答えが返されるためです。

例えば:

a = [1, 3, 3, 3, 5]
b = [1, 3, 3, 4, 5]
set(b) > set(a)

意味はありません。はい、それは間違った答えを出しますが、集合論は1,3,5対1,3,4,5を比較しているだけなので、これは正しくありません。すべての重複を含める必要があります。

代わりに、各項目の各出現をカウントし、等しいかどうかをチェックする必要があります。O(N ^ 2)演算を使用せず、クイックソートを必要としないため、これはそれほど高価ではありません。

#!/usr/bin/env python

from collections import Counter

def containedInFirst(a, b):
  a_count = Counter(a)
  b_count = Counter(b)
  for key in b_count:
    if a_count.has_key(key) == False:
      return False
    if b_count[key] > a_count[key]:
      return False
  return True


a = [1, 3, 3, 3, 5]
b = [1, 3, 3, 4, 5]
print "b in a: ", containedInFirst(a, b)

a = [1, 3, 3, 3, 4, 4, 5]
b = [1, 3, 3, 4, 5]
print "b in a: ", containedInFirst(a, b)

次に、これを実行すると、次のようになります。

$ python contained.py 
b in a:  False
b in a:  True

1

2つの文字列を比較することを検討している人はいないので、これが私の提案です。

もちろん、パイプ( "|")がどちらのリストにも含まれていないかどうか、また別の文字が自動的に選択されているかどうかを確認することもできますが、アイデアはわかりました。

数字が数桁になる可能性があるため、区切り文字として空の文字列を使用することは解決策ではありません([12,3]!= [1,23])

def issublist(l1,l2):
    return '|'.join([str(i) for i in l1]) in '|'.join([str(i) for i in l2])

0

パーティーに遅れたらごめんなさい。;)

set Aがのサブセットであるかどうかを確認するset BPythonA.issubset(B)、とA <= B。それは上で動作setのみと素晴らしい作品しかし内部実装の複雑さは不明です。リファレンス:https : //docs.python.org/2/library/sets.html#set-objects

list Aサブセットであるかどうかを確認するためのアルゴリズムを思いつきましたlist B

  • サブセットの検索の複雑さを軽減するためにsort、サブセットを取得するために要素を比較する前に、最初に両方のリストが適切であると思います 。
  • それは私を助けた第二のリストの要素の値が時に最初のリストの要素の値よりも大きいです。breakloopB[j]A[i]
  • last_index_j最後に中断loopしたlist Bところからやり直すために使用されます。これは、開始から回避始まるの比較に役立ちます list B(あなたが開始するために、不必要な推測として、あるlist Bから、index 0それ以降にiterations。)
  • 複雑さO(n ln n)は、両方のリストのソートとO(n)サブセットのチェックでそれぞれ異なります。
    O(n ln n) + O(n ln n) + O(n) = O(n ln n)

  • コードにはprint、各iterationで何が起こっているかを確認するためのたくさんのステートメントがありますloop。これらは理解のみを目的としています。

あるリストが別のリストのサブセットであるかどうかを確認する

is_subset = True;

A = [9, 3, 11, 1, 7, 2];
B = [11, 4, 6, 2, 15, 1, 9, 8, 5, 3];

print(A, B);

# skip checking if list A has elements more than list B
if len(A) > len(B):
    is_subset = False;
else:
    # complexity of sorting using quicksort or merge sort: O(n ln n)
    # use best sorting algorithm available to minimize complexity
    A.sort();
    B.sort();

    print(A, B);

    # complexity: O(n^2)
    # for a in A:
    #   if a not in B:
    #       is_subset = False;
    #       break;

    # complexity: O(n)
    is_found = False;
    last_index_j = 0;

    for i in range(len(A)):
        for j in range(last_index_j, len(B)):
            is_found = False;

            print("i=" + str(i) + ", j=" + str(j) + ", " + str(A[i]) + "==" + str(B[j]) + "?");

            if B[j] <= A[i]:
                if A[i] == B[j]:
                    is_found = True;
                last_index_j = j;
            else:
                is_found = False;
                break;

            if is_found:
                print("Found: " + str(A[i]));
                last_index_j = last_index_j + 1;
                break;
            else:
                print("Not found: " + str(A[i]));

        if is_found == False:
            is_subset = False;
            break;

print("subset") if is_subset else print("not subset");

出力

[9, 3, 11, 1, 7, 2] [11, 4, 6, 2, 15, 1, 9, 8, 5, 3]
[1, 2, 3, 7, 9, 11] [1, 2, 3, 4, 5, 6, 8, 9, 11, 15]
i=0, j=0, 1==1?
Found: 1
i=1, j=1, 2==1?
Not found: 2
i=1, j=2, 2==2?
Found: 2
i=2, j=3, 3==3?
Found: 3
i=3, j=4, 7==4?
Not found: 7
i=3, j=5, 7==5?
Not found: 7
i=3, j=6, 7==6?
Not found: 7
i=3, j=7, 7==8?
not subset

それらを並べ替えると、セットの代わりにリストを使用する理由がなくなります...
LtWorf

0

以下のコードは、特定のセットが別のセットの「適切なサブセット」であるかどうかをチェックします

 def is_proper_subset(set, superset):
     return all(x in superset for x in set) and len(set)<len(superset)


ありがとう@YannVernierサブセットとスーパーセットの両方に空のチェックを含めるように変更したので、両方が空の場合はfalseを返します。
レオバスティン2017年

だが 、なぜこれを行うのですか?AがBのサブセットであることは、AにBにないアイテムが含まれていないこと、またはAのすべてのアイテムがBにもあることを意味します。したがって、空のセットは、それ自体を含むすべてのセットのサブセットです。あなたの余分なチェックはそうではないと主張し、あなたはこれが何らかの形で理想的であると主張しますが、確立された用語に反しています。利点は何ですか?
Yann Vernier 2017年

ありがとう@YannVernierこれで、コードは、指定されたセットが別のセットの「適切なサブセット」であるかどうかをチェックします。
レオバスティン2017年

これは、セットの使用に依存する回答と同じくらい悪いです。数学的に言えば、セットは異なる要素のコレクションですが、あるリストが別のリストの一部であるかどうかを確認する場合、その仮定に依存することはできません。最初のリストに重複が含まれる場合、問題の要素が2番目のリストに1回だけ存在しても、関数はTrueを返す可能性があります。リストを比較しようとするとき、これは正しい動作ではないと思います。
inVader

0

Python 3.5では、を実行し[*set()][index]て要素を取得できます。他の方法よりもはるかに遅いソリューションです。

one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

result = set(x in two for x in one)

[*result][0] == True

またはlenとsetだけで

len(set(a+b)) == len(set(a))

0

これが、あるリストが別のリストのサブセットであるかどうかを知る方法です。私の場合、シーケンスが重要です。

def is_subset(list_long,list_short):
    short_length = len(list_short)
    subset_list = []
    for i in range(len(list_long)-short_length+1):
        subset_list.append(list_long[i:i+short_length])
    if list_short in subset_list:
        return True
    else: return False

0

ほとんどの解決策は、リストに重複がないことを考慮しています。リストに重複がある場合、これを試すことができます:

def isSubList(subList,mlist):
    uniqueElements=set(subList)
    for e in uniqueElements:
        if subList.count(e) > mlist.count(e):
            return False     
    # It is sublist
    return True

これにより、サブリストにリストと異なる要素や、​​共通の要素が多くなることはありません。

lst=[1,2,2,3,4]
sl1=[2,2,3]
sl2=[2,2,2]
sl3=[2,5]

print(isSubList(sl1,lst)) # True
print(isSubList(sl2,lst)) # False
print(isSubList(sl3,lst)) # False

-1

あるリストが別のリストに「含まれる」かどうかを尋ねている場合:

>>>if listA in listB: return True

listAの各要素がlistBの一致する要素の数と等しいかどうかを確認する場合は、次のことを試してください。

all(True if listA.count(item) <= listB.count(item) else False for item in listA)

これは私にはうまくいきません。listA == listBであってもfalseを返します
cass

@cass文字列でのみテストしました。あなたのマシンでこれを試してください。pastebin.com/9whnDYq4
DevPlayer

2番目の部分ではなく、「if listA in listB:return True」の部分を参照していました。
CASS

@cass考慮:['one'、 'two']の['one'、 'two']はFalseをもたらします。['one'、 'two'、 'three']の['one'、 'two']はFalseを返します。[['one'、 'two']、 'three']の['one'、 'two']はTrueになります。つまり、もしlistA == ListBであれば、listAはlistB内のリスト要素である必要があるため、listBのlistAは常にFalseを返します。多分あなたは考えています:listBのlistAは「listAの項目はlistBの項目としてリストされています。それはlistBのlistAの意味ではありません
DevPlayer

@cassああ、私の投稿が混乱するのがわかります。元の投稿では、listAがlistBのサブセットであるかどうかをテストするよう求められました。技術的に、私の投稿は元の投稿の質問に基づいて間違っています。それが正しいためには、質問が「if listA in [item0、item2、listA、item3、listA、]」を要求している必要があります。「['d'、 'c'、 'f'、 'a'、 'b'、 'a']の['a'、 'b'、 'c'のアイテム]」ではありません。
DevPlayer 2015年

-2

の場合a2 is subset of a1Length of set(a1 + a2) == Length of set(a1)

a1 = [1, 2, 3, 4, 5]
a2 = [1, 2, 3]

len(set(a1)) == len(set(a1 + a2))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.