3
\$\begingroup\$

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.

asked Mar 4, 2015 at 15:43
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$
  • 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]
answered Mar 4, 2015 at 16:06
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.