Пример #1
0
def detrend(var, ax=None, lcopy=True, ldetrend=True, ltrend=False, degree=1, rcond=None, w=None,  
            lsmooth=False, lresidual=False, window_len=11, window='hanning'): 
  ''' subtract a linear trend from a time-series array (operation is in-place) '''
  # check input
  if not isinstance(var,np.ndarray): raise NotImplementedError # too many checks
  if lcopy: var = var.copy() # make copy - not in-place!
  # fit over entire array (usually not what we want...)
  if ax is None and ldetrend: ax = np.arange(var.size) # make dummy axis, if necessary
  if var.ndim != 1:
    shape = var.shape 
    var = var.ravel() # flatten array, if necessary
  else: shape = None
  # apply optional detrending
  if ldetrend or ltrend:
    # fit linear trend
    trend = np.polyfit(ax, var, deg=degree, rcond=rcond, w=w, full=False, cov=False)
    # evaluate and subtract linear trend
    if ldetrend and ltrend: raise ArgumentError("Can either return trend/polyfit or residuals, not both.")
    elif ldetrend and not ltrend: var -= np.polyval(trend, ax) # residuals
    elif ltrend and not ldetrend: var = np.polyval(trend, ax) # residuals
  # apply optional smoothing
  if lsmooth and lresidual: raise ArgumentError("Can either return smoothed array or residuals, not both.")
  elif lsmooth: var = smooth(var, window_len=window_len, window=window)  
  elif lresidual: var -= smooth(var, window_len=window_len, window=window)
  # return detrended and/or smoothed time-series
  if shape is not None: var = var.reshape(shape)
  return var
Пример #2
0
def detrend(var,
            ax=None,
            lcopy=True,
            ldetrend=True,
            ltrend=False,
            degree=1,
            rcond=None,
            w=None,
            lsmooth=False,
            lresidual=False,
            window_len=11,
            window='hanning'):
    ''' subtract a linear trend from a time-series array (operation is in-place) '''
    # check input
    if not isinstance(var, np.ndarray):
        raise NotImplementedError  # too many checks
    if lcopy: var = var.copy()  # make copy - not in-place!
    # fit over entire array (usually not what we want...)
    if ax is None and ldetrend:
        ax = np.arange(var.size)  # make dummy axis, if necessary
    if var.ndim != 1:
        shape = var.shape
        var = var.ravel()  # flatten array, if necessary
    else:
        shape = None
    # apply optional detrending
    if ldetrend or ltrend:
        # fit linear trend
        trend = np.polyfit(ax,
                           var,
                           deg=degree,
                           rcond=rcond,
                           w=w,
                           full=False,
                           cov=False)
        # evaluate and subtract linear trend
        if ldetrend and ltrend:
            raise ArgumentError(
                "Can either return trend/polyfit or residuals, not both.")
        elif ldetrend and not ltrend:
            var -= np.polyval(trend, ax)  # residuals
        elif ltrend and not ldetrend:
            var = np.polyval(trend, ax)  # residuals
    # apply optional smoothing
    if lsmooth and lresidual:
        raise ArgumentError(
            "Can either return smoothed array or residuals, not both.")
    elif lsmooth:
        var = smooth(var, window_len=window_len, window=window)
    elif lresidual:
        var -= smooth(var, window_len=window_len, window=window)
    # return detrended and/or smoothed time-series
    if shape is not None: var = var.reshape(shape)
    return var
Пример #3
0
def getPlotValues(var, checkunits=None, checkname=None, lsmooth=False, lperi=False, laxis=False):
  ''' Helper function to check variable/axis, get (scaled) values for plot, and return appropriate units. '''
  # figure out units
  if var.plot is not None: 
    varname = var.plot.name 
    if checkname is not None and varname != checkname: # only check plotname! 
      raise VariableError, "Expected variable name '{}', found '{}'.".format(checkname,varname)
  else: varname = var.atts['name']
  val = var.getArray(unmask=True, copy=True) # the data to plot
  if var.plot is not None:
    if var.units != var.plot.units: 
      val *=  var.plot.scalefactor
      val += var.plot.offset
    varunits = var.plot.units
  else: 
    varunits = var.atts['units']    
  if checkunits is not None and  varunits != checkunits: 
    raise VariableError, "Units for variable '{}': expected {}, found {}.".format(var.name,checkunits,varunits) 
  # some post-processing
  val = val.squeeze()
  if lsmooth: val = smooth(val)
  if lperi: 
    if laxis: 
      delta = np.diff(val)
      val = np.concatenate((val[:1]-delta[:1],val,val[-1:]+delta[-1:]))
    else: val = np.concatenate((val[-1:],val,val[:1]))
  # return values, units, name
  return val, varunits, varname     
