リスト内のn番目のアイテムのインデックスを検索したい。例えば。、
x=[False,True,True,False,True,False,True,False,False,False,True,False,True]
N番目の真のインデックスは何ですか? 5回目(インデックスが0の場合は4回目)が必要な場合、答えは10です。
私は思いついた:
indargs = [ i for i,a in enumerate(x) if a ]
indargs[n]
ご了承ください x.index
は、最初の出現、またはあるポイントの後の最初の出現を返すため、私が知る限り、解決策ではありません。
上記と同様の場合のnumpyの解決策もあります。 cumsum
とwhere
を使用しますが、問題を解決するための無数の方法があるかどうか知りたいのですが。
Project Euler 問題のためにSieve of Eratosthenesを実装したときに最初にこれに遭遇して以来、パフォーマンスが心配ですが、これは他の状況で遭遇したより一般的な質問です。
編集:私はたくさんの素晴らしい答えを得たので、私はいくつかのパフォーマンステストを行うことにしました。以下は、4000番目/ 1000番目のTrueを検索するtimeit
要素を持つリストのlen
実行時間(秒単位)です。リストはランダムなTrue/Falseです。以下にリンクされているソースコード。ちょっと面倒です。上記の単純なリスト内包表記であるlistcomp
を除いて、ポスターの名前の短い/変更されたバージョンを使用して関数を説明しました。
True Test (100'th True in a list containing True/False)
nelements eyquem_occur eyquem_occurrence graddy taymon listcomp hettinger26 hettinger
3000: 0.007824 0.031117 0.002144 0.007694 0.026908 0.003563 0.003563
10000: 0.018424 0.103049 0.002233 0.018063 0.088245 0.003610 0.003769
50000: 0.078383 0.515265 0.002140 0.078074 0.442630 0.003719 0.003608
100000: 0.152804 1.054196 0.002129 0.152691 0.903827 0.003741 0.003769
200000: 0.303084 2.123534 0.002212 0.301918 1.837870 0.003522 0.003601
True Test (1000'th True in a list containing True/False)
nelements eyquem_occur eyquem_occurrence graddy taymon listcomp hettinger26 hettinger
3000: 0.038461 0.031358 0.024167 0.039277 0.026640 0.035283 0.034482
10000: 0.049063 0.103241 0.024120 0.049383 0.088688 0.035515 0.034700
50000: 0.108860 0.516037 0.023956 0.109546 0.442078 0.035269 0.035373
100000: 0.183568 1.049817 0.024228 0.184406 0.906709 0.035135 0.036027
200000: 0.333501 2.141629 0.024239 0.333908 1.826397 0.034879 0.036551
True Test (20000'th True in a list containing True/False)
nelements eyquem_occur eyquem_occurrence graddy taymon listcomp hettinger26 hettinger
3000: 0.004520 0.004439 0.036853 0.004458 0.026900 0.053460 0.053734
10000: 0.014925 0.014715 0.126084 0.014864 0.088470 0.177792 0.177716
50000: 0.766154 0.515107 0.499068 0.781289 0.443654 0.707134 0.711072
100000: 0.837363 1.051426 0.501842 0.862350 0.903189 0.707552 0.706808
200000: 0.991740 2.124445 0.498408 1.008187 1.839797 0.715844 0.709063
Number Test (750'th 0 in a list containing 0-9)
nelements eyquem_occur eyquem_occurrence graddy taymon listcomp hettinger26 hettinger
3000: 0.026996 0.026887 0.015494 0.030343 0.022417 0.026557 0.026236
10000: 0.037887 0.089267 0.015839 0.040519 0.074941 0.026525 0.027057
50000: 0.097777 0.445236 0.015396 0.101242 0.371496 0.025945 0.026156
100000: 0.173794 0.905993 0.015409 0.176317 0.762155 0.026215 0.026871
200000: 0.324930 1.847375 0.015506 0.327957 1.536012 0.027390 0.026657
ヘッティンガーのitertoolsソリューションはほとんどの場合最高です。 taymonとgraddyのソリューションは、ほとんどの状況で次善の策ですが、リストの理解のアプローチは、nが高いようなn番目のインスタンスまたはn未満のリストがある場合に短い配列に適しています。発生回数がn回未満である可能性がある場合は、最初のcount
チェックで時間を節約できます。また、True/Falseの代わりに数字を検索する場合は、graddyの方が効率的です...その理由は明確ではありません。 eyquemのソリューションは、基本的に他のソリューションと同等ですが、オーバーヘッドが多少異なります。 eyquem_occurrenceはlistcompに似ていますが、eyquem_occurはテイモンのソリューションとほぼ同じです。
list.indexを使用した@Taymonからの回答は素晴らしかったです。
FWIW、これが itertools module を使用した機能的なアプローチです。リストだけでなく、反復可能な入力でも機能します。
>>> from itertools import compress, count, imap, islice
>>> from functools import partial
>>> from operator import eq
>>> def nth_item(n, item, iterable):
indicies = compress(count(), imap(partial(eq, item), iterable))
return next(islice(indicies, n, None), -1)
この例は、Pythonの機能ツールセットを効果的に組み合わせる方法を示しているためNiceです。パイプラインがセットアップされると、Pythonのevalループを回避する必要がないことに注意してください。すべてがCの速度で、小さなメモリフットプリントで、遅延評価で、変数の割り当てなしで、個別にテスト可能なコンポーネントで実行されます。 IOW、それは関数型プログラマーが夢見るすべてです:-)
サンプル実行:
>>> x = [False,True,True,False,True,False,True,False,False,False,True,False,True]
>>> nth_item(50, True, x)
-1
>>> nth_item(0, True, x)
1
>>> nth_item(1, True, x)
2
>>> nth_item(2, True, x)
4
>>> nth_item(3, True, x)
6
これが最速の方法であるとは断言できませんが、かなり良いと思います。
i = -1
for j in xrange(n):
i = x.index(True, i + 1)
答えはi
です。
効率が問題である場合、O(N)をとるリスト内包ではなく、通常(O(L))を繰り返すほうがよいと思います。ここで、Lはリストの長さです。
例:非常に大きなリストを考えて、最初の発生を見つけたいN = 1最初の発生を見つけたらすぐに停止するのが明らかに良い
count = 0
for index,i in enumerate(L):
if i:
count = count + 1
if count==N:
return index
パフォーマンスに関心がある場合は、アルゴリズムによる最適化が可能かどうかを確認するのが最善です。たとえば、同じ値でこの関数を何度も呼び出す場合は、以前の計算をキャッシュすることができます(たとえば、要素の50番目のオカレンスを見つけたら、O(1)
時間で以前のオカレンスを見つけることができます。 )。
それ以外の場合は、テクニックが(遅延)イテレーターで機能することを確認する必要があります。
私がそれを実装することを考えることができる最も* in *エレガントでパフォーマンスに満足している方法は次のとおりです:
_def indexOfNthOccurrence(N, element, stream):
"""for N>0, returns index or None"""
seen = 0
for i,x in enumerate(stream):
if x==element:
seen += 1
if seen==N:
return i
_
(列挙と他の手法のパフォーマンスの違いを本当に気にする場合は、特にCに頼る可能性のあるnumpy関数を使用して、プロファイリングに頼る必要があります)
ストリーム全体を前処理し、O(1)
クエリをサポートするには:
_from collections import *
cache = defaultdict(list)
for i,elem in enumerate(YOUR_LIST):
cache[elem] += [i]
# e.g. [3,2,3,2,5,5,1]
# 0 1 2 3 4 5 6
# cache: {3:[0,2], 1:[6], 2:[1,3], 5:[4,5]}
_
最初にリストオブジェクトを作成し、このリストのn番目の1要素を返すソリューション:function occurence()
そして、私はそれらを愛しているので、ジェネレーターを使用して、関数型プログラマーの夢も満たすソリューションだと思います:function occur()
S = 'stackoverflow.com is a fantastic amazing site'
print 'object S is string %r' % S
print "indexes of 'a' in S :",[indx for indx,elem in enumerate(S) if elem=='a']
def occurence(itrbl,x,nth):
return [indx for indx,elem in enumerate(itrbl)
if elem==x ][nth-1] if x in itrbl \
else None
def occur(itrbl,x,nth):
return (i for pos,i in enumerate(indx for indx,elem in enumerate(itrbl)
if elem==x)
if pos==nth-1).next() if x in itrbl\
else None
print "\noccurence(S,'a',4th) ==",occurence(S,'a',4)
print "\noccur(S,'a',4th) ==",occur(S,'a',4)
結果
object S is string 'stackoverflow.com is a fantastic amazing site'
indexes of 'a' in S : [2, 21, 24, 27, 33, 35]
occur(S,'a',4th) == 27
occurence(S,'a',4th) == 27
2番目の解決策は複雑に見えますが、実際にはそうではありません。 iterableを完全に実行する必要はありません。必要なオカレンスが見つかるとすぐにプロセスが停止します。
[y for y in enumerate(x) if y[1]==True][z][0]
注:ここで、Zはn番目のオカレンスです。
リストnth
でx
のitrbl
のオカレンスを見つける別の方法を次に示します。
def nthoccur(nth,x,itrbl):
count,index = 0,0
while count < nth:
if index > len(itrbl) - 1:
return None
Elif itrbl[index] == x:
count += 1
index += 1
else:
index += 1
return index - 1
これでうまくいくと思います。
def get_nth_occurrence_of_specific_term(my_list, term, n):
assert type(n) is int and n > 0
start = -1
for i in range(n):
if term not in my_list[start + 1:]:
return -1
start = my_list.index(term, start + 1)
return start
count を使用できます:
_from itertools import count
x = [False, True, True, False, True, False, True, False, False, False, True, False, True]
def nth_index(n, item, iterable):
counter = count(1)
return next((i for i, e in enumerate(iterable) if e == item and next(counter) == n), -1)
print(nth_index(3, True, x))
_
出力
_4
_
アイデアは、e == item and next(counter) == n)
の短絡特性により、式next(counter) == n
は_e == item
_の場合にのみ評価されるため、item
。
ここに方法があります:
上記の例の場合:
x=[False,True,True,False,True,False,True,False,False,False,True,False,True]
関数find_indexを定義できます
def find_index(lst, value, n):
c=[]
i=0
for element in lst :
if element == value :
c .append (i)
i+=1
return c[n]
そして、関数を適用すると:
nth_index = find_index(x, True, 4)
print nth_index
結果は次のとおりです。
10
next
とenumerate
およびジェネレータ式を使用できます。 itertools.islice
必要に応じて、イテラブルをスライスできます。
from itertools import islice
x = [False,True,True,False,True,False,True,False,False,False,True,False,True]
def get_nth_index(L, val, n):
"""return index of nth instance where value in list equals val"""
return next(islice((i for i, j in enumerate(L) if j == val), n-1, n), -1)
res = get_nth_index(x, True, 3) # 4
イテレータが使い果たされた場合、つまり、指定された値のn番目のオカレンスが存在しない場合、next
はデフォルト値を返すことができます。この場合には -1
: