第一个大于x的Python列表索引是什么?

111

如何用最符合Python风格的方式找到列表中第一个大于x的索引?

例如,对于以下列表:

list = [0.5, 0.3, 0.9, 0.8]

该函数
f(list, 0.7)

将返回

2.

70
不要将“list”用作变量名... - mshsayem
2
这个问题不够明确。答案是2,是因为0.9 > 0.7还是因为0.8 > 0.7?换句话说,你是按顺序搜索还是按递增的值搜索? - Sergey Orshanskiy
我投票关闭此问题作为一个重复的问题而不是做相反的操作,因为较新的问题更通用。 - Cristian Ciupitu
好的,所以这个函数获取列表中下一个更大的值,但它不会获取下一个最大的值?那应该是0.8,也就是索引3,如何实现呢? - Rudy Van Drie
12个回答

155
next(x[0] for x in enumerate(L) if x[1] > 0.7)

35
虽然我希望避免使用“魔法数字”,但以下代码可以找到第一个大于0.7的元素索引:next(idx for idx, value in enumerate(L) if value > 0.7)。 - mthurlin
46
对于简明性和 next(),加一;但为了可读性也许可以改成这样:next(i for i,v in enumerate(L) if v > 0.7) - Will Hardy
20
虽然外观不错,但在没有结果的情况下会引发令人困惑的 StopIteration。 - Virgil Dupras
3
@Wim: 但是你又退回到评估整个序列了。使用 itertools.chain() 替代添加列表的方法。 - Ignacio Vazquez-Abrams
8
@Wim 这里不需要使用 chain()。next() 函数接受第二个参数:next((i for i, x in enumerate(L) if x > value), -1) - jfs
显示剩余6条评论

57

如果列表已排序,则对于大型列表,bisect.bisect_left(alist, value)next(i for i, x in enumerate(alist) if x >= value) 更快。


不错的回答——对于我来说,使用一个小的4元素排序列表确实快了5到6倍,但是(不确定是否出错),当我使用timeit在一个长的10000元素的numpy数组上计时时,发现它比上面列表理解的答案慢大约两倍,这让我感到惊讶。 - ClimateUnboxed
2
@AdrianTompkins:你的基准测试有问题。bisect_left是O(log n),而listcomp是O(n),也就是说,n越大,bisect_left()的优势就越大。我尝试使用bisect_left()range(10**6)中查找500_000的索引-> 3.75微秒,而使用带有next()的genexpr -> 51.0毫秒[比预期慢了10_000倍]。 - jfs
1
通常情况下,对于SO来说,很难理解为什么这不被标记为解决方案。当然,早些时候已经有了一个答案,但是@c00kiemonster,如果你还活着并且在使用Python,请醒醒看看!12年后,这仍然比上面那个更好。 - Ricardo
如果我的列表的类型是“类”,该如何继续进行? - ignacio

21
>>> alist= [0.5, 0.3, 0.9, 0.8]
>>> [ n for n,i in enumerate(alist) if i>0.7 ][0]
2

2
如果'x'大于列表中的任何其他值,则它将失败。 - mshsayem
2
@mshsayem:对于这种情况,问题定义不清楚。失败可能是正确的做法。 - S.Lott
@S.Loot:好观点。如果不在列表中,则失败会导致将其分配给变量时出现可理解的错误:IndexError: list index out of range。使用 index = next[ n for n,i in enumerate(alist) if i>0.7 ] 错误会显示:NameError: name 'index' is not definednext 稍微快一些:60,000 个数字的时间差为 12.7 ns 对比 11.9 ns。 - Leo

16
filter(lambda x: x>.7, seq)[0]

4
虽然技术上是正确的,但不要使用过滤器,而应该使用列表推导式,这样可读性和性能都更好。 - mthurlin
filter(lambda x: x[1] > .7, enumerate(seq))[0][0] - 简单的线性搜索 - lowtech
5
@truppo在Python 3中,filter返回一个生成器,所以它应该不比列表推导式差? 另外,我发现这种方法比枚举解决方案更易读。 - BubuIIC
这个程序的一个不好的地方是,如果序列中没有大于0.7的项,就会进入异常处理。 - Brian C.
解决方案在技术上是不正确的。问题的作者询问如何在列表中找到元素的索引,但是这个解决方案返回了一个记录。更重要的是,在Python 3.8中,它比bisect_left()(最快的)和enumerate()慢。 - Sergey Nevmerzhitsky

