【问题标题】:Detect peaks in list of numbers and record their positions检测数字列表中的峰值并记录它们的位置
【发布时间】:2019-05-23 16:18:06
【问题描述】:

我正在尝试创建一些代码来返回数值数组的“峰值”(或局部最大值)的位置和值。

例如,列表arr = [0, 1, 2, 5, 1, 0]3 位置有一个峰值,其值为5(因为arr[3] 等于5)。

数组的第一个和最后一个元素不会被视为峰值(在数学函数的上下文中,您不知道之后和之前是什么,因此,您不知道它是峰值还是不是)。

def pick_peaks(arr):
    print(arr)
    posPeaks = {
        "pos": [],
        "peaks": [],
    }
    startFound = False
    n = 0
    while startFound == False:
        if arr[n] == arr[n+1]:
            n += 1
        else:
            startFound = True

    endFound = False
    m = len(arr) - 1
    while endFound == False:
        if arr[m] == arr[m-1]:
            m -= 1
        else:
            endFound = True

    for i in range(n+1, m):
        if arr[i] == arr[i-1]:
            None
        elif arr[i] >= arr[i-1] and arr[i] >= arr[i+1]:
            posPeaks["pos"].append(i)
            posPeaks["peaks"].append(arr[i])

    return posPeaks

我的问题是高原。 [1, 2, 2, 2, 1] 有一个峰值,而[1, 2, 2, 2, 3] 没有。当高原为峰时,记录高原的第一个位置。

感谢任何帮助。

【问题讨论】:

  • 在第一个高原示例中,高原的值为 2,但它的位置是什么?
  • @quamrana 啊是的我忘了加这个,当高原是高峰时,使用高原的第一个位置。
  • 一些要点 1. [] 创建列表,而不是数组。 2. 尽可能使用列表推导、映射...而不是 for/while 循环 3. 寻找 enumerat 和 argmax 函数

标签: python list dictionary comparison-operators


【解决方案1】:

我建议您使用groupby 对连续的相等值进行分组,然后为每个组存储第一个位置,例如[1, 2, 2, 2, 1],它会在元组列表[(1, 0), (2, 1), (1, 4)] 之后创建以下列表,将所有内容放在一起:

from itertools import groupby


def peaks(data):
    start = 0
    sequence = []
    for key, group in groupby(data):
        sequence.append((key, start))
        start += sum(1 for _ in group)

    for (b, bi), (m, mi), (a, ai) in zip(sequence, sequence[1:], sequence[2:]):
        if b < m and a < m:
            yield m, mi


print(list(peaks([0, 1, 2, 5, 1, 0])))
print(list(peaks([1, 2, 2, 2, 1])))
print(list(peaks([1, 2, 2, 2, 3])))

输出

[(5, 3)]
[(2, 1)]
[]

