1

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?

Liam
  • 1,471
  • 1
  • 15
  • 25
Nitin Kumar
  • 133
  • 1
  • 6
  • 1
    So, you have the code that works for *one* curve, and you need code that works for *some number* of curves (e.g. 42 curves)? – phimuemue Nov 27 '11 at 13:23
  • I don't have code that work for one curve but yes I can get the partial sum like this (over the entire iterations). I have 10 and 25 iterations of displacements (along a line, between 2 pts). I need a list of those maxima and minima pts, but could not figure out how. – Nitin Kumar Nov 27 '11 at 13:45
  • Your `iterations` would be a list of lists, and you would just run `extrema(initial, deltas) for deltas in iterations`. – Cito Nov 27 '11 at 13:53

1 Answers1

2

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))
Cito
  • 5,365
  • 28
  • 30
  • Thanks Cito,that is working perfectly for one curve.I had several curves,like the deltas for 2 curves = [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, 0, 1, 2, 4, 7, 9, 16, 25, 13, 12, 11, 8, 5, 2, 1, 0, 0, 0, -1, -1, -3, -5, -7, -9, -14, -24, -14, -9, -8, -6, -4, -3, -1, 0, 0, 0].Each of them is start with first point,have positive delta values till second point and then return to first point having negative delta value,just like drawing a line back and forth(say n times).Each should have maxima and minina pts. – Nitin Kumar Nov 27 '11 at 14:21
  • Ah, ok, so you don't want the absolute extrema of a list of small curves, but the local extrema of one long curve, right? – Cito Nov 27 '11 at 14:24
  • Yes, I want the local minima and maxima. I what to see how much displacement I have for each curve. – Nitin Kumar Nov 27 '11 at 14:44
  • So the idea is then to first analyze the curve, in order to get its average value as well as min and max deviation. If you have these values, it is easy to determine when another "cycle" has started, and reset the local min and max values in order to calculate new ones for the next cycle. – Cito Nov 27 '11 at 14:59
  • Thanks Cito, I tried the code with 10 iterations and indeed I could all the points in one of the curve. With the other, it was missing some points (initial point I suppose). I am working on it. Also, could you point me in right direction for converting generator values into the list as I would be using those extrema point to plot, along with the curves. Thank you very much again. – Nitin Kumar Nov 28 '11 at 21:45
  • Right, as I said you may want to skip values at the beginning until you come to a value that is between min_threshhold and max_threshhold. And at the end you may want to yield a last min_value, max_value even if the last cycle was not completed. Converting into list is easy, just call list(generator). – Cito Nov 28 '11 at 23:19
  • I tried the codes by changing 6 to 14 in "threshold = (max(values) - min(values))/6". Now, I am getting 10 values for each, the local minima and maxima. However, the values seems incorrect. You can see the plot here: http://www.flickr.com/photos/7353234@N08/6449689777/in/photostream/ . If cycle/loop/line could be separated somehow, I could just take the minimum and maximum value for the loop. It would be the length of each lines (to which I am interested). – Nitin Kumar Dec 04 '11 at 01:06
  • If you make the threshold too low, small jitter will be detected. If you post an example with complete input data and your expected output data somewhere, I could try adapting the algorithm a bit more. – Cito Dec 04 '11 at 08:53
  • I could write an alternative code to produce the data that you asked for. I have uploaded the data here: http://www.megaupload.com/?d=XQFZ6LQ2 . You will find the note.txt in data_1.zip besides a excel file and a plot file with the expected local extrema. Thanks! – Nitin Kumar Dec 06 '11 at 16:45
  • Ok, I have edited the answer, adding a solution that works with your example data. The input file must have the offset pairs, separated by semicolons. I have tested it with your data file 400_558_25iter.txt and got the same results plus/minus one because where you start the new cycle is a bit arbitrary. Note that the position is calibrated to zero when a new cycle starts, I think that is what you want. – Cito Dec 06 '11 at 22:44
  • Just to explain what's going on in that code: Basically, it just calculates the moing average of the offsets. If it's very low, and the maximum has already been reached, it yields the extreme values. Then it resets all values and starts a new cycle. – Cito Dec 06 '11 at 22:51
  • Thank you very much! Your codes are very clever! :) I've tested and found them as you said. Taking the opportunity, I'd like to clear few points: if point.abs() > threshold: (What is happening in this line? Also, what is threshold here and why it is needed? And, when you say the offset, you are referring to delta values, right?. And, I tried to convert the moves and extrema values to list so I can use them for plotting but it shows an error that 'Point' object does not support indexing. I need separate values for the delta x, y and min & max. x, y but list comprehension is not working. – Nitin Kumar Dec 08 '11 at 00:49
  • Each cycle has a min and a max turning point. In order to distinguish them, we can check whether `point.abs() > threshold`. The `abs()` method calculates how far away the point is from the origin. You can get the separate values with `[(pmin.x, pmin.y, pmax.x, pmax.y) for pmin, pmax in extrema(moves)]`. – Cito Dec 08 '11 at 17:11
  • I've plotted the extrema pts and found them to be quite different than expected. I've understood, why it's so. As the data has quite lot of jitter and doesn't look like line. So, for estimating a line length, we need to consider only one list (x or y) otherwise any bump in a line will be detected as min/max in x or y. Please find the plot here: http://www.flickr.com/photos/7353234@N08/6494806557/in/photostream . So,as soon as we'll get local max. for x(around 6000), then it may give y value at that index and vice versa for x. I'd like to see the variance in drawn line lengths (sensor perf) – Nitin Kumar Dec 11 '11 at 21:16
  • You may get better results if you calculate proper line lengths using the formula sqrt(x*x+y*y) and if you scale x and y differently, since I noticed they have different orders of magnitude in your input data. I leave you alone at this point - hope you could get some inspiration from my example code and have some fun with Python. It's an ideal language for experimenting with data. – Cito Dec 11 '11 at 23:50