If the data is suitable, you can try to split the signal to half periods roughly by alternatly searching for the first point when the signal reaches an upper threshold and a lower threshold. After this, you just find the minimum or maximum in each half period.

Let me show an example because my description is not clear. Assume the signal is like this:

our @s = map { sin(0.18*$_) + rand(0.6) - 0.3 } 0 .. 99;
Then you split the time to approximate half periods like this:
our(@xlo, @xhi); my $t = my $t0 = $s[0] < 0; for my $x (0 .. @s - 1) { + if ($t) { if ($s[$x] > 0.5) { $t = 0; push @xhi, $x; } } else { if ( +$s[$x] < -0.5) { $t = 1; push @xlo, $x; } } }

Now values in @xlo give the start of each lower half period, and similarly @xhi contain the starting time of each higher half period. So now you search for a minimum in each lower half period and the maximum in each upper half.

our(@xmi, @xma); for my $p (0 .. @xlo - 1) { my $xmi = $xlo[$p]; for m +y $x ($xlo[$p] .. ($xhi[$p + $t0] || @s) - 1) { $s[$x] < $s[$xmi] and + $xmi = $x; } push @xmi, $xmi; } for my $p (0 .. @xhi - 1) { my $xma += $xhi[$p]; for my $x ($xhi[$p] .. ($xlo[$p + 1 - $t0] || @s) - 1) { +$s[$xma] < $s[$x] and $xma = $x; } push @xma, $xma; }

(The code is complicated only because of the boundary conditions, furthermore it is left as an exercise to the reader to fix any off by one errors.)

Let's plot the results

for my $x (0 .. @s - 1) { my $l = " "x15 . ":" . " "x15; if (@xlo && $ +xlo[0] <= $x) { shift @xlo; substr $l, 0, 10, "v"x10; } elsif (@xhi & +& $xhi[0] <= $x) { shift @xhi; substr $l, 20, 10, "v"x10; } if (@xmi +and $xmi[0] <= $x) { shift @xmi; substr $l, 0, 7, "minimum"; } elsif +(@xma and $xma[0] <= $x) { shift @xma; substr $l, 23, 7, "maximum"; } + substr $l, 15+int(10*$s[$x]), 1, "*"; print $l, "\n"; }

We get, for example, this.

* *: : * : v*vvvvvvvv : * : * : * : * : * : * : * : max*mum : * : * : * : * : * * *: * vvvvvvvv*v : * : * : * : * : min*mum : * : * : * : * : * : * : * : * : * : :* : * : * : vv*vvvvvvv : * : * : * : * : * : max*mum : * : * : * : * : * : * : * *: * * : * : * : vvvvvvvv*v : * : * : min*mum : * : * : * : * : * : * : * : * : *: : * :* : v*vvvvvvvv : * : * : * : * : * : * : * : maxi*um : * : * : * : * : * : * : * *: * : vvvvvvvv*v : * : * : * : * : * : * : minim*m : * : * :

If the data is more noisy, you may need to smooth it before comparing with the thresholds. If the amplitude varies then it's not so easy to set a fix threshold this way, so I'm not sure what to do in that case.


In reply to Re: Finding local maxima/minima in noisy, pediodic data by ambrus
in thread Finding local maxima/minima in noisy, pediodic data by kikuchiyo

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.