acgc.stats.boxcar

  1# -*- coding: utf-8 -*-
  2
  3import numpy as np
  4
  5__all__ = [
  6    "boxcar",
  7    "boxcarpoly"
  8]
  9
 10def boxcarpoly( array, width, order=2, align='center'):
 11    '''Calculate a boxcar polynomial (i.e. running polynomial fit) of an array. 
 12    
 13    See `boxcar` for parameter definitions
 14    '''
 15    return boxcar( array, width, order=order, align=align, method='polynomial')
 16
 17def boxcar( array, width, align='center', method='mean', order=2 ):
 18    '''Calculate a boxcar average (i.e. running mean, median, or polynomial fit) of an array. 
 19    
 20    Elements of input array are assumed to be equally spaced along an (unspecified) 
 21    coordinate dimension.
 22
 23    A centered boxcar average with even widths is traditionally
 24    not allowed. This BOXCAR program allows even widths by
 25    giving half weights to elements on either end of the
 26    averaging window and full weights to all others. A centered
 27    average with even width therefore uses uses WIDTH+1
 28    elements in its averaging kernel.  
 29
 30    Parameters
 31    ----------
 32    array : array of float (N,)
 33        1D array of values to average
 34    width : int
 35        number of elements to include in the average. 
 36    align : str
 37        specifies how the averaging window for each element of the output array
 38        aligns with the input array. 
 39        Values: center (default), forward (trailing elements), backward (leading elements)
 40    method : {'mean' (default), 'median', 'polynomial'}
 41        specifies the averaging kernel function
 42    order : int, default=2
 43        specifies the polynomial order to fit within each boxcar window.
 44        This has no effect unless `method`='polynomial'
 45        A parabola is order=2; order=0 is equivalent to method="mean"
 46            
 47    Returns
 48    -------
 49    result : array of float (N,)
 50        1D array with averages with same size as input array. 
 51        Elements on either end may be NaN
 52    '''
 53
 54    N = array.size
 55
 56    # Initialize smoothed array
 57    smootharray = np.empty_like(array)
 58    smootharray[:] = np.NaN
 59
 60    # uniform averaging kernel
 61    kernel = np.ones(width)
 62
 63    # Setup for backward boxcar
 64    if align=='backward':
 65        # Half widths before and after element I
 66
 67        HW1 = width-1
 68        HW2 = width-HW1-1
 69
 70    # Setup for forward boxcar
 71    elif align=='forward':
 72        HW1 = 0
 73        HW2 = width-HW1-1
 74
 75    # Setup for centered boxcar
 76    elif align=='center':    
 77        # Separate treatments for odd and even widths
 78        if np.mod(width,2)==1:
 79            # Odd widths
 80            HW1 = np.floor(width/2)
 81            HW2 = width-HW1-1
 82
 83        else:
 84            # Even widths
 85            HW1 = width/2
 86            HW2 = width-HW1
 87
 88            # Uniform kernel in middle
 89            kernel = np.ones(width+1)
 90
 91            # Half weight kernel for ends
 92            kernel[0] = 0.5
 93            kernel[width] = 0.5
 94
 95            # Normalize the kernel
 96            kernel=kernel/kernel.sum()
 97    else:
 98        raise ValueError( f'align={align} not implemented. '
 99                         + 'Value should be "center", "forward" or "backward".' )