Пример #4
0
def getPlotValues(var, checkunits=None, checkname=None, lsmooth=False, lperi=False, laxis=False):
  ''' Helper function to check variable/axis, get (scaled) values for plot, and return appropriate units. '''
  # figure out units
  if var.plot is not None: 
    varname = var.plot.name 
    if checkname is not None and varname != checkname: # only check plotname! 
      raise VariableError, "Expected variable name '{}', found '{}'.".format(checkname,varname)
  else: varname = var.atts['name']
  if np.issubdtype(var.dtype, np.datetime64): val = var.data_array.copy() # need to preserve dates
  else: val = var.getArray(unmask=True, fillValue=np.NaN, dtype=np.float, copy=True) # the data to plot
  # N.B.: matplotlib does not understand masked arrays, therefor we have to convert masked values to NaN's
  #       (and convert the data to float in the process...)
  if var.plot is not None:
    if var.units != var.plot.units: 
      val *=  var.plot.scalefactor
      val += var.plot.offset
    varunits = var.plot.units
  else: 
    varunits = var.atts['units']    
  if checkunits is not None and  varunits != checkunits: 
    raise VariableError, "Units for variable '{}': expected {}, found {}.".format(var.name,checkunits,varunits) 
  # some post-processing
  if val.size > 1: val = val.squeeze()
  if lsmooth: val = smooth(val)
  if lperi: 
    if laxis: 
      delta = np.diff(val)
      val = np.concatenate((val[:1]-delta[:1],val,val[-1:]+delta[-1:]))
    else: val = np.concatenate((val[-1:],val,val[:1]))
  # return values, units, name
  return val, varunits, varname     
Пример #5
0
def getPlotValues(var,
                  checkunits=None,
                  checkname=None,
                  lsmooth=False,
                  lperi=False,
                  pseudo_axis=None,
                  laxis=False):
    ''' Helper function to check variable/axis, get (scaled) values for plot, and return appropriate units. '''
    # figure out units
    if var.plot is not None:
        varname = var.plot.name
        if checkname is not None and varname != checkname:  # only check plotname!
            raise VariableError(
                "Expected variable name '{}', found '{}'.".format(
                    checkname, varname))
    else:
        varname = var.atts['name']
    if np.issubdtype(var.dtype, np.datetime64):
        val = var.data_array.copy()  # need to preserve dates
    else:
        val = var.getArray(unmask=True,
                           fillValue=np.NaN,
                           dtype=np.float,
                           copy=True)  # the data to plot
    # N.B.: matplotlib does not understand masked arrays, therefor we have to convert masked values to NaN's
    #       (and convert the data to float in the process...)
    if var.plot is not None:
        if var.units != var.plot.units:
            val *= var.plot.scalefactor
            val += var.plot.offset
        varunits = var.plot.units
    else:
        varunits = var.atts['units']
    if checkunits is not None and varunits != checkunits:
        raise VariableError(
            "Units for variable '{}': expected {}, found {}.".format(
                var.name, checkunits, varunits))
    # some post-processing
    if val.size > 1: val = val.squeeze()
    if lsmooth: val = smooth(val)
    if lperi:
        if laxis:
            delta = np.diff(val)
            val = np.concatenate(
                (val[:1] - delta[:1], val, val[-1:] + delta[-1:]))
        else:
            val = np.concatenate((val[-1:], val, val[:1]))
    # return values, units, name
    return val, varunits, varname
Пример #6
0
def getPlotValues(var,
                  checkunits=None,
                  checkname=None,
                  lsmooth=False,
                  lperi=False,
                  laxis=False):
    ''' Helper function to check variable/axis, get (scaled) values for plot, and return appropriate units. '''
    # figure out units
    if var.plot is not None:
        varname = var.plot.name
        if checkname is not None and varname != checkname:  # only check plotname!
            raise VariableError, "Expected variable name '{}', found '{}'.".format(
                checkname, varname)
    else:
        varname = var.atts['name']
    val = var.getArray(unmask=True, copy=True)  # the data to plot
    if var.plot is not None:
        if var.units != var.plot.units:
            val *= var.plot.scalefactor
            val += var.plot.offset
        varunits = var.plot.units
    else:
        varunits = var.atts['units']
    if checkunits is not None and varunits != checkunits:
        raise VariableError, "Units for variable '{}': expected {}, found {}.".format(
            var.name, checkunits, varunits)
    # some post-processing
    val = val.squeeze()
    if lsmooth: val = smooth(val)
    if lperi:
        if laxis:
            delta = np.diff(val)
            val = np.concatenate(
                (val[:1] - delta[:1], val, val[-1:] + delta[-1:]))
        else:
            val = np.concatenate((val[-1:], val, val[:1]))
    # return values, units, name
    return val, varunits, varname
