2012-05-14 1 views
2

私はPython(2.7で動作します)が新しく、とても貴重なリソースであることを発見しています!Pythonでタプルの複数のリストの2番目の要素の交差を取得する簡単かつ効率的な方法は?

私が本当にやりたいことは容易(と計算が見つけるされ

list1 = [(111, 222), (111, 333), (111, 444)] 
list2 = [(555, 333), (555, 444), (555, 777)] 
list3 = [(123, 444), (123, 888), (123, 999)] 

、例えば、のは、私は一般的にフォーム(ID、値)の2要素のタプルの複数のリストに働いているとしましょう効率的な)方法でこれらのタプルの第2要素の交差を取得します。私は Python docsを見て、セットが私が望むことをするかもしれないことがわかりました...そして this postは私が2つのリストの交差を得る方法を理解する助けになりました。

newList1 = [] 
for tuple in list1: 
    newList1.append(tuple[1]) 
newList2 = [] 
for tuple in list2: 
    newList2.append(tuple[1]) 
newList3 = [] 
for tuple in list3: 
    newList3.append(tuple[1]) 

してから、このような各ペアの交点を取得:

i_of_1and2 = set(newList1).intersection(newList2) 
i_of_1and3 = set(newList2).intersection(newList3) 
i_of_2and3 = set(newList1).intersection(newList3) 

は、私はこのようなタプルをループすることにより、3全く新しい「値のみ」のリストを作ることができることを理解します

しかし私のリストは数十万(時には何千万もの)のタプルのように少し大きいです。これは実際には、これら3つのリストタプルの2番目の要素の共通部分を取得する最良の方法ですか?それは...私には優雅ではないようです。

ありがとうございました!

+2

一般的には、出力例を得ることをお勧めします。 –

+0

これらのリストは、各タプルの2番目の要素によって順序付けられていますか?あなたはそれらのリストの要素を並べ替えることが許されていますか?もしそうなら、マージソートのようなアルゴリズムを試して、データのコピーを避けることができます。 – liori

+0

良い点。出力は元のlist1とlist2の2番目の要素、つまり(ID、値)タプルの共通の値の交点を含むリストまたはセットになると期待します。だから、i_of_1と2のために、私は[333,444]を期待しています。 – CJH

答えて

3

あなたはvariable1そもそも大きな問題は、一般的に悪い示していますsign - 複数の値を使用する場合は、データ構造を使用し、番号の付いた名前の変数はあまり使わないでください。これはあなたのコードを何度も何度もやり直すのをやめ、バグを止めるのを助けます。

は、のではなく、リストのリストを使用してみましょう:

values = [ 
    [(111, 222), (111, 333), (111, 444)], 
    [(555, 333), (555, 444), (555, 777)], 
    [(123, 444), (123, 888), (123, 999)] 
] 

は、今、私たちはサブリスト内の各タプルの唯一の第二の要素を取得したいです。

>>> [[item[1] for item in sublist] for sublist in values] 
[[222, 333, 444], [333, 444, 777], [444, 888, 999]] 

そして、私達は項目間の交差点をしたい、我々は、2つの可能なの様々なペアを取得するためにitertools.combinations()を使用します:これはlist comprehensionを使用して計算するのは簡単ですので、

>>> for values, more_values in itertools.combinations(new_values, 2): 
...  set(values).intersection(more_values) 
... 
{444, 333} 
{444} 
{444} 

、場合私たちは一緒にこれをラップ:

たちを与える
import itertools 

values = [ 
    [(111, 222), (111, 333), (111, 444)], 
    [(555, 333), (555, 444), (555, 777)], 
    [(123, 444), (123, 888), (123, 999)] 
] 

sets_of_first_items = ({item[1] for item in sublist} for sublist in values) 
for values, more_values in itertools.combinations(sets_of_first_items, 2): 
    print(values.intersection(more_values)) 

を:

{444, 333} 
{444} 
{444} 

私がここで行った変更は、リストを作成してセットにするのを避け、リストの理解ではなくジェネレータ表現を使用することを怠って評価するので、内側のリストをセットしました。

あなたは私たちが交差点を生成するために使用されているリストのインデックスを望んでいた場合、最終的な注意として、それはthe enumerate() builtinを行うに簡単です:

sets_of_first_items = ({item[1] for item in sublist} for sublist in values) 
for (first_number, first_values), (second_number, second_values) in itertools.combinations(enumerate(sets_of_first_items), 2): 
    print("Intersection of {0} and {1}: {2}".format(first_number, second_number, first_values.intersection(second_values))) 

