在Python中,如何获得满足某个条件的列表的索引范围?

2024-10-03 23:18:20 发布

您现在位置:Python中文网/ 问答频道 /正文

例如,a=[12,11,5,7,2,21,32,13,6,42,1,8,9,0,32,38],我想找到它的值小于10的索引范围。有没有一种简单的方法可以返回索引范围[[2,4],[8],[10,13]]?在

我写了一个函数,但觉得很乏味:

def indexscope(dlist):
    newinterval = True
    scope = []
    for i in range(len(dlist)):
        if dlist[i] < 10:
            if newinterval:
                interval = [i]
                newinterval = False
            else:
                k = i
        else:
            if not newinterval:
                interval[1] = k + 1
                scope.append(interval)
                newinterval = True
    return scope

对于上面提到的例子,我可以使用indexscope(a)来获得结果。在

但是,如果我想获得下一次需要值大于20的另一个索引范围呢?丑八怪又写了一个函数?有没有一种简单的方法可以在一般水平上得到指数范围?

更新

1.是的,正如你们有些人所说,我需要的是指数范围。稍后我将处理每个索引范围中的元素。保持每个连续元素在同一范围内对我来说非常重要。在

因此,返回值[[2,4],[8],[10,13]]和[[2,3,4],[8],[10,11,12,13]]都可以接受。在

2.目前,@TigerhawkT3的答案可以满足我在例子中的要求。@德廷的回答抓住了我进一步的要求。但我仍在寻找一个更简单的方法。在

更新2

3.我终于得到了一个两行字的答案,参考了@TigerhawkT3的答案和互联网上的其他答案。现在我可以放弃函数定义,只需根据需要替换标准。谢谢你们在这里帮助我。在

^{pr2}$

Tags: 方法函数答案true元素if指数else
3条回答
a = [12,11,5,7,2,21,32,13,6,42,1,8,9,0,32,38]
indices = [idx for idx,val in enumerate(a) if val < 10]

这将创建一个list个索引:

^{pr2}$

为了便于解析,我建议保持这种方式,但您也可以将其转换为以下范围:

ranges = [[]]
for val in indices:
    if not ranges[-1] or ranges[-1][-1] == val-1:
        ranges[-1].append(val)
    else:
        ranges.append([val])

这将创建一个list范围:

^{4}$

现在去掉中间部分:

ranges = [[item[0],item[-1]] if len(item) > 1 else item for item in ranges]

结果:

[[2, 4], [8], [10, 13]]

如果您已经准备好使用numpy,为了获得所有元素的索引,numpy库中有一个简单的函数,名为numpy.nonzero(),您必须传入要检查的条件。在

示例-

In [1]: import numpy as np

In [2]: n = np.array([11,23,4,5,1222,33,6,10])

In [6]: ni = np.nonzero(n < 10)
Out[6]: (array([2, 3, 6]),)

In [7]: ni[0]
Out[7]: array([2, 3, 6])

在此之后,返回您想要的索引的第一个元素。在

如果您只需要满足特定条件的值,numpy库中还有另一个非常简单的解决方案可以满足您的需要,要使用的函数名为-numpy.where,请注意这将给出这些值的值,而不是索引。例如-

^{pr2}$

您可以让函数将函数作为参数,用作构建间隔的谓词:

def indexscope(dlist, predicate):
    scope = []
    start = end = -1
    for i, v in enumerate(dlist):
        if predicate(v):
            if start == -1:
                start = end = i
                continue
            if end + 1 == i:
                end = i
            else:
                scope.append([start] if start == end else [start, end])
                start = end = i
    if start != -1: 
        scope.append([start] if start == end else [start, end])
    return scope

a = [12,11,5,7,2,21,32,13,6,42,1,8,9,0,32,38]

def less_than_10(n):
    return n < 10

print(indexscope(a, less_than_10))
print(indexscope(a, lambda x: x > 20))


[[2, 4], [8], [10, 13]]
[[5, 6], [9], [14, 15]]

使用scipy:

^{pr2}$

结果将作为slice对象返回,但这对您非常有利,因为您可以使用它们来对抗原始np数组:

small_a = [12,11,5,7,2,21,32,13,6,42,1,8,9,0,32,38]
small_np_array = np.array(small_a)

valid_ranges = passing_ranges(small_np_array, lambda n: n < 10)

for r in valid_ranges:
    print(r[0], small_np_array[r])

slice(2, 5, None) [5 7 2]
slice(8, 9, None) [6]
slice(10, 14, None) [1 8 9 0]

基准

^{4}$

下面是您的答案,我甚至内联谓词以删除函数调用:

from itertools import groupby, count

def xibinke(a):
    l = [idx for idx,value in enumerate(a) if value<10]
    return [list(g) for _,g in groupby(l,key=lambda n,c=count():n-next(c))]

%timeit xibinke(large_a)
1 loops, best of 3: 14.6 s per loop

相关问题 更多 >