Пример #7
0
def linePlot(varlist,
             ax=None,
             fig=None,
             linestyles=None,
             varatts=None,
             legend=None,
             xline=None,
             yline=None,
             title=None,
             flipxy=None,
             xlabel=None,
             ylabel=None,
             xlim=None,
             ylim=None,
             lsmooth=False,
             lprint=False,
             **kwargs):
    ''' A function to draw a list of 1D variables into an axes, and annotate the plot based on variable properties. '''
    warn('Deprecated function: use Figure or Axes class methods.')
    # create axes, if necessary
    if ax is None:
        if fig is None: fig, ax = getFigAx(1)  # single panel
        else: ax = fig.axes[0]
    # varlist is the list of variable objects that are to be plotted
    #print varlist
    if isinstance(varlist, Variable): varlist = [varlist]
    elif not isinstance(varlist, (tuple, list)) or not all(
        [isinstance(var, Variable) for var in varlist]):
        raise TypeError
    for var in varlist:
        var.squeeze()  # remove singleton dimensions
    # linestyles is just a list of line styles for each plot
    if isinstance(linestyles, (basestring, NoneType)):
        linestyles = [linestyles] * len(varlist)
    elif not isinstance(linestyles, (tuple, list)):
        if not all([isinstance(linestyles, basestring) for var in varlist]):
            raise TypeError
        if len(varlist) != len(linestyles):
            raise ListError, "Failed to match linestyles to varlist!"
    # varatts are variable-specific attributes that are parsed for special keywords and then passed on to the
    if varatts is None: varatts = [dict()] * len(varlist)
    elif isinstance(varatts, dict):
        tmp = [
            varatts[var.name] if var.name in varatts else dict()
            for var in varlist
        ]
        if any(tmp): varatts = tmp  # if any variable names were found
        else:
            varatts = [varatts] * len(
                varlist
            )  # assume it is one varatts dict, which will be used for all variables
    elif not isinstance(varatts, (tuple, list)):
        raise TypeError
    if not all([isinstance(atts, dict) for atts in varatts]): raise TypeError
    # check axis: they need to have only one axes, which has to be the same for all!
    if len(varatts) != len(varlist):
        raise ListError, "Failed to match varatts to varlist!"
    for var in varlist:
        if var.ndim > 1:
            raise AxisError, "Variable '{}' has more than one dimension; consider squeezing.".format(
                var.name)
        elif var.ndim == 0:
            raise AxisError, "Variable '{}' is a scalar; consider display as a line.".format(
                var.name)
    # loop over variables
    plts = []
    varname = None
    varunits = None
    axname = None
    axunits = None  # list of plot handles
    for var, linestyle, varatt in zip(varlist, linestyles, varatts):
        varax = var.axes[0]
        # scale axis and variable values
        axe, axunits, axname = getPlotValues(varax,
                                             checkunits=axunits,
                                             checkname=None)
        val, varunits, varname = getPlotValues(var,
                                               checkunits=varunits,
                                               checkname=None)
        # variable and axis scaling is not always independent...
        if var.plot is not None and varax.plot is not None:
            if 'preserve' in var.plot and 'scalefactor' in varax.plot:
                if varax.units != axunits and var.plot.preserve == 'area':
                    val /= varax.plot.scalefactor
        # figure out keyword options
        kwatts = kwargs.copy()
        kwatts.update(varatt)  # join individual and common attributes
        if 'label' not in kwatts:
            kwatts['label'] = var.name  # default label: variable name
        # N.B.: other scaling behavior could be added here
        if lprint: print varname, varunits, val.mean()
        if lsmooth: val = smooth(val)
        # figure out orientation
        if flipxy: xx, yy = val, axe
        else: xx, yy = axe, val
        # call plot function
        if linestyle is None: plts.append(ax.plot(xx, yy, **kwatts)[0])
        else: plts.append(ax.plot(xx, yy, linestyle, **kwatts)[0])
    # set axes limits
    if isinstance(xlim, (list, tuple)) and len(xlim) == 2: ax.set_xlim(*xlim)
    elif xlim is not None: raise TypeError
    if isinstance(ylim, (list, tuple)) and len(ylim) == 2: ax.set_ylim(*ylim)
    elif ylim is not None: raise TypeError
    # set title
    if title is not None:
        ax.set_title(title, dict(fontsize='medium'))
        pos = ax.get_position()
        pos = pos.from_bounds(x0=pos.x0,
                              y0=pos.y0,
                              width=pos.width,
                              height=pos.height - 0.03)
        ax.set_position(pos)
    # set axes labels
    if flipxy:
        xname, xunits, yname, yunits = varname, varunits, axname, axunits
    else:
        xname, xunits, yname, yunits = axname, axunits, varname, varunits
    if not xlabel:
        xlabel = '{0:s} [{1:s}]'.format(
            xname, xunits) if xunits else '{0:s}'.format(xname)
    else:
        xlabel = xlabel.format(xname, xunits)
    if not ylabel:
        ylabel = '{0:s} [{1:s}]'.format(
            yname, yunits) if yunits else '{0:s}'.format(yname)
    else:
        ylabel = ylabel.format(yname, yunits)
    # a typical custom label that makes use of the units would look like this: 'custom label [{1:s}]',
    # where {} will be replaced by the appropriate default units (which have to be the same anyway)
    xpad = 2
    xticks = ax.get_xaxis().get_ticklabels()
    ypad = -2
    yticks = ax.get_yaxis().get_ticklabels()
    # len(xticks) > 0 is necessary to avoid errors with AxesGrid, which removes invisible tick labels
    if len(xticks) > 0 and xticks[-1].get_visible():
        ax.set_xlabel(xlabel, labelpad=xpad)
    elif len(yticks) > 0 and not title:
        yticks[0].set_visible(False)  # avoid overlap
    if len(yticks) > 0 and yticks[-1].get_visible():
        ax.set_ylabel(ylabel, labelpad=ypad)
    elif len(xticks) > 0:
        xticks[0].set_visible(False)  # avoid overlap
    # make monthly ticks
    if axname == 'time' and axunits == 'month':
        ax.xaxis.set_minor_locator(
            mpl.ticker.AutoMinorLocator(2))  # ax.minorticks_on()
    # add legend
    if legend:
        legatts = dict()
        if ax.get_yaxis().get_label():
            legatts['fontsize'] = ax.get_yaxis().get_label().get_fontsize()
        if isinstance(legend, dict): legatts.update(legend)
        elif isinstance(legend, (int, np.integer, float, np.inexact)):
            legatts['loc'] = legend
        ax.legend(**legatts)
    # add orientation lines
    if isinstance(xline, (int, np.integer, float, np.inexact)):
        ax.axhline(y=xline, color='black')
    elif isinstance(xline, dict):
        ax.axhline(**xline)
    if isinstance(yline, (int, np.integer, float, np.inexact)):
        ax.axvline(x=yline, color='black')
    elif isinstance(xline, dict):
        ax.axvline(**yline)
    # return handle
    return plts
