Search code examples
pythonlistdictionarycomparison-operators

Detect peaks in list of numbers and record their positions


I am trying to create some code that returns the positions and the values of the "peaks" (or local maxima) of a numeric array.

For example, the list arr = [0, 1, 2, 5, 1, 0] has a peak at position 3 with a value of 5 (since arr[3] equals 5).

The first and last elements of the array will not be considered as peaks (in the context of a mathematical function, you don't know what is after and before and therefore, you don't know if it is a peak or not).

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

My issue is with plateaus. [1, 2, 2, 2, 1] has a peak while [1, 2, 2, 2, 3] does not. When a plateau is a peak, the first position of the plateau is recorded.

Any help is appreciated.


Solution

  • I suggest you use groupby to group contiguous equal values, then for each group store the first position, example for [1, 2, 2, 2, 1] it creates the following list following list of tuples [(1, 0), (2, 1), (1, 4)], putting all together:

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

    Output

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