Given a set of values (points on a graph) find all the max/min points such that
- there is no max is followed by a max (i.e. the max and min points alternate)
- there is some minimum amount of change (
C
) between each max and min (.4 for this data set) - the value of the minimums must be lower than the adjacent maximums
Here is what I have so far
import matplotlib.pyplot as plt
MAX = 1
MIN = -1
NEITHER = 0
def normalize(a):
#this function is for graphing purposes
min_ = min(a)
max_ = max(a)
d = max_ - min_
a = [e - min_ for e in a]
a = [e / d for e in a]
return a
def compress(values, extrema):
#identifies consecutive mins/maxs and takes the more extreme of the two
prev_ext = None
last_index = 0
for i, ext in enumerate(extrema):
if ext == NEITHER:
continue
if prev_ext is None:
prev_ext = ext
last_index = i
continue
cc = values[i]
pc = values[last_index]
if ext == prev_ext == MAX:
if cc >= pc:
extrema[last_index] = NEITHER
else:
extrema[i] = NEITHER
if ext == prev_ext == MIN:
if cc <= pc:
extrema[last_index] = NEITHER
else:
extrema[i] = NEITHER
if ext != NEITHER:
last_index = i
prev_ext = ext
return extrema
def label(values, C=.4):
#C is the amount that must change between two points for those points to
#be considered extremes
extrema = [NEITHER]
prev = values[0]
for curr in values[1:]:
delta = abs(prev - curr)
if curr > prev and delta > C:
extrema.append(MAX)
elif curr < prev and delta > C:
extrema.append(MIN)
else:
extrema.append(NEITHER)
prev = curr
oldextrema = list(extrema)
extrema = compress(values, extrema)
while oldextrema != extrema:
oldextrema = list(extrema)
extrema = compress(values, extrema)
#min_indicies = [i for i, e in enumerate(extrema) if e == MIN]
#max_indicies = [i for i, e in enumerate(extrema) if e == MAX]
return extrema
if __name__ == "__main__":
values = [294.82, 294.85, 294.85, 294.83, 294.67, 294.77, 295.71,
296.31, 296.07, 295.38, 295.83, 296.14, 296.2, 296.35,
296.07, 296.35, 296.83, 296.9, 296.41, 296.49, 296.43,
295.77, 295.75, 296.04, 296.12, 296.09, 296.16, 296.1,
296.32, 296.24, 296.3, 296.5, 296.22, 296.16, 296.13,
296.07, 296.09, 296.04, 296.29, 296.32, 296.31, 296.35,
296.43, 296.6, 296.58, 296.4, 296.38, 296.6, 296.56,
296.63, 296.72, 296.65, 296.69, 296.59, 296.56, 296.63,
296.74, 296.75, 296.91, 296.81, 296.71, 296.78, 296.65,
296.66, 296.71, 296.73, 296.89, 296.98, 297.19, 297.09,
297.01, 296.95, 296.92, 297.0, 296.41, 296.39, 296.05,
295.68, 295.11, 295.19, 295.3, 295.64, 295.53, 295.36,
295.86, 295.75, 295.63, 295.65, 295.7, 295.61, 295.57,
295.52, 295.54, 295.5, 295.44, 295.44, 295.82, 295.85,
295.86, 296.09, 295.75, 295.41, 295.8, 296.2, 295.66,
293.49, 293.4, 293.58, 294.37, 294.16, 295.13, 295.58,
295.34, 295.35, 295.78, 295.87, 296.03, 296.32, 296.87,
296.9, 296.87, 297.3, 296.98, 297.0, 296.87, 297.11,
296.97, 297.1, 296.78, 296.72, 296.95, 297.04, 297.11,
296.94, 296.88, 297.2, 297.04, 296.83, 297.08, 296.96,
297.07, 296.85, 297.04, 296.93, 296.98, 296.92, 296.96,
296.97, 296.89, 296.99, 296.94, 296.83, 296.75, 296.89,
296.93, 296.92, 297.3, 297.12, 297.43]
extrema = label(values)
plt.plot(normalize(values))
plt.plot(extrema)
plt.show()
What I've written seems cumbersome and I'm fairly certain I'm not covering all the edge cases. I'm using numpy
on this project but I couldn't find anything in numpy that would help me solve this (or more likely I found it but didn't comprehend). Is there a more concise way to write this? If there is anything I can do to clarify please don't hesitate to ask.
1 Answer 1
- The docstring is the common way of describing functions in Python.
- Especially in Math code, you should articulate a bit more the description of your functions.
- List comprehension can be joined together.
- Assigning
max
is not necessary as you use it only once. - Some tests make development faster and code easier to read.
- You can return directly, without using an intermediate variable.
- Longer names are preferable: use
delta
instead of d:
Putting it all together:
def normalize(array):
"""
Taking an array of numbers as input,
numbers become from 0 to 1 while retaining their
previous ratio.
Especially useful for graphing purposes.
>>> normalize(range(5))
[0.0, 0.25, 0.5, 0.75, 1.0]
>>> normalize([5,100,45])
[0.0, 1.0, 0.42105263157894735]
"""
min_ = min(array)
delta = max(array) - min_
return [(i - min_) / delta for i in array]