pythonpointscurvesminima

Maxima and Minima Point of a Curve in Python


I have a captured a data from a displacement sensor, the delta values for one iteration look like this. 0, 1, 2, 4, 7, 9, 14, 24, 14, 10, 9, 7, 3 2, 1, 0, 0, 0, 0, -1, -3, -5, -7, -9, -14, -24, -14, -9, -8, -6, -4, -3, -1, 0, 0, 0. (other iterations are also have same pattern.)

I am interested in the maxima and minima points of a curves. I start with a initial position and come back to this position for a loops for line(I've take the partial sum of the values to get the total displacement or line). The partial sum look like this [0, 1, 3, 7, 14, 23, 37, 61, 75, 85, 94, 101, 104, 106, 107, 107, 107, 107, 107, 106, 103, 98, 91, 82, 68, 44, 30, 21, 13, 7, 3, 0, -1, -1, -1, -1]. I am interested in 107 and -1 (the next curve minima)

But I am not figure out the code for say n no. of curve (iteration). Can you help me with this?


Solution

  • You can use this function for getting the absolute extrema:

    def extrema(value, deltas):
        max_value = min_value = value
        for delta in deltas:
            value += delta
            if value < min_value:
                min_value = value
            elif value > max_value:
                max_value = value
        return min_value, max_value
    

    Here I have adapted the function to yield local extrema:

    def extrema(value, deltas):
        values = [value]
        for delta in deltas:
            value += delta
            values.append(value)
        average = sum(values)/len(values)
        threshold = (max(values) - min(values))/6
        min_threshold = average - threshold
        max_threshold = average + threshold
        min_value = max_value = None
        for value in values:
            if value < min_threshold:
                if min_value is None or value < min_value:
                    min_value = value
            elif value > max_threshold:
                if max_value is None or value > max_value:
                    max_value = value
            elif min_value is not None and max_value is not None:
                yield min_value, max_value
                max_value = min_value = None
    

    You can fine-tune the function from here. For instance, the function could skip the first values until min_threshold < value < max_threshold to find the start of a cycle, and at the end it could yield the last extremum if it did not end with a full cycle.

    Lastly, here is a function that works with point tuples as in your example data.

    class Point(object):
    
        __slots__ = ('x', 'y')
    
        def __init__(self, x=0, y=0):
            self.x = x
            self.y = y
    
        def __repr__(self):
            return str((self.x, self.y))
    
        def __iadd__(self, other):
            self.x += other.x
            self.y += other.y
            return self
    
        def __isub__(self, other):
            self.x -= other.x
            self.y -= other.y
            return self
    
        def __idiv__(self, number):
            self.x /= number
            self.y /= number
            return self
    
        def abs(self):
            return abs(self.x) + abs(self.y)
    
        def copy(self):
            return Point(self.x, self.y)
    
    
    def extrema(moves, jitter=0.1, threshold=1000, sample=16):
        point = Point()
        minpoint = Point()
        maxpoint = Point()
        average = Point()
        average /= 1.0
        turned = False
        for move in moves:
            point += move
            x = point.x
            if x < minpoint.x:
                minpoint.x = x
            elif x > maxpoint.x:
                maxpoint.x = x
            y = point.y
            if y < minpoint.y:
                minpoint.y = y
            elif y > maxpoint.y:
                maxpoint.y = y
            delta = move.copy()
            delta -= average
            delta /= sample
            average += delta
            if average.abs() < jitter:
                if point.abs() > threshold:
                    turned = True
                elif turned:
                    yield minpoint, maxpoint
                    point = Point() # reset (calibrate)
                    minpoint = Point()
                    maxpoint = Point()
                    turned = False
    
    
    # read data from file
    moves = [Point(*map(int, move.split(',')))
        for move in open('data.txt').read().split(';') if move]
    
    # generate extrema
    print list(extrema(moves))