Source code for clusteror.utils

'''
This module works as a transient store of useful functions. New standalone
functions will be first placed here. As they grow in number and can be
consolidated into an independent class, module, or even a new package.
'''


[docs]def find_local_extremes(series, contrast): ''' Finds local minima and maxima according to ``contrast``. In theory, they can be determined by first derivative and second derivative. The result derived this way is of no value in dealing with a very noisy, zig-zag data as too many local extremes would be found for any turn-around. The method presented here compares the point currently looked at and the opposite potential extreme that is updated as scanning through the data sequence. For instance, a potential maximum is 10, then a data point of value smaller than 10 / (1 + contrast) is written down as a local minimum. Parameters ---------- series: Pandas Series One dimenional data to find local extremes in. contrast: float A value between 0 and 1 as a threshold between minimum and maximum. Returns ------- local_min_inds: list List of indices for local minima. local_mins: list List of minimum values. local_max_inds: list List of indices for local maxima. local_maxs: list List of maximum values. ''' state = { 'pmin': None, 'pmin_ind': None, 'pmax': None, 'pmax_ind': None, 'lmin': None, 'lmax': None } # initialise, all starting points are potential local min and local max state['pmin_ind'] = series.index.tolist()[0] state['pmin'] = series.iat[0] state['pmax_ind'] = series.index.tolist()[0] state['pmax'] = series.iat[0] # store true local mins and maxes local_min_inds = [] local_mins = [] local_max_inds = [] local_maxs = [] # walk through all rows for ind, value in series.iteritems(): if state['pmin'] is not None and state['pmax'] is not None: # when just starts out or find a potential extreme after # confirming a local extreme if value <= state['pmin']: # value is smaller then update potential min state['pmin'] = value state['pmin_ind'] = ind if value * (1 + contrast) <= state['pmax']: # if the gap between current point and potential max is # larger than contrast # then confirm the last potential max is a true local max state['lmax'] = state['pmax'] local_max_inds.append(state['pmax_ind']) local_maxs.append(state['pmax']) # so for a moment no potential max state['pmax'] = None state['pmax_ind'] = None elif value >= state['pmax']: # value is larger then update potential max state['pmax'] = value state['pmax_ind'] = ind if value > state['pmin'] * (1 + contrast): # if the gap between current point and potential min is # larger than contrast # then confirm the last potential min is a true local min state['lmin'] = state['pmin'] local_min_inds.append(state['pmin_ind']) local_mins.append(state['pmin']) # so for a moment no potential min state['pmin'] = None state['pmin_ind'] = None else: # point is between potenital min and potential max # just pass without updating pass elif state['pmax'] is not None and state['lmin'] is not None: # when just found a local min, trying to find next local max if value >= state['pmax']: # update if value is larger state['pmax'] = value state['pmax_ind'] = ind elif value <= state['lmin']: # this is where it just after a sharp blip # confirm the last point is a local max state['lmax'] = state['pmax'] local_max_inds.append(state['pmax_ind']) local_maxs.append(state['pmax']) # so for a moment no potential max state['pmax'] = None state['pmax_ind'] = None # the current point becomes a potential min state['pmin'] = value state['pmin_ind'] = ind else: # smaller than the last point, so this is a potential min state['lmin'] = None state['pmin'] = value state['pmin_ind'] = ind elif state['pmin'] is not None and state['lmax'] is not None: # when just found a local max, trying to find the next local min if value <= state['pmin']: # update if value is smaller state['pmin'] = value state['pmin_ind'] = ind elif value >= state['lmax']: # this is where just after a deep dip # confirm the last point is a local min state['lmin'] = state['pmin'] local_min_inds.append(state['pmin_ind']) local_mins.append(state['pmin']) # so for a moment no potential min state['pmin'] = None state['pmin_ind'] = None # the current point becomes a potential max state['pmax'] = value state['pmax_ind'] = ind else: # larger than the last point, so this is a potential max state['lmax'] = None state['pmax'] = value state['pmax_ind'] = ind else: print('strange') return local_min_inds, local_mins, local_max_inds, local_maxs