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.

\$\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]
\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.