查找列表中第 n 个项目的索引
Posted
技术标签:
【中文标题】查找列表中第 n 个项目的索引【英文标题】:Find the index of the n'th item in a list 【发布时间】:2012-01-10 08:17:40 【问题描述】:我想查找列表中第 n 次出现的项目的索引。例如,
x=[False,True,True,False,True,False,True,False,False,False,True,False,True]
第 n 个真的索引是多少?如果我想要第五次出现(如果索引为零,则为第四次),答案是 10。
我想出了:
indargs = [ i for i,a in enumerate(x) if a ]
indargs[n]
请注意,x.index
在某个时间点后返回第一次出现或第一次出现,因此据我所知不是解决方案。
对于与上述类似的情况,numpy 中也有一个解决方案,例如使用cumsum
和where
,但我想知道是否有无 numpy 的方法来解决问题。
我很担心性能问题,因为我在为 Project Euler 问题实施 Eratosthenes 筛法时第一次遇到此问题,但这是我在其他情况下遇到的更普遍的问题。
编辑:我得到了很多很好的答案,所以我决定做一些性能测试。下面是 timeit
以秒为单位的列表的执行时间,其中 len
nelements 搜索第 4000'th/1000'th True。列表是随机的真/假。源代码链接如下;有点乱。除了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 和 graddy 的解决方案在大多数情况下是次佳的,尽管当您想要 n 高的第 n 个实例或出现次数少于 n 的列表时,列表理解方法对于短数组可能更好。如果出现少于 n 次的机会,则最初的 count
检查会节省时间。此外,在搜索数字而不是 True/False 时,graddy's 效率更高......不清楚为什么会这样。 eyquem 的解决方案本质上等同于其他的解决方案,但开销或多或少; eyquem_occur 与 taymon 的解大致相同,而 eyquem_occurrence 与 listcomp 类似。
【问题讨论】:
编辑:我之前的评论假设您问的是不同的问题,而不是关于语法的问题。对不起。我不是 Python 人,但似乎应该能够用 for 循环计算你想要的次数,每次都增加你的计数器。将其封装在一个 while 循环中。所以 while(amountOfTrues我不能肯定这是最快的方式,但我想它会很好:
i = -1
for j in xrange(n):
i = x.index(True, i + 1)
答案是i
。
【讨论】:
好点...在大多数情况下,这可能比完整列表理解更有效。 +1 干得好。这是一个干净的解决方案,最大限度地利用 list.index 的 start 参数:-)【参考方案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
【讨论】:
【参考方案3】:如果您关心性能,最好看看是否可以进行算法优化。例如,如果您在相同的值上多次调用此函数,您可能希望缓存以前的计算(例如,一旦您找到一个元素的第 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
(如果你真的关心 enumerate 和其他技术的性能差异,你需要使用 profiling,尤其是 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]
【讨论】:
【参考方案4】:@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 速度完成,内存占用很小,使用惰性求值,没有变量分配,以及可单独测试的组件。 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
【讨论】:
我喜欢它,虽然我倾向于将第一个子计算拆分为“def item_indices(iterable, item):”所以我可以给它一个文档字符串。 太棒了。现在为什么不是内置的list
方法?
旁注:是否可以在 python 2.6 中安装 itertools 2.7?还是存在根本的不兼容?也许我应该把这个作为一个不同的问题来问......
@keflavich 我不知道在不重建 Python 的情况下反向移植 2.7 itertools 的简单方法,但您可以实现纯 Python 等效项,如 2.7 itertools 文档中所示。试试这个:compress = lambda data, selectors: (d for d, s in izip(data, selectors) if s)
.【参考方案5】:
[y for y in enumerate(x) if y[1]==True][z][0]
注意:这里 Z 是第 n 次出现,
【讨论】:
非常优雅。一个更清晰的版本符合我的口味:[i for i, e in enumerate(x) if e == True][z].【参考方案6】:首先创建一个列表对象并返回该列表的第n-1个元素的解决方案:函数occurence()
我认为,还有一个解决方案也可以实现函数式程序员的梦想,使用生成器,因为我喜欢它们:function occur()
S = '***.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 '***.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
第二种解决方案看起来很复杂,但实际上并非如此。它不需要完全遍历可迭代对象:一旦找到所需的事件,进程就会停止。
【讨论】:
【参考方案7】:这是在列表itrbl
中查找nth
出现的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
【讨论】:
【参考方案8】:这是一种方法: 对于上面的例子:
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
【讨论】:
【参考方案9】:我认为这应该可行。
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
【讨论】:
【参考方案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
:
【讨论】:
【参考方案11】:你可以使用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
的元素。
【讨论】:
以上是关于查找列表中第 n 个项目的索引的主要内容,如果未能解决你的问题,请参考以下文章