100
101    # Convert to integer type
102    HW1 = int(HW1)
103    HW2 = int(HW2)
104
105    # Do boxcar
106    for i in range(HW1,N-HW2-1):
107
108        # Sub array that we operate on
109        sub = array[i-HW1:i+HW2+1]
110
111        if method=='median':
112            # Running median
113            smootharray[i] = np.nanmedian(sub)
114
115        elif method=='mean':
116            # Running mean
117            # Kernel average, only over finite elements
118            #(NaNs removed from numerator and denominator
119            smootharray[i] = np.nansum( sub*kernel ) / np.sum(kernel[np.where(np.isfinite(sub))])
120
121        elif method=='polynomial':
122
123            # Local x coordinate
124            x = np.arange(-HW1,HW2+1)
125
126            # Fit with polynomial of specified order
127            # Avoid NaNs
128            idx = np.isfinite(sub)
129
130            # number of valid points (non NaN)
131            nvalid = np.sum(idx)
132
133            if nvalid==0:
134                # smoothed value is NaN when there are no valid points
135                smootharray[i] = np.nan
136
137            else:
138                # A polynomial fit requires at least (order+1) points.
139                # When there are fewer points, use the highest possible order.
140                ordmax = np.max([np.min([order,np.sum(idx)-1]),0])
141
142                p = np.polyfit(x[idx],sub[idx],ordmax,w=kernel[idx])
143
144                # The fitted value at local x=0 is just the constant term
145                smootharray[i] = p[order]
146
147        else:
148            raise ValueError( f'method={method} not implemented. '
149                             + 'Value should be "mean", "median" or "polynomial"')
150
151    return smootharray
def boxcar(array, width, align='center', method='mean', order=2):
 18def boxcar( array, width, align='center', method='mean', order=2 ):
 19    '''Calculate a boxcar average (i.e. running mean, median, or polynomial fit) of an array. 
 20    
 21    Elements of input array are assumed to be equally spaced along an (unspecified) 
 22    coordinate dimension.
 23
 24    A centered boxcar average with even widths is traditionally
 25    not allowed. This BOXCAR program allows even widths by
 26    giving half weights to elements on either end of the
 27    averaging window and full weights to all others. A centered
 28    average with even width therefore uses uses WIDTH+1
 29    elements in its averaging kernel.  
 30
 31    Parameters
 32    ----------
 33    array : array of float (N,)
 34        1D array of values to average
 35    width : int
 36        number of elements to include in the average. 
 37    align : str
 38        specifies how the averaging window for each element of the output array
 39        aligns with the input array. 
 40        Values: center (default), forward (trailing elements), backward (leading elements)
 41    method : {'mean' (default), 'median', 'polynomial'}
 42        specifies the averaging kernel function
 43    order : int, default=2
 44        specifies the polynomial order to fit within each boxcar window.
 45        This has no effect unless `method`='polynomial'
 46        A parabola is order=2; order=0 is equivalent to method="mean"
 47            
 48    Returns
 49    -------
 50    result : array of float (N,)
 51        1D array with averages with same size as input array. 
 52        Elements on either end may be NaN
 53    '''
 54
 55    N = array.size
 56
 57    # Initialize smoothed array
 58    smootharray = np.empty_like(array)
 59    smootharray[:] = np.NaN
 60
 61    # uniform averaging kernel
 62    kernel = np.ones(width)
 63
 64    # Setup for backward boxcar
 65    if align=='backward':
 66        # Half widths before and after element I
 67
 68        HW1 = width-1
 69        HW2 = width-HW1-1
 70
 71    # Setup for forward boxcar
 72    elif align=='forward':
 73        HW1 = 0
 74        HW2 = width-HW1-1
 75
 76    # Setup for centered boxcar
 77    elif align=='center':    
 78        # Separate treatments for odd and even widths
 79        if np.mod(width,2)==1:
 80            # Odd widths
 81            HW1 = np.floor(width/2)
 82            HW2 = width-HW1-1
 83
 84        else:
 85            # Even widths
 86            HW1 = width/2
 87            HW2 = width-HW1
 88
 89            # Uniform kernel in middle
 90            kernel = np.ones(width+1)
 91
 92            # Half weight kernel for ends
 93            kernel[0] = 0.5
 94            kernel[width] = 0.5
 95
 96            # Normalize the kernel
 97            kernel=kernel/kernel.sum()
 98    else:
 99        raise ValueError( f'align={align} not implemented. '
100                         + 'Value should be "center", "forward" or "backward".' )
101
102    # Convert to integer type
103    HW1 = int(HW1)
104    HW2 = int(HW2)
105
106    # Do boxcar
107    for i in range(HW1,N-HW2-1):
108
109        # Sub array that we operate on
110        sub = array[i-HW1:i+HW2+1]
111
112        if method=='median':
113            # Running median
114            smootharray[i] = np.nanmedian(sub)
115
116        elif method=='mean':
117            # Running mean
118            # Kernel average, only over finite elements
119            #(NaNs removed from numerator and denominator
120            smootharray[i] = np.nansum( sub*kernel ) / np.sum(kernel[np.where(np.isfinite(sub))])
121
122        elif method=='polynomial':
123
124            # Local x coordinate
125            x = np.arange(-HW1,HW2+1)
126
127            # Fit with polynomial of specified order
128            # Avoid NaNs
129            idx = np.isfinite(sub)
130
131            # number of valid points (non NaN)
132            nvalid = np.sum(idx)
133
134            if nvalid==0:
135                # smoothed value is NaN when there are no valid points
136                smootharray[i] = np.nan
137
138            else:
139                # A polynomial fit requires at least (order+1) points.
140                # When there are fewer points, use the highest possible order.
141                ordmax = np.max([np.min([order,np.sum(idx)-1]),0])
142
143                p = np.polyfit(x[idx],sub[idx],ordmax,w=kernel[idx])
144
145                # The fitted value at local x=0 is just the constant term
146                smootharray[i] = p[order]
147
148        else:
149            raise ValueError( f'method={method} not implemented. '
150                             + 'Value should be "mean", "median" or "polynomial"')
151
152    return smootharray

Calculate a boxcar average (i.e. running mean, median, or polynomial fit) of an array.

Elements of input array are assumed to be equally spaced along an (unspecified) coordinate dimension.

A centered boxcar average with even widths is traditionally not allowed. This BOXCAR program allows even widths by giving half weights to elements on either end of the averaging window and full weights to all others. A centered average with even width therefore uses uses WIDTH+1 elements in its averaging kernel.

Parameters
  • array (array of float (N,)): 1D array of values to average
  • width (int): number of elements to include in the average.
  • align (str): specifies how the averaging window for each element of the output array aligns with the input array. Values: center (default), forward (trailing elements), backward (leading elements)
  • method ({'mean' (default), 'median', 'polynomial'}): specifies the averaging kernel function
  • order (int, default=2): specifies the polynomial order to fit within each boxcar window. This has no effect unless method='polynomial' A parabola is order=2; order=0 is equivalent to method="mean"
Returns
  • result (array of float (N,)): 1D array with averages with same size as input array. Elements on either end may be NaN
def boxcarpoly(array, width, order=2, align='center'):
11def boxcarpoly( array, width, order=2, align='center'):
12    '''Calculate a boxcar polynomial (i.e. running polynomial fit) of an array. 
13    
14    See `boxcar` for parameter definitions
15    '''
16    return boxcar( array, width, order=order, align=align, method='polynomial')

Calculate a boxcar polynomial (i.e. running polynomial fit) of an array.

See boxcar for parameter definitions