Reputation: 349
I am attempting to write a simple set of instructions to return the index of a peak in a set of data. In my actual data set I have multiple peaks (so simply finding the max or min values is not sufficient). For my purposes, I can define a peak as any value that is less than the value before in and also less than the value following in (I am looking for negative peaks).
I have a sort of working example but it fails if there are two identical numbers in the list.
mylist = [10, 8, 5, 3, 1, 3, 4]
min =[]
for i in mylist[1:-1]: # the pre and post comparison means I have to start from item 2 to second last item
pre = mylist.index(i)-1
post = mylist.index(i)+1
print('current i', i)
print('index', pre, post)
print('values pre post', mylist[pre], mylist[post])
if i < mylist[pre] and i< mylist[post]:
print('true')
min.append(mylist.index(i))
print('min=', min)
This seems to work until it gets to the second '3' (at position 5) in the list in which case it evaluates it against the values either side of the first '3'
....
current i 1
index 3 5
values pre post 3 3
true
current i 3
index 2 4
values pre post 5 1
min= [4]
As you can see it correctly finds that the value after '1' is '3' but I think its basically saying the value at index 5 = 3 so what values are either side of 3 and then reading the first 3. In my head this seems like it should be trivial, but am flummoxed. My searches and the suggested questions when writing this didn't flag any duplicates, but I would be amazed if I'm the first person this has happened to...
(also for explanation, I was using scipy find_peaks but it doesn't work for my purposes. If the final data points are rising it identifies the last data point as a peak. e.g. [... 11, 12, 13, 14, 15]
it would identify '15' as a peak, which it may not be).
Upvotes: 2
Views: 7760
Reputation: 2559
If you're willing to use numpy and scipy, you can use scipy.argrelextrema
on a numpy array to get a local minimum (after https://stackoverflow.com/a/13491866/3651127):
import numpy as np
from scipy.signal import argrelextrema
mylist = [10, 8, 5, 3, 1, 3, 4]
x = np.array(mylist)
# for local minima
argrelextrema(x, np.less)
which returns:
(array([4], dtype=int64),)
Upvotes: 0
Reputation: 1436
I faced the same issue when I tried to iterate through mylist[1:-1]
Since you need the indices for preceding element in the first iteration and succeeding element in the last iteration, the code will miss locating some values and push 'IndexError: list index out of range
error as well.
Also, you are writing a lot of code to correlate index, value at each iteration, that is inbuilt in the enumerate(list) function
Below code works okay and gets you what you need:
mylist = [10, 8, 5, 3, 1, 3, 4]
peak = [] # min is a python keyword, so it is advised that you don't define it as a variable, we'll use peak to list all min values
# While iterating through indices and values, use enumerate
for i,x in enumerate(mylist): # iterate through all indices
if i == 0 or i == (len(mylist)-1): # pass the first and last index
pass
else:
pre = mylist[i-1] # use the index variable i to locate pre
post = mylist [i+1] # and post
print ("Selected list >> Pre: {}, Index: {}, Post: {}".format(pre,x,post)) # updated the print line to make it more readable
if pre > x < post: # check if current element is between pre and post
print ("True")
peak.append(x) # append to peak list if true
print (peak)
[Out]:
Selected list >> Pre: 10, Index: 8, Post: 5
Selected list >> Pre: 8, Index: 5, Post: 3
Selected list >> Pre: 5, Index: 3, Post: 1
Selected list >> Pre: 3, Index: 1, Post: 3 # Peak found
True # Print true
Selected list >> Pre: 1, Index: 3, Post: 4
[1] # only 1 peak in the list
Let me know if you're happy with the code.
Upvotes: 4