2011-12-01 75 views
37

我想查找列表中第n個項目發生的索引。例如,查找列表中第n個項目的索引

x=[False,True,True,False,True,False,True,False,False,False,True,False,True] 

第n個真值的指數是多少?如果我想第五發生(第4,如果零索引),答案是10

我想出:

indargs = [ i for i,a in enumerate(x) if a ] 
indargs[n] 

注意x.index返回第一次出現或經過一番首次出現點,因此,據我所知,不是一個解決方案。

對於類似於上述情況的情況,在numpy中也存在解決方案,例如,使用cumsumwhere,但我想知道是否有一個numpy自由的方式來解決這個問題。

自從我第一次遇到這個問題時,我擔心性能問題,同時實施了Eratosthenes篩選問題Project Euler問題,但這是我在其他情況下遇到的一個更普遍的問題。

編輯:我得到了很多很好的答案,所以我決定做一些性能測試。以下是timeit執行時間,以len元素搜索第4000/1000個真的列表的秒數執行。這些列表是隨機的真/假。下面鏈接的源代碼;這是一個混亂。我使用海報名稱的短/修改版本來描述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 

Hettinger的itertools解決方案几乎總是最好的。 taymon's和graddy的解決方案在大多數情況下是次佳的,但是當你想要第n個實例使得n很高或列表中出現少於n個事件時,列表理解方法對於短陣列可能更好。如果有可能出現少於n次的情況,則最初的count檢查可節省時間。另外,當搜索數字而不是True/False時,graddy的效率更高......不清楚原因是什麼。 eyquem的解決方案基本上等同於其他開銷略微增加或減少的其他解決方案; eyquem_occur與taymon的解決方案大致相同,而eyquem_occurrence與listcomp相似。

+0

編輯:我以前的評論假設你問的是不同的問題,而不是語法。抱歉。我不是Python傢伙,但它似乎應該能夠計算出無論你想用for循環發生多少次事件,每次都增加計數器。在一個while循環中加以解析。因此,雖然(amountOfTrues varatis

+3

+ 1爲傑出的答覆比較答案。做得好! –

回答

34

@Taymon使用list.index的答案很棒。

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的功能的工具集。請注意,一旦流水線設置完成,Python的eval循環就沒有任何行程 - 所有事情都以C速度完成,內存佔用極小,延遲評估,無變量分配以及可單獨測試的組件。督察,它是一切功能的程序員夢想:-)

採樣運行:

>>> 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 
+0

我喜歡它,但我傾向於將第一個將其計算爲「def item_indices(iterable,item):」所以我可以給它一個文檔字符串。 – ncoghlan

+0

太棒了。現在爲什麼不是一個內置的'list'方法? – keflavich

+0

旁註:是否有可能在python 2.6中安裝itertools 2.7?還是有根本的不兼容性?也許我應該問這是一個不同的問題... – keflavich

27

我不能肯定地說,這是最快的方式,但我想它會是不錯的:

i = -1 
for j in xrange(n): 
    i = x.index(True, i + 1) 

答案是i

+0

好點......對於大多數情況來說,這可能比完整的列表理解更有效。 – keflavich

+3

+1不錯的工作。這是一個乾淨的解決方案,最大限度地利用了* start *參數* list.index * :-) –

+0

我喜歡你的風格 - 看起來簡單地編碼:) – Ralf

2

如果效率是一個問題,我認爲其更好地迭代正常(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 
2

如果你關心性能,以儘快停止,你是最好的關閉看是否有算法最優化你(們)能做到。例如,如果您使用相同的值多次調用此函數,則可能希望緩存先前的計算(例如,一旦找到元素的第50次出現,您可以在O(1)時間內找到以前發生的任何事件)。

否則,你想確保你的技術對(惰性)迭代器有效。

最* *優雅和性能的快樂方式,我能想到實施它的是:

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 

(如果你真的關心枚舉和其他技術之間的性能差異,你會需要訴諸紋,尤其是與numpy的功能,其可以訴諸C)

要預處理整個流和支持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]} 
2
[y for y in enumerate(x) if y[1]==True][z][0] 

注:這裏Z是第n個次數,

+0

非常優雅。一個稍微更清晰的版本,以我的口味:[我爲我,如果e ==真的[z]枚舉(x)中]。 – markolopa

2

,首先創建一個解決方案列表對象並返回此列表的第n-1個元素:函數發生()

而且一個滿足函數程序的解決方案ers'dreams太,我認爲,使用發電機,因爲我愛他們:功能發生()

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

這裏是另一種方式來找到一個列表itrblnth發生x

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 
0

這裏是一個辦法:
對於上面的例子:

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 
0

我認爲這應該工作。

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 
相關問題