web-dev-qa-db-ja.com

Pythonでタプルのリストを検索する方法

だから私はこのようなタプルのリストを持っています:

_[(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
_

このリストは、数値が何かに等しいタプルに必要です。

したがって、search(53)を実行すると、_2_のインデックス値が返されます。

これを行う簡単な方法はありますか?

88
hdx
[i for i, v in enumerate(L) if v[0] == 53]

リスト内包表記 を使用できます:

>>> a = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
>>> [x[0] for x in a]
[1, 22, 53, 44]
>>> [x[0] for x in a].index(53)
2
48
Greg Hewgill

tl; dr

ジェネレータ式 は、おそらくあなたの問題に対する最もパフォーマンスが高く簡単な解決策です。

_l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

result = next((i for i, v in enumerate(l) if v[0] == 53), None)
# 2
_

説明

リストを理解することで、この質問に対する簡単な解決策を提供するいくつかの答えがあります。これらの答えは完全に正しいですが、最適ではありません。ユースケースによっては、いくつかの簡単な変更を加えることで大きなメリットが得られる場合があります。

このユースケースでリスト内包表記を使用するときに見られる主な問題は、全体のリストが処理されることですが、1要素

Pythonは、ここで理想的なシンプルな構造を提供します。 ジェネレータ式 と呼ばれます。以下に例を示します。

_# Our input list, same as before
l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

# Call next on our generator expression.
next((i for i, v in enumerate(l) if v[0] == 53), None)
_

このメソッドは、ささいな例のリスト内包表記と基本的に同じことを実行することが期待できますが、より大きなデータセットを使用している場合はどうでしょうか?そこで、ジェネレーターメソッドを使用する利点が発揮されます。新しいリストを作成するのではなく、既存のリストを反復可能オブジェクトとして使用し、next()を使用してジェネレーターから最初のアイテムを取得します。

いくつかの大きなデータセットでこれらのメソッドが異なる方法で実行する方法を見てみましょう。これらは10000000 + 1個の要素で構成された大きなリストで、ターゲットが最初(最良)または最後(最悪)にあります。次のリスト内包表記を使用して、これらのリストの両方が同等に機能することを確認できます。

リスト内包表記

"最悪の場合"

_worst_case = ([(False, 'F')] * 10000000) + [(True, 'T')]
print [i for i, v in enumerate(worst_case) if v[0] is True]

# [10000000]
#          2 function calls in 3.885 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.885    3.885    3.885    3.885 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
_

"最良の場合"

_best_case = [(True, 'T')] + ([(False, 'F')] * 10000000)
print [i for i, v in enumerate(best_case) if v[0] is True]

# [0]
#          2 function calls in 3.864 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.864    3.864    3.864    3.864 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
_

ジェネレーター式

ジェネレーターに関する私の仮説は次のとおりです。ジェネレーターは、最良の場合は大幅にパフォーマンスが向上しますが、最悪の場合も同様にパフォーマンスが向上することがわかります。このパフォーマンスの向上は、主にジェネレーターが遅延評価されるためです。つまり、値を生成するために必要なもののみを計算します。

最悪の場合

_# 10000000
#          5 function calls in 1.733 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         2    1.455    0.727    1.455    0.727 so_lc.py:10(<genexpr>)
#         1    0.278    0.278    1.733    1.733 so_lc.py:9(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    1.455    1.455 {next}
_

最良の場合

_best_case  = [(True, 'T')] + ([(False, 'F')] * 10000000)
print next((i for i, v in enumerate(best_case) if v[0] == True), None)

# 0
#          5 function calls in 0.316 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    0.316    0.316    0.316    0.316 so_lc.py:6(<module>)
#         2    0.000    0.000    0.000    0.000 so_lc.py:7(<genexpr>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    0.000    0.000 {next}
_

何?!最良の場合はリストの内包表記を吹き飛ばしますが、リストの内包表記よりもこの程度までパフォーマンスが悪いとは思っていませんでした。どう?率直に言って、私はさらなる研究なしでしか推測できませんでした。

このすべてを一粒の塩で処理します。ここでは、堅牢なプロファイリングは実行していません。非常に基本的なテストのみを実行しています。このタイプのリスト検索では、ジェネレータ式の方がパフォーマンスが高いことを理解するには十分です。

これはすべて基本的な組み込みのpythonであることに注意してください。何もインポートしたり、ライブラリを使用したりする必要はありません。

Peter Norvigの dacity cs212 コースで検索するためのこのテクニックを初めて見ました。

42
Jon Surrell

タプルは基本的にキーと値のペアです-python dict--:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
val = dict(l)[53]

編集-ああ、あなたは(53、 "xuxa")のインデックス値が欲しいと言います。これが実際にの場合、元のリストを反復処理するか、より複雑な辞書を作成する必要があります。

d = dict((n,i) for (i,n) in enumerate(e[0] for e in l))
idx = d[53]
26
Andrew Jaffe

うーん...まあ、頭に浮かぶ簡単な方法は、それを辞書に変換することです

d = dict(thelist)

アクセスd[53]

[〜#〜] edit [〜#〜]:おっと、初めて質問を読み違えた。特定の番号が格納されているインデックスを実際に取得したいようです。その場合、試してください

dict((t[0], i) for i, t in enumerate(thelist))

単純な古いdict変換の代わりに。その後、d[53]は2です。

12
David Z

リストが長く、数字が繰り返される可能性がある場合、 Python sortcontainers moduleSortedList タイプの使用を検討してください。 SortedListタイプは、タプルを番号順に自動的に維持し、高速検索を可能にします。

例えば:

from sortedcontainers import SortedList
sl = SortedList([(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")])

# Get the index of 53:

index = sl.bisect((53,))

# With the index, get the Tuple:

tup = sl[index]

これは、バイナリ検索を行うことにより、リストの理解の提案よりもはるかに高速に動作します。辞書の提案はさらに高速になりますが、異なる文字列で数字が重複する可能性がある場合は機能しません。

異なる文字列で番号が重複している場合は、もう1つの手順を実行する必要があります。

end = sl.bisect((53 + 1,))

results = sl[index:end]

54を二等分することにより、スライスの終了インデックスを見つけます。これは、受け入れられた回答と比較して、長いリストでは大幅に高速になります。

6
GrantJ

別の方法。

Zip(*a)[0].index(53)
1
RussW