web-dev-qa-db-ja.com

pythonの複数セットの和集合

[[1, '34', '44'], [1, '40', '30', '41'], [1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']]

リストのリストがあります。私の目的は、1つのサブリストが他のサブリストと共通するものがあるかどうかを確認することです(比較する最初のインデックスオブジェクトを除く)。共通点がある場合は、それらのサブリストを統合します。

たとえば、この例では、私の最終的な回答は次のようになります。

[[1, '34, '44', '40' '30', '41', '42', '43']]

サブリストをセットに変換してから、union()およびintersection()操作を使用する必要があることを理解できます。しかし、私が行き詰まっているのは、各セット/サブリストを比較する方法です。リストの内容が変更されてエラーが発生するため、リストに対してループを実行して各サブリストを1つずつ比較することはできません。

私が知りたいのは、(セットに変換された)すべてのサブリストを比較し、それらの結合を取得する効率的な方法はありますか?

21

itertools モジュールは、この問題の短い作業を行います。

>>> from itertools import chain
>>> list(set(chain.from_iterable(d)))
[1, '41', '42', '43', '40', '34', '30', '44']

これを行う別の方法は、リストをunion()の個別の引数にアンパックすることです。

>>> list(set().union(*d))
[1, '41', '42', '43', '40', '34', '30', '44']

後者の方法では、すべての重複が排除され、入力を最初にセットに変換する必要はありません。また、インポートは必要ありません。

35

展開演算子*

>> list(set.union(*map(set, a)))
[1, '44', '30', '42', '43', '40', '41', '34']

(コメントを提供してくれたRaymond Hettingerに感謝!)

(ご了承ください

set.union(*tup)

開梱します

set.union(tup[0], tup[1], ... tup[n - 1])

21
Ami Tavory

私は個人的に、reduceの可読性が好きで、単純な条件関数と組み合わせると、

somelists = [[1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']] # your original lists
somesets = map(set,somelists) #your lists as sets

def condition(s1,s2): # condition to apply recursively to the sets
    if s1.intersection(s2):
        return s1.union(s2)
reduce( condition,somesets)
#{1, '30', '34', '40', '41', '42', '43', '44'}

もちろん、必要に応じて、この結果を2Dリストにキャストできますlist([reduce(...

これはchain.fromiterableの回答より3倍遅いということに注意してください。

2
dermen
In [20]: s
Out[20]: 
[[1, '34', '44'],
 [1, '40', '30', '41'],
 [1, '41', '40', '42'],
 [1, '42', '41', '43'],
 [1, '43', '42', '44'],
 [1, '44', '34', '43']]
In [31]: list({x for _list in s for x in _list})
Out[31]: [1, '44', '30', '42', '43', '40', '41', '34']

更新:

コメントをありがとう

1
Ajay

Itertoolsを使用して、このアクションを実行できます。リストに変数名Aがあるとします。

import itertools

single_list_with_all_values = list(itertools.chain(*A))
single_list_with_all_values.sort()

print set(single_list_with_all_values)
1
Arpit Goyal
>>> big = [[1, '34', '44'], [1, '40', '30', '41'], [1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']]
>>> set(reduce ( lambda l,a : l + a, big))
set([1, '44', '30', '42', '43', '40', '41', '34'])

そして、本当に最終結果としてリストのリストが必要な場合

>>>>[list(set(reduce ( lambda l,a : l + a, big)))]
[[1, '44', '30', '42', '43', '40', '41', '34']]

そして、リストの追加のためにラムダ関数を再コーディングしたくない場合:

>>>>[list(set(reduce ( list.__add__, big)))]
[[1, '44', '30', '42', '43', '40', '41', '34']]

[〜#〜] edit [〜#〜]:listの代わりにitertools.chainを使用することについての推奨の後で。

Timeitはlist .__ add__で約2.8秒、itertools.chainで約3.5秒かかるようです。

私はこのページをチェックしました。そうです、itertools.chainには、パフォーマンスを大幅に向上させるfrom_iterableメソッドが含まれています。 list .__ add __、itertools.chainおよびitertools.chain.from_iterableを使用して以下を参照してください。

>>> timeit.timeit("[list(set(reduce ( list.__add__, big)))]", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
16.051744650801993
>>> timeit.timeit("[list(set(reduce ( itertools.chain, big)))]", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
54.721315866467194
>>> timeit.timeit("list(set(itertools.chain.from_iterable(big)))", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
0.040056066849501804

アドバイスありがとうございました:)

1
Azurtree