たちを与える:

Intersection of 0 and 1: {444, 333} 
Intersection of 0 and 2: {444} 
Intersection of 1 and 2: {444} 

編集:

noted by tonyl7126と同様に、これは大きなデータ構造を使用することで大きな助けになる問題です。ここでの最良の選択肢は、一連の製品IDにユーザーIDの辞書を使用することです。セットが必要なときだけリストとしてデータを格納し、それを後でセットに変換する理由はなく、dictは保管しようとしているデータのタイプにとってはるかに優れたソリューションです。

import itertools 

values = { 
    "111": {222, 333, 444}, 
    "555": {333, 444, 777}, 
    "123": {444, 888, 999} 
} 

for (first_user, first_values), (second_user, second_values) in itertools.combinations(values.items(), 2): 
    print("Intersection of {0} and {1}: {2}".format(first_user, second_user, first_values.intersection(second_values))) 

たちを与える:

は、次の例を参照してくださいあなたはまだPythonで辞書について読んだ場合

Intersection of 555 and 123: {444} 
Intersection of 555 and 111: {444, 333} 
Intersection of 123 and 111: {444} 
+0

+1同じように私はそれを行った:D唯一のことは、OPはPython 2.7を指定しているため、最終的な例は相互互換性がないということです。 – jamylak

+0

驚くばかり!私はこれをショット、おかげで与えます。学習の瞬間にも感謝しています。 – CJH

+0

@jamylak:yikes、互換性の問題はありますか? – CJH

1

set.intersection(...)メソッドが2つ以上のセットを取り、それらの交差を検出するという事実を利用することができます。また、list comprehensionsを使用してコードの膨張を減らすこともできます。最後に、argument list unpackingを使用して1つのライナーにすることができます。たとえば:

>>> list1 = [(111, 222), (111, 333), (111, 444)] 
>>> list2 = [(555, 333), (555, 444), (555, 777)] 
>>> list3 = [(123, 444), (123, 888), (123, 999)] 
>>> 
>>> set.intersection(*[set(t[1] for t in l) for l in (list1, list2, list3)]) 
set([444]) 

あなたは何が起こっているか理解するために、set.intersection(...)への呼び出しは、このPythonのコードと同等です:

>>> allsets = [] 
>>> for l in (list1, list2, list3): 
... n = set() 
... for t in l: 
...  n.add(t[1]) 
... allsets.append(n) 
... 
>>> allsets 
[set([444, 333, 222]), set([777, 444, 333]), set([888, 444, 999])] 
>>> allsets[0].intersection(allsets[1]).intersection(allsets[2]) 
set([444]) 
+0

ああ、これは私が使用するもののように見えます。ありがとう! – CJH

2

が、私はわからないんだけど、それはそれは何に合うかもしれないように思えますあなたはリストとの組み合わせでより良くしようとしています。辞書は、2要素タプルでエミュレートしているように、キーと値で構成されています。

たとえば、list1、list2、およびlist3は、次のようになる辞書として表現できます(111がidであると仮定します)。 your_dict = {"111":[222、333、444] "555":[333、444、777]、 "123":[444,888,999]

したがって、 "111"のような特定のIDのすべての値を取得したい場合は、書き込み: your_dict.get( "111") これはリストを返します。辞書のいくつかのドキュメントへのリンクもあります。 http://docs.python.org/library/stdtypes.html#typesmapping

+0

ヒントをありがとう! – CJH

+1

'your_dict.get(" 111 ")'を使うと言うのは本当に奇妙なことです。 Pythonの 'your_dict [" 111 "]'を使っている時間の99.9%。 –

1

これは簡単な方法です。

>>> list1 = [(111, 222), (111, 333), (111, 444)] 
>>> list2 = [(555, 333), (555, 444), (555, 777)] 
>>> list3 = [(123, 444), (123, 888), (123, 999)] 
>>> lists = [list1, list2, list3] 
>>> set.intersection(*(set(zip(*list)[1]) for list in lists)) 
set([444]) 
  1. zip *トリックタプルを解凍し、設定を第2の要素の を取得するために使用されます。
  2. set.intersection *は、それらをすべて一緒に交差するために使用されます。

効率に関しては、私はまず簡単な方法で試してみると、最適化を試みる前に十分に速い場合は を参照してください。

+0

素敵なトリック、ありがとう! – CJH

関連する問題