在数字列表中检测峰并记录其位置

时间:2018-12-24 14:09:32

标签: python list dictionary comparison-operators

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

例如,列表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]没有达到峰值。当高原是峰值时,将记录高原的第一位置。

感谢您的帮助。

5 个答案:

答案 0 :(得分: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)]
[]

答案 1 :(得分:1)

我知道我参加聚会可能会有点晚,但是我想使用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)

        ...

答案 2 :(得分:0)

如果可以对数据进行预处理以除去重复数字并仅保留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': []}

答案 3 :(得分:0)

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

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]))
[]

答案 4 :(得分:0)

一个简短的脚本可能是:

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