Пример #8
0
def linePlot(varlist, ax=None, fig=None, linestyles=None, varatts=None, legend=None,
	   				 xline=None, yline=None, title=None, flipxy=None, xlabel=None, ylabel=None, xlim=None,
	  	   		 ylim=None, lsmooth=False, lprint=False, **kwargs):
  ''' A function to draw a list of 1D variables into an axes, and annotate the plot based on variable properties. '''
  warn('Deprecated function: use Figure or Axes class methods.')
  # create axes, if necessary
  if ax is None: 
    if fig is None: fig,ax = getFigAx(1) # single panel
    else: ax = fig.axes[0]
  # varlist is the list of variable objects that are to be plotted
  #print varlist
  if isinstance(varlist,Variable): varlist = [varlist]
  elif not isinstance(varlist,(tuple,list)) or not all([isinstance(var,Variable) for var in varlist]): raise TypeError
  for var in varlist: var.squeeze() # remove singleton dimensions
  # linestyles is just a list of line styles for each plot
  if isinstance(linestyles,(basestring,NoneType)): linestyles = [linestyles]*len(varlist)
  elif not isinstance(linestyles,(tuple,list)): 
    if not all([isinstance(linestyles,basestring) for var in varlist]): raise TypeError
    if len(varlist) != len(linestyles): raise ListError, "Failed to match linestyles to varlist!"
  # varatts are variable-specific attributes that are parsed for special keywords and then passed on to the
  if varatts is None: varatts = [dict()]*len(varlist)  
  elif isinstance(varatts,dict):
    tmp = [varatts[var.name] if var.name in varatts else dict() for var in varlist]
    if any(tmp): varatts = tmp # if any variable names were found
    else: varatts = [varatts]*len(varlist) # assume it is one varatts dict, which will be used for all variables
  elif not isinstance(varatts,(tuple,list)): raise TypeError
  if not all([isinstance(atts,dict) for atts in varatts]): raise TypeError
  # check axis: they need to have only one axes, which has to be the same for all!
  if len(varatts) != len(varlist): raise ListError, "Failed to match varatts to varlist!"  
  for var in varlist: 
    if var.ndim > 1: raise AxisError, "Variable '{}' has more than one dimension; consider squeezing.".format(var.name)
    elif var.ndim == 0: raise AxisError, "Variable '{}' is a scalar; consider display as a line.".format(var.name)
  # loop over variables
  plts = []; varname = None; varunits = None; axname = None; axunits = None # list of plot handles
  for var,linestyle,varatt in zip(varlist,linestyles,varatts):
    varax = var.axes[0]
    # scale axis and variable values 
    axe, axunits, axname = getPlotValues(varax, checkunits=axunits, checkname=None)
    val, varunits, varname = getPlotValues(var, checkunits=varunits, checkname=None)
    # variable and axis scaling is not always independent...
    if var.plot is not None and varax.plot is not None: 
      if 'preserve' in var.plot and 'scalefactor' in varax.plot:
        if varax.units != axunits and var.plot.preserve == 'area':
          val /= varax.plot.scalefactor  
    # figure out keyword options
    kwatts = kwargs.copy(); kwatts.update(varatt) # join individual and common attributes     
    if 'label' not in kwatts: kwatts['label'] = var.name # default label: variable name
    # N.B.: other scaling behavior could be added here
    if lprint: print varname, varunits, val.mean()    
    if lsmooth: val = smooth(val)
    # figure out orientation
    if flipxy: xx,yy = val, axe 
    else: xx,yy = axe, val
    # call plot function
    if linestyle is None: plts.append(ax.plot(xx, yy, **kwatts)[0])
    else: plts.append(ax.plot(xx, yy, linestyle, **kwatts)[0])
  # set axes limits
  if isinstance(xlim,(list,tuple)) and len(xlim)==2: ax.set_xlim(*xlim)
  elif xlim is not None: raise TypeError
  if isinstance(ylim,(list,tuple)) and len(ylim)==2: ax.set_ylim(*ylim)
  elif ylim is not None: raise TypeError 
  # set title
  if title is not None:
    ax.set_title(title, dict(fontsize='medium'))
    pos = ax.get_position()
    pos = pos.from_bounds(x0=pos.x0, y0=pos.y0, width=pos.width, height=pos.height-0.03)    
    ax.set_position(pos)
  # set axes labels  
  if flipxy: xname,xunits,yname,yunits = varname,varunits,axname,axunits
  else: xname,xunits,yname,yunits = axname,axunits,varname,varunits
  if not xlabel: xlabel = '{0:s} [{1:s}]'.format(xname,xunits) if xunits else '{0:s}'.format(xname)
  else: xlabel = xlabel.format(xname,xunits)
  if not ylabel: ylabel = '{0:s} [{1:s}]'.format(yname,yunits) if yunits else '{0:s}'.format(yname)
  else: ylabel = ylabel.format(yname,yunits)
  # a typical custom label that makes use of the units would look like this: 'custom label [{1:s}]', 
  # where {} will be replaced by the appropriate default units (which have to be the same anyway)
  xpad =  2; xticks = ax.get_xaxis().get_ticklabels()
  ypad = -2; yticks = ax.get_yaxis().get_ticklabels()
  # len(xticks) > 0 is necessary to avoid errors with AxesGrid, which removes invisible tick labels 
  if len(xticks) > 0 and xticks[-1].get_visible(): ax.set_xlabel(xlabel, labelpad=xpad)
  elif len(yticks) > 0 and not title: yticks[0].set_visible(False) # avoid overlap
  if len(yticks) > 0 and yticks[-1].get_visible(): ax.set_ylabel(ylabel, labelpad=ypad)
  elif len(xticks) > 0: xticks[0].set_visible(False) # avoid overlap
  # make monthly ticks
  if axname == 'time' and axunits == 'month':
    ax.xaxis.set_minor_locator(mpl.ticker.AutoMinorLocator(2)) # ax.minorticks_on()
  # add legend
  if legend:
    legatts = dict()
    if ax.get_yaxis().get_label():
      legatts['fontsize'] = ax.get_yaxis().get_label().get_fontsize()
    if isinstance(legend,dict): legatts.update(legend) 
    elif isinstance(legend,(int,np.integer,float,np.inexact)): legatts['loc'] = legend
    ax.legend(**legatts)
  # add orientation lines
  if isinstance(xline,(int,np.integer,float,np.inexact)): ax.axhline(y=xline, color='black')
  elif isinstance(xline,dict): ax.axhline(**xline)
  if isinstance(yline,(int,np.integer,float,np.inexact)): ax.axvline(x=yline, color='black')
  elif isinstance(xline,dict): ax.axvline(**yline)
  # return handle
  return plts      