【讨论】:

    【解决方案2】:

    我知道我参加聚会可能有点晚了,但我想分享我使用 NumPy 数组的解决方案:

    def get_level_peaks(v):
        peaks = []
    
        i = 1
        while i < v.size-1:
            pos_left = i
            pos_right = i
    
            while v[pos_left] == v[i] and pos_left > 0:
                pos_left -= 1
    
            while v[pos_right] == v[i] and pos_right < v.size-1:
                pos_right += 1
    
            is_lower_peak = v[pos_left] > v[i] and v[i] < v[pos_right]
            is_upper_peak = v[pos_left] < v[i] and v[i] > v[pos_right]
    
            if is_upper_peak or is_lower_peak:
                peaks.append(i)
    
            i = pos_right
    
        peaks = np.array(peaks)
    
        """
        # uncomment this part of the code
        # to include first and last positions
    
        first_pos, last_pos = 0, v.size-1
        peaks = np.append([first_pos], peaks)
        peaks = np.append(peaks, [last_pos])
        """
    
        return peaks
    
    v = np.array([7, 2, 0, 4, 4, 6, 6, 9, 5, 5])
    p = get_peaks(v)
    print(v)        # [7 2 0 4 4 6 6 9 5 5]
    print(p)        # [0 2 7 9] (peak indexes)
    print(v[p])     # [7 0 9 5] (peak elements)
    
    v = np.array([8, 2, 1, 0, 1, 2, 2, 5, 9, 3])
    p = get_peaks(v)
    print(v)        # [8 2 1 0 1 2 2 5 9 3]
    print(p)        # [0 3 8 9] (peak indexes)
    print(v[p])     # [8 0 9 3] (peak elements)
    
    v = np.array([9, 8, 8, 8, 0, 8, 9, 9, 9, 6])
    p = get_peaks(v)
    print(v)        # [9 8 8 8 0 8 9 9 9 6]
    print(p)        # [0 4 6 9] (peak indexes)
    print(v[p])     # [9 0 9 6] (peak elements)
    

    在示例 3 中,我们有一个从索引 6 到索引 8 的平坦上峰。在这种情况下,索引将始终指示高原的最左侧位置。如果要指示中间位置或最右边位置,只需更改这部分代码即可:

            ...
    
            if is_upper_peak or is_lower_peak:
                peaks.append(i)
    
            ...
    

    到这里:

            ...
    
            # middle position
            if is_upper_peak or is_lower_peak:
                peaks.append((pos_left + pos_right) // 2)
    
            ...
    
            ...
    
            # rightmost position
            if is_upper_peak or is_lower_peak:
                peaks.append(pos_right)
    
            ...
    

    【讨论】:

      【解决方案3】:

      此代码采用窗口编号并给出该窗口大小内的峰值

      l=[1,2,3,4,5,4,3,2,1,2,3,4,3,2,4,2,1,2]
      n=int(input("The size of window on either side "))
      for i in range(n,len(l)-n):
          if max(l[i-n:i]+l[i+1:i+n+1])<l[i]:
              print(l[i],' at index = ',i)
      

      【讨论】:

        【解决方案4】:

        如果您可以预处理数据以删除重复数字并仅保留 1 个唯一数字,则您也可以对高原使用相同的算法。因此,您可以将示例 [1, 2, 2, 2, 1] 转换为 [1, 2, 1] 并应用相同的算法。

        编辑: 代码:

        from itertools import groupby
        
        def process_data(data):
            return [list(val for num in group) for val, group in groupby(data)]
        
        
        def peaks(arr):
            #print(arr)
            posPeaks = {
            "pos": [],
            "peaks": [],
            }
            startFound = False
            n = 0
            while startFound == False:
                if arr[n][0] == arr[n+1][0]:
                    n += 1
                else:
                    startFound = True
        
            endFound = False
            m = len(arr) - 1
            while endFound == False:
                if arr[m][0] == arr[m-1][0]:
                    m -= 1
                else:
                    endFound = True
        
            for i in range(n+1, m):
                if arr[i][0] == arr[i-1][0]:
                    None
                elif arr[i][0] >= arr[i-1][0] and arr[i][0] >= arr[i+1][0]:
                    pos = sum([len(arr[idx]) for idx in range(i)])
                    posPeaks["pos"].append(pos) #.append(i)
                    posPeaks["peaks"].append(arr[i][0])
            return posPeaks
        
        
        
        print(peaks(process_data([0, 1, 2, 5, 1, 0])))
        print(peaks(process_data([1, 2, 2, 2, 1])))
        print(peaks(process_data([1, 2, 2, 2, 3])))
        

        输出:

        {'pos': [3], 'peaks': [5]}
        {'pos': [1], 'peaks': [2]}
        {'pos': [], 'peaks': []}
        

        【讨论】:

        • 这不会搞砸后面的山峰位置吗?
        • 是的,它会的。在这种情况下,您可以创建一个数字列表。例如:[1, 2, 2, 2, 1][[1], [2, 2, 2], [1]]。修改您的代码以仅检查列表中每个列表的第一个元素。因此,在这种情况下,您的循环将遍历 1,2 和 1。在返回位置时,您可以使用每个列表的长度返回索引位置。
        • 查看编辑后的帖子,使用相同的代码保留索引,几乎没有修改。
        【解决方案5】:

        这是一个相当简单的生成器函数。只需循环并保持必要的状态:i(“增长”的最后一个索引),up(如果最后一个值变化是“增长”,则为真)

        def peaks(ar):
            i, up = 0, False
            for j in range(1, len(ar)):
                prev, val = ar[j-1], ar[j]
                if up and val < prev:
                    yield prev, i
                    up = False
                if val > prev:
                    i, up = j, True
        
        >>> list(peaks([0,1,2,5,1,0]))
        [(5, 3)]
        >>> list(peaks([0,1,2,5,1,2,0]))
        [(5, 3), (2, 5)]
        >>> list(peaks([0,1,2,5,1,2,0,3]))
        [(5, 3), (2, 5)]
        >>> list(peaks([1,2,2,2,1]))
        [(2, 1)]
        >>> list(peaks([1,2,2,2,3]))
        []
        

        【讨论】:

          【解决方案6】:

          更短的脚本可以是:

          data_array = [1, 2, 5, 4, 6, 9]
          # Delete the first and the last element of the data array. 
          reduced_array = [ data_array[i] for i in range(1, len(data_array)-1) ]
          # Find the maximum value of the modified array 
          peak_value = max(reduced_array)
          # Print out the maximum value and its index in the data array. 
          print 'The peak value is: ' + str(peak_value)
          print 'And its position is: ' + str(data_array.index(peak_value))
          

          输出:

          The peak value is: 6
          And its position is: 4
          

          【讨论】:

            猜你喜欢
            • 2017-12-02
            • 2018-03-27
            • 1970-01-01
            • 2011-02-28
            • 1970-01-01
            • 1970-01-01
            • 2016-07-18
            • 2017-04-22
            • 1970-01-01
            相关资源
            最近更新 更多