2011-12-01 99 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 
相关问题