11
这个答案通过使用enumerate在列表上进行简单搜索,以跟踪列表中的位置,并在找到第一个大于阈值的值时退出循环。如果没有满足条件的条目,则会引发错误。
for index, elem in enumerate(elements):
    if elem > reference:
        return index
raise ValueError("Nothing Found")

9

1) NUMPY ARGWHERE, 通用列表

如果您愿意使用numpy(在此处导入为np),则以下内容适用于通用列表(排序或未排序):

np.argwhere(np.array(searchlist)>x)[0]

或者如果您需要将答案作为整数索引:

np.argwhere(np.array(searchlist)>x)[0][0]

2) NUMPY SEARCHSORTED,排序的列表(在搜索列表中非常高效)

然而,如果您的搜索列表[l1,l2,...]已排序,则使用函数np.searchsorted更加简洁和美观:

np.searchsorted(searchlist,x)

使用这个函数的好处是,除了在搜索列表[l1,l2,...]中搜索单个值x之外,您还可以在搜索列表中搜索值列表[x1,x2,x3...xn](即x也可以是一个列表),在这种情况下与列表推导相比非常高效

5

另一个例子:

map(lambda x: x>.7, seq).index(True)

4
我知道已经有很多答案了,但是有时我觉得"pythonic"这个词被翻译成“一行代码”的意思。我认为更好的定义应该更接近于this answer:
"利用Python语言的特性编写清晰、简洁且易于维护的代码。"
虽然上述答案中有些简洁,但我并不认为它们很清晰,对于新手程序员来说需要一段时间才能理解,因此对于由许多技能水平组成的团队来说并不是非常易于维护。
l = [0.5, 0.3, 0.9, 0.8]

def f(l, x):
    for i in l:
        if i >x: break
    return l.index(i)


f(l,.7)

或者

l = [0.5, 0.3, 0.9, 0.8]

def f(l, x):
    for i in l:
        if i >x: return l.index(i)



f(l,.7)

我认为以上内容对于新手来说很容易理解,同时又足够简洁以被任何有经验的Python程序员接受。
我认为写愚蠢的代码是一种积极的做法。

我同意你对清晰代码的评论,但如果在大型代码中需要多次使用,使用循环会使其变得非常缓慢。 - ClimateUnboxed

2
>>> f=lambda seq, m: [ii for ii in xrange(0, len(seq)) if seq[ii] > m][0]
>>> f([.5, .3, .9, .8], 0.7)
2

看起来很不错。但理论上它将遍历整个列表,然后返回第一个结果(大于x),对吗?有没有办法使其在找到第一个结果后立即停止? - c00kiemonster
遍历整个列表有什么问题吗?如果第一个大于0.7的值接近列表末尾,则没有区别。 - ghostdog74
3
没错。但在这个特定的情况下,我打算使用该函数的列表非常长,因此我希望它在找到匹配项后立即停止遍历... - c00kiemonster
无论列表是否长,如果第一个值是列表中倒数第二个项目,则仍需遍历整个列表才能到达该位置! - ghostdog74
4
是的,但这并不是希望所有情况都成为最坏情况的原因。 - UncleBens

2

当我的列表非常长时,我遇到了类似的问题。理解或基于筛选器的解决方案会遍历整个列表。相反,itertools.takewhile 在条件第一次为false的时候就会打断循环:

from itertools import takewhile

def f(l, b): return len([x for x in takewhile(lambda x: x[1] <= b, enumerate(l))])

l = [0.5, 0.3, 0.9, 0.8]
f(l, 0.7)

1
为什么你不直接写 def f(l, b): return len(list(takewhile(lambda x: x[1] <= b, enumerate(l)))) 呢? - Avo Asatryan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接