Python - 在列表中根据不同标准查找前后值

2 投票
2 回答
2357 浏览
提问于 2025-04-18 17:52

我想在Python中遍历一个数字列表,找到某个特定元素的索引,然后找出与它最近的符合某些条件的元素。我找不到任何内置的函数可以让我在列表中保持当前位置,以便我可以根据不同的搜索条件找到前后的项目。Python中有这样的东西吗?

我有一个很长的数字列表,我想在其中找到一个特定的重复模式。

比如说:

L = [1, 1, 3, 5, 7, 5, 1, 2, 1, 1, 1, 8, 9, 1, 1, 1]

假设我想通过寻找列表中第一个大于4的数字的索引来找到“峰值”,然后再找出两边最近的小于2的数字的索引。接着我想找到下一个峰值,重复这个过程。(实际上这个模式比这复杂得多。)

所以在这个例子中,我最终想要的输出是1:6, 10:13。

我用这个来找到第一个值:

a = next(i for i, v in enumerate(L) if v > 4)

或者用这个来找到所有大于4的值,以便后面进行分组:

indexes = [i for i, v in enumerate(L) if v > 4]

我尝试过next、iter、生成器、各种for循环等等,但都没有成功。我也看过islice,但感觉每找到一个索引就把列表切成两部分,然后在这两部分上进行正向和反向搜索,这样做太复杂了。难道没有更简单的方法吗?

任何帮助都将非常感谢。

2 个回答

2

这个方法可以找到你提到的峰值,但需要知道列表的起始索引,以便确定从哪里开始搜索:

def findpeaks(lst, index, v1, v2):
    l = lst[index:]
    ele = next(i for (i, v) in enumerate(l) if v > v1)
    idx = ele - next(i for (i, v) in enumerate(l[:ele+1][::-1]) if v < v2)
    jdx = ele + next(i for (i, v) in enumerate(l[ele:]) if v < v2)
    # Returns a tuple containing:
    #
    # the index of the element > v1
    # the index of the element < v2 (found before the element > v1),
    # the index of the element < v2 (found after the element > v1).
    return (ele + index, idx + index, jdx + index)

这个过程是这样进行的:

  1. 首先找到第一个值符合条件的元素(在你的例子中是大于4)
  2. 然后找到这个索引之前的元素,看看哪个元素符合第二个值的条件(小于2)。这个过程是通过从第一步找到的索引开始,创建一个列表的切片,然后把这个切片反转过来。接着,你找到的索引需要从第一步的索引中减去。
  3. 接下来,向前搜索,创建一个原始列表的切片,从那里继续查找。
  4. 最后的结果需要考虑到起始索引,所以要把这个索引加到结果中。就这样。

举个例子:

L = [1, 1, 3, 5, 7, 5, 1, 2, 1, 1, 1, 8, 9, 1, 1, 1]

print findpeaks(L, 0, 4, 2) # prints (3, 1, 6)
print findpeaks(L, 6, 4, 2) # prints (11, 10, 13) 

接下来的明显步骤是找到所有符合这个条件的元素。一个建议是可以把这个过程做成递归的——不过这个你可以自己尝试。

2

我会使用一个生成器函数,并在遍历输入数据时记录符合你条件的索引:

L = [1, 1, 3, 5, 7, 5, 1, 2, 1, 1, 1, 8, 9, 1, 1, 1]

def peak_groups(l):
    start_i = 0
    peak_i = None
    for i,x in enumerate(l):
        if peak_i is None:
            # look for start of peak group, or peak itself
            if x < 2:
                start_i = i
            elif x > 6:
                peak_i = i
        else:
            # look for end of peak group
            if x < 2:
                yield (start_i, peak_i, i)
                start_i = i
                peak_i = None

    # finally check edge condition if we reached the end of the list
    if peak_i is not None:
        yield (start_i, peak_i, i)

for group in peak_groups(L):
    print group

这样会得到:

(1, 4, 6)
(10, 11, 13)

好处是你只需要遍历输入数据一次。虽然在实际情况中,分组条件可能没有那么简单。

你需要考虑如果多个“峰值组”重叠时应该怎么处理,目前这个方法并没有找到组中最大的峰值,但这应该是一个好的起点。

撰写回答