Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find local minima and maxima simultanously with scipy.signal.argrelextrema

I would like to find the local minima and maxima simultanously in an array. I attempted to use Scipy's argrelextrema, but I don't find any documentation on the required comparator function. I attempted to write my own with no success. How could I do this?

I'd like to do this, but only calling argrelextrema once instead of iterating over the data twice with argrelmin and argrelmax, as my data is pretty long, and I have to do this multiple times with it:

   import numpy as np
   import scipy.signal as sg

   data = [[xes], [ys]]   #a long 2d array
   max_places = np.array(sg.argrelmax(data[1]))[0] 
   min_places = np.array(sg.argrelmin(data[1]))[0]
   extrema_places = np.contantenate((max_places, min_places))
like image 200
Neinstein Avatar asked Oct 18 '22 03:10

Neinstein


1 Answers

Your goal doesn't make sense.

Using the example in the docs:

In [199]: from scipy.signal import argrelextrema
In [200]:  x = np.array([2, 1, 2, 3, 2, 0, 1, 0])
In [201]: argrelextrema(x, np.greater)
Out[201]: (array([3, 6], dtype=int32),)

this gets the local maxima. Change to less to get the local minima.

In [202]: argrelextrema(x, np.less)
Out[202]: (array([1, 5], dtype=int32),)

A lambda function can work instead of np.greater:

In [204]: argrelextrema(x, lambda a,b: a>b)
Out[204]: (array([3, 6], dtype=int32),)

We could combine > and < with a or, but that just fetches all points (except the ends).

In [205]: argrelextrema(x, lambda a,b: (a>b) | (a<b))
Out[205]: (array([1, 2, 3, 4, 5, 6], dtype=int32),)

Change a value of x so two adjacent values are the same (and thus the lambda test is false)

In [211]: x
Out[211]: array([2, 1, 2, 3, 2, 2, 1, 0])
In [212]: argrelextrema(x, lambda a,b: (a>b) | (a<b))
Out[212]: (array([1, 2, 3, 6], dtype=int32),)

But lets say you could give it desired function, what kind of result would you want? The maxima and minima strung together as one array, or as two, or as a 2d array?

np.array([1,3,5,6])
(np.array([3,6]), np.array([1,5]))

Just realized that the result is already a tuple, like where, one array per dimension. In fact that's what it returns:

results = _boolrelextrema(data, comparator,
                          axis, order, mode)
return np.where(results)

The meat of the task is in that _boolrelextrama function; which presumably returns an array like data, but with True/False based on the comparator.

Are you looking for a turning point, in either direction? You might want to play with order.

In [217]: argrelextrema(x, lambda a,b: (a>b) | (a<b), order=2)
Out[217]: (array([1, 3, 6], dtype=int32),)

With that modified x this order 2 version does return both extrema. But I doubt if it works if the runs between turns are longer.

See full code in sg._peak_finding.py (see also the [source] link in the documentation). _boolrelextrema uses the comparator to test data against several shifted versions of data. mode is used by np.take.


So in detail

argrelmax is doing:

In [262]: x = np.array([2, 1, 2, 3, 2, 0, 1, 0])   # data
In [263]: locs = np.arange(0,len(x))          # all indices
In [264]: plus=np.take(x,locs+1,mode='clip')   # shift up, with 'clip'
In [265]: minus=np.take(x,locs-1,mode='clip')  # shift down
In [266]: plus
Out[266]: array([1, 2, 3, 2, 0, 1, 0, 0])
In [267]: minus
Out[267]: array([2, 2, 1, 2, 3, 2, 0, 1])
In [268]: (x>plus) & (x>minus)             # compare x with shifted arrays
Out[268]: array([False, False, False,  True, False, False,  True, False], dtype=bool)
In [269]: np.where(_)
Out[269]: (array([3, 6], dtype=int32),)

argrelmin generates the same plus and minus, but then does:

In [270]: np.where((x<plus) & (x<minus))
Out[270]: (array([1, 5], dtype=int32),)

Doing the 'or' step before where interweaves the indices:

In [271]: np.where(((x<plus) & (x<minus)) | (x>plus) & (x>minus))
Out[271]: (array([1, 3, 5, 6], dtype=int32),)

A combined min_max could save on the 2 take operations, but otherwise it has do the same 2 comparator steps.

Note that all these operations are in compiled code; there's no Python level iteration on the data array.

like image 152
hpaulj Avatar answered Oct 21 '22 09:10

hpaulj