Пример #9
0
  if ax is None and ldetrend: ax = np.arange(var.size) # make dummy axis, if necessary
  if var.ndim != 1:
    shape = var.shape 
    var = var.ravel() # flatten array, if necessary
  else: shape = None
  # apply optional detrending
  if ldetrend or ltrend:
    # fit linear trend
    trend = np.polyfit(ax, var, deg=degree, rcond=rcond, w=w, full=False, cov=False)
    # evaluate and subtract linear trend
    if ldetrend and ltrend: raise ArgumentError, "Can either return trend/polyfit or residuals, not both."
    elif ldetrend and not ltrend: var -= np.polyval(trend, ax) # residuals
    elif ltrend and not ldetrend: var = np.polyval(trend, ax) # residuals
  # apply optional smoothing
  if lsmooth and lresidual: raise ArgumentError, "Can either return smoothed array or residuals, not both."
  elif lsmooth: var = smooth(var, window_len=window_len, window=window)  
  elif lresidual: var -= smooth(var, window_len=window_len, window=window)
  # return detrended and/or smoothed time-series
  if shape is not None: var = var.reshape(shape)
  return var

# function to smooth a vector (numpy array): moving mean, nothing fancy
def movingMean(x,i):
  ''' smooth a vector (x, numpy array) using a moving mean of window width 2*i+1 '''
  if x.ndim > 1: raise ValueError
  xs = x.copy() # smoothed output vector
  i = 2*i
  d = i+1 # denominator  for later
  while i>0:    
    t = x.copy(); t[i:] = t[:-i];  xs += t
    t = x.copy(); t[:-i] = t[i:];  xs += t
Пример #10
0
  if ax is None and ldetrend: ax = np.arange(var.size) # make dummy axis, if necessary
  if var.ndim != 1:
    shape = var.shape 
    var = var.ravel() # flatten array, if necessary
  else: shape = None
  # apply optional detrending
  if ldetrend or ltrend:
    # fit linear trend
    trend = np.polyfit(ax, var, deg=degree, rcond=rcond, w=w, full=False, cov=False)
    # evaluate and subtract linear trend
    if ldetrend and ltrend: raise ArgumentError, "Can either return trend/polyfit or residuals, not both."
    elif ldetrend and not ltrend: var -= np.polyval(trend, ax) # residuals
    elif ltrend and not ldetrend: var = np.polyval(trend, ax) # residuals
  # apply optional smoothing
  if lsmooth and lresidual: raise ArgumentError, "Can either return smoothed array or residuals, not both."
  elif lsmooth: var = smooth(var, window_len=window_len, window=window)  
  elif lresidual: var -= smooth(var, window_len=window_len, window=window)
  # return detrended and/or smoothed time-series
  if shape is not None: var = var.reshape(shape)
  return var

# function to smooth a vector (numpy array): moving mean, nothing fancy
def movingMean(x,i):
  ''' smooth a vector (x, numpy array) using a moving mean of window width 2*i+1 '''
  if x.ndim > 1: raise ValueError
  xs = x.copy() # smoothed output vector
  i = 2*i
  d = i+1 # denominator  for later
  while i>0:    
    t = x.copy(); t[i:] = t[:-i];  xs += t
    t = x.copy(); t[:-i] = t[i:];  xs += t