3

私はPythonを初めて使用し(2.7で動作)、SOが非常に貴重なリソースであることに気づいています。

一般に(ID、値)の形式の2要素タプルのいくつかのリストを使用しているとしましょう。

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

私が本当にやりたいのは、これらのタプルの2番目の要素の交差を取得する簡単な(そして計算効率の高い)方法を見つけることです。Pythonのドキュメントを調べたところ、セットが私が望むことを実行する可能性があることがわかりました...この投稿は、2つのリストの共通部分を取得する方法を理解するのに役立ちました。

次のようにタプルをループすることで、3つのまったく新しい「値のみ」のリストを作成できることを理解しています。

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つのリストタプルの2番目の要素の交差を取得するための本当に最良の方法ですか?それは...私には...不法な...のようです。

助けてくれてありがとう!

4

4 に答える 4

3

そもそも大きな問題を示しているのvariable1は、一般的に悪い兆候です。複数の値が必要な場合は、名前が付けられた多くの変数ではなく、データ構造を使用してください。これにより、コードを何度も繰り返すことがなくなり、バグを防ぐことができます。

代わりにリストのリストを使用しましょう:

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

ここで、サブリスト内の各タプルの2番目の要素のみを取得します。これは、リスト内包表記を使用して計算するのに十分簡単です。

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

次に、アイテム間の交差が必要です。これを使用itertools.combinations()して、2つのさまざまなペアを取得します。

>>> 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}

ここで行った変更は、内部リストをセット内包表記にすること、リストをセットに変換するためだけにリストを作成することを回避すること、および遅延評価されるため、リスト内包表記ではなくジェネレータ式を使用することでした。

最後に、交差点の生成に使用しているリストのインデックスが必要な場合はenumerate()組み込みの機能を使用するのは簡単です。

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}

編集:

tonyl7126で指摘されているように、これは、より優れたデータ構造を使用することで大いに役立つ可能性のある問題でもあります。ここでの最良のオプションは、一連の製品IDにユーザーIDのdictを使用することです。セットのみが必要で、後でセットに変換する場合は、データをリストとして保存する理由はありません。保存しようとしているデータの種類に対して、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)))

私たちに与える:

Intersection of 555 and 123: {444}
Intersection of 555 and 111: {444, 333}
Intersection of 123 and 111: {444}
于 2012-05-14T01:38:09.550 に答える
2

Pythonの辞書についてまだ読んだことがあるかどうかはわかりませんが、リストと組み合わせてもっとうまくやろうとしていることに合うかもしれません。辞書は、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

于 2012-05-14T01:45:30.150 に答える
1

set.intersection(...)メソッドが2つ以上のセットを取り、それらの共通部分を見つけるという事実を利用できます。また、リスト内包表記を使用して、コードの膨張を減らすことができます。そして最後に、引数リストの解凍を使用して、ワンライナーにすることができます。例えば:

>>> 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])
于 2012-05-14T01:36:20.080 に答える
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 *は、それらをすべて交差させるために使用されます。

効率に関しては、最初に簡単な方法を試して、最適化を試みる前にそれが十分に速いかどうかを確認します。

于 2012-05-14T04:38:43.333 に答える