示例#1
0
def interpolate_2d_linear_v1(
        x_in,
        y_in,
        samples,
        x_out,
        y_out,
        dtype=np.float,
        out_of_bounds = 'nan',
        info_on_screen=True,
        prefix='interpolate_2D_linear: ',
        ):
    """Linear 2D-interpolation for numericals or datetime.
    
    Parameters
    ----------
    * x_in, y_in: the sample grid. They can both either be 1D-arrays of
      numercials or of instances of datetime
    * samples: 2D-array of the function values on the (x_in,y_in)-grid.
      Must be given in the form samples[x_in, y_in]
    * x_out, y_out: the grid on which samples will be interpolated. If the
      elements of x_in are instances of datetime, the those of x_out
      should be, too. The same holds for y_in and y_out.
    * out_of_bounds ('nan' or 'zero'): if x_out or y_out are outside the
      input grid, then the output will be assigned this value
    * dtype : datatype of the output array.
   
    Returns
    -------
    The function returns a 2D-array which is a linear interpolation
    between the sample values in the form samples_out[x_out,y_out]
    
    NOTE: implementation assume_sorted to be done!
   
    Written in 2014
    by Andreas Anhaeuser
    Insitute for Geophysics and Meteorology
    University of Cologne
    Germany
    <*****@*****.**> """
    #### INPUT CHECK ###
    if not (len(x_in), len(y_in)) == np.shape(samples):
        raise AssertionError('Dimensions of x_in, y_in and samples do ' + \
            'not match.')
    xi = np.array(x_in)
    yi = np.array(y_in)
    xo = np.array(x_out)
    yo = np.array(y_out)
    zi = np.array(samples)
    
    # convert datetime_list into a numerical list:
    for c in [xi, yi, xo, yo]:
        if c[0].__class__ == dt.datetime:
            c[:] = np.array(du.datetime_to_seconds(c))

    # 2D linear interpolation function:
    def f(xin, yin, zin, x, y, oob):
        if x < min(xin) or x > max(xin) or y < min(yin) or y > max(yin):
            return oob        
        
        x1, idx1 = nearest_neighbour(xin, x, direction='down')
        x2, idx2 = nearest_neighbour(xin, x, direction='up'  )
        y1, idy1 = nearest_neighbour(yin, y, direction='down')
        y2, idy2 = nearest_neighbour(yin, y, direction='up'  )
        z11 = zin[idx1, idy1]
        z12 = zin[idx1, idy2]
        z21 = zin[idx2, idy1]
        z22 = zin[idx2, idy2]
        
        # avoid problems if D == 0:
        if x1 == x2 and y1 == y2:
            result = z11
        elif x1 == x2:
            # interpolate only in y-direction
            result = ((y-y1)*z12 + (y2-y)*z11)/(y2-y1)
        elif y1 == y2:
            # interpolate only in x-direction
            result = ((x - x1) * z21 + (x2 - x) * z11) / (x2  -x1)
        else:
            # 2D interpolation:
            D = (x2 - x1) * (y2 - y1)
            result =  (1/D * (z11 * (x2 - x)  *(y2 - y) + \
                              z21 * (x - x1) * (y2 - y) + \
                              z12 * (x2 - x) * (y - y1) + \
                              z22 * (x - x1) * (y - y1) \
                              )  \
                       )
        return result
               
    # out of bounds value:
    if out_of_bounds in ['zero', 'Zero', 'zeros', 'Zeros', '0', 0]:
        oob = 0
    elif out_of_bounds in ['nan', 'nans', 'NaN', 'NaNs', np.nan]:
        oob = np.nan

    X = len(xo)
    Y = len(yo)
    zo = np.zeros([X, Y], dtype=dtype)

    performance_info = PI((X*Y), prefix=prefix)
    for i in range(X):
        for j in range(Y):
            if info_on_screen:
                performance_info.loop_and_show()

            zo[i, j] = dtype(f(xi, yi, zi, xo[i], yo[j], oob))
                
    return zo 
示例#2
0
def interpolate_2d_to_1d(
        x_in,
        y_in,
        samples,
        x_out,
        y_out,
        x_tolerance=0.,
        y_tolerance=0.,
        out_of_bounds = 'nan',
        info_on_screen=True,
        prefix='interpolate_2D_linear: ',
        assume_sorted=False,
        dtype=float,
        ):
    """Linear 2D-interpolation for numericals or datetime.
    
        Parameters
        ----------
        x_in, y_in: the sample grid. They can both either be 1D-arrays of
            numercials or of instances of datetime
        samples: 2D-array of the function values on the (x_in,y_in)-grid.
            Must be given in the form samples[x_in, y_in]
        x_out, y_out: the grid on which samples will be interpolated. If the
            elements of x_in are instances of datetime, the those of x_out
            should be, too. The same holds for y_in and y_out.
        out_of_bounds ('nan' or 'zero'): if x_out or y_out are outside the
            input grid, then the output will be assigned this value
        dtype : datatype of the output array.
       
        Returns
        -------
        The function returns a 2D-array which is a linear interpolation
        between the sample values in the form samples_out[x_out,y_out]
        
        NOTE: implementation assume_sorted to be done!
       
        Written in 2014
        by Andreas Anhaeuser
        Insitute for Geophysics and Meteorology
        University of Cologne
        Germany
        <*****@*****.**>
    """
    #### INPUT CHECK ###
    if not (len(x_in), len(y_in)) == np.shape(samples):
        raise AssertionError('Dimensions of x_in, y_in and samples do ' + \
            'not match.')
    xi = np.array(x_in)
    yi = np.array(y_in)
    xo = np.array(x_out)
    yo = np.array(y_out)
    zi = np.array(samples)
    
    # convert datetime_list into a numerical list:
    for c in [xi, yi, xo, yo]:
        if c[0].__class__ == dt.datetime:
            c[:] = np.array(du.datetime_to_seconds(c))

    # out of bounds value:
    if out_of_bounds in ['zero', 'Zero', 'zeros', 'Zeros', '0', 0]:
        oob = 0
    elif out_of_bounds in ['nan', 'nans', 'NaN', 'NaNs', np.nan]:
        oob = np.nan

    N = len(xo)
    assert len(yo) == N
    zo = oob * np.ones(N, dtype=dtype)

    f = interpolate_2d_one_point
    for n in range(N):
        z = f(xi, yi, zi, xo[n], yo[n], oob, assume_sorted=assume_sorted)
        zo[n] = dtype(z)

    return zo 
示例#3
0
def interpolate_1d_linear_old(
        x_in,
        val_in,
        x_out,
        x_tolerance=0,
        out_of_bounds='nan',
        assume_sorted=True,
        many_nans=False,
        ):
    """Linear 1D-interpolation for numbers or datetime.

        x_in and x_out can be floats or datetime.datetime instances.
       
        Parameters
        ----------
        x_in : array of floats or list of datetime.datetime
        x_out : array of floats or list of datetime.datetime
        val_in : array of floats
            same length as x_in
        x_tolerance : float or datetime.timedelta, optional
            only nearest neighbours that are close than x_tolerance are
            considered. 0 causes infinite tolerance! Default: 0.
        out_of_bounds : {'zero', 'nan', 'nearest', float}
            out of bounds value are replaced by this
        assume_sorted : bool
            if True, the function assumes that x_in be sorted in rising order
        many_nans : bool
            Switch this to True, if either x_in or val_in contain a lot of
            nan's.  The function yields exactly the same result, regardless on
            whether many_nans is True or False. It is just a matter of
            performance.  If x_in or val_in contain a lot of nan's, switching
            this to True will speed up the function. If they don't, it will
            slow down the function.
        
        Returns
        -------
        val_out : array
            The output is a linear interpolation between the sample values
            given in values_sample at the times given in val_in.     

        Notes
        -----
        float vs datetime.datetime:
            If some of x_in, x_out, or x_tolerance are given in numerical
            values and others as instances of datetime.datetime (or
            datetime.timedelta in the case of x_tolerance), the numericals are
            considered as seconds (since 1970).

        x_tolercance:
            if 0, this is interpreted as infinite tolerance!
       
        Author
        ------
        Written in 2014-2016
        by Andreas Anhaeuser
        Insitute for Geophysics and Meteorology
        University of Cologne
        Germany
        <*****@*****.**>
    """
    
    #### CHECK INPUT ###
    if not len(x_in) == len(val_in):
        raise LookupError('x_in and val_in must be of the same length.')

    #### Unsorted
    # recursively call function if unsorted:
    if not assume_sorted:
        idx_old = np.argsort(x_in)
        xi_sor  = sorted(x_in)
        vi_sor  = [val_in[i] for i in idx_old]
        
        o       = out_of_bounds
        return interpolate_1d_linear(x_in = xi_sor,
            val_in = vi_sor, x_out = x_out, x_tolerance = x_tolerance, 
            out_of_bounds = o, assume_sorted = True)
        
    # this is for dealing with out of bounds values lateron:
    if out_of_bounds in ['nan', 'NaN']:
        oob  = 'val'
        oobv = np.nan
    elif out_of_bounds in ['zero', 'zeros', '0']:
        oob  = 'val'
        oobv = 0.
    elif isinstance(out_of_bounds, numbers.Number):
        oob  = 'val'
        oobv = out_of_bounds
    else:
        oob  = 'nn'
        oobv = None
    
    # special case: empty input array:
    if len(x_in) == 0:
        return np.array([oobv] * len(x_out))
    
    # special case: empty output array:
    if len(x_out) == 0:
        return np.array([])
    
    # copy arrays and convert to np.array:
    xi  = x_in[:]
    xo  = x_out[:]
    vi  = val_in[:]
    tol = x_tolerance
    
    # convert datetime_list into a numerical list:
    for c in [xi, xo, vi]:
        if isinstance(c[0], dt.datetime):
            c[:] = du.datetime_to_seconds(c)

    # convert numpy arrays to lists:
    def convert(x):
        if isinstance(x, np.ndarray):
            return x.tolist()
        else:
            return x
    xi = convert(xi)
    xo = convert(xo)
    vi = convert(vi)

    # convert tol from timedelta to numerical:
    if isinstance(tol, dt.timedelta):
        tol = tol.seconds

    # convert tol==0 to inf:
    if tol == 0:
        tol = np.inf

    #### NaN's
    # If vi contains a lot of nan's, this will slow down nearest_neighbour.
    # For this reason, delete these elements from xi and vi:
    if many_nans:
        n = 0
        numbers = []
        for n in range(len(xi)):
            if not (np.isnan(vi[n]) or np.isnan(xi[n])):
                numbers.append(n)
        xi = [xi[n] for n in numbers]
        vi = [vi[n] for n in numbers]
            
    #### INTERPOLATION    
    # interpolation function:
    def f(xlo, xhi, vlo, vhi, x):
        """Linear interpolation function."""
        return ((x-xlo) * vhi + (xhi-x) * vlo) / (xhi-xlo)
        
    # initialization:
    valout = []
    N = len(xo)
    n = 0
    for x in xo:
        n += 1
        ###
        # FIND NEAREST NEIGHBOURS AND DETERMINE DISTANCES
        # nearest neighbours:
        xlo, ilo, vlo = nearest_neighbour(
            xi, x,
            direction='down', values=vi, skip_nans=True, assume_sorted=True)
            
        xhi, ihi, vhi = nearest_neighbour(
                xi, x, direction='up', values=vi,
                skip_nans=True, assume_sorted=True)


        # if no nearest neighbour is found:
        if xlo==None: xlo = -np.inf
        if xhi==None: xhi = +np.inf
        if vlo==None: vlo =  np.nan
        if vhi==None: vhi =  np.nan
        
        # distances:
        distlo = x-xlo
        disthi = xhi-x
        dist   = [distlo, disthi]
      
        ###
        # DETERMINE THE INTERPOLATED VALUE v CORRESPONDING TO x
        # special case: x is exactly on a non-nan input point:
        if max(dist) == 0:
            v = vlo   # (vlo==vhi in this case)
        
        # finite tolerance:
        elif tol < np.inf:
            inbounds = (distlo <= tol, disthi <= tol)
            
            if inbounds == (True, True):
                v = f(xlo, xhi, vlo, vhi, x)
            elif inbounds == (True, False):
                v = vlo
            elif inbounds == (False, True):
                v = vhi
            elif inbounds == (False, False) and oob == 'val':
                v = oobv
            elif inbounds == (False, False) and oob == 'nn':
                if distlo <= disthi:
                    v = vlo
                else:
                    v = vhi
        
        # infinite tolerance:
        elif tol == np.inf:
            neighbours = (distlo<np.inf,disthi<np.inf)
            
            if neighbours==(True, True):
                v = f(xlo,xhi,vlo,vhi,x)
            elif False in neighbours and oob=='val':
                v = oobv
            elif False in neighbours and oob=='nn':
                if distlo<=disthi: v = vlo
                else:              v = vhi
                

        # EXTEND OUTPUT ARRAY BY v:  
        valout.append(v)
        
    return np.array(valout)
示例#4
0
def interpolate_2d_linear_v2(
        x_in,
        y_in,
        samples,
        x_out,
        y_out,
        x_tolerance=0.,
        y_tolerance=0.,
        out_of_bounds = 'nan',
        info_on_screen=True,
        prefix='interpolate_2D_linear: ',
        assume_sorted=False,
        dtype=float,
        ):
    """Linear 2D-interpolation for numericals or datetime.
    
        Parameters
        ----------
        x_in, y_in
            the sample grid. They can both either be 1D-arrays of numercials or
            of instances of datetime
        samples: 2d-array
            the function values on the (x_in,y_in)-grid. Must be given in the
            form samples[x_in, y_in]
        x_out, y_out
            the grid on which samples will be interpolated. If the elements of
            x_in are instances of datetime, the those of x_out should be, too.
            The same holds for y_in and y_out.
        out_of_bounds : {'nan', 'zero'}
            if x_out or y_out are outside the input grid, then the output will
            be assigned this value
        dtype
            datatype of the output array.
       
        Returns
        -------
        The function returns a 2D-array which is a linear interpolation
        between the sample values in the form samples_out[x_out,y_out]
        
        To do
        -----
        implement `assume_sorted`
       
        Written in 2014
        by Andreas Anhaeuser
        Insitute for Geophysics and Meteorology
        University of Cologne
        Germany
        <*****@*****.**>
    """
    #### INPUT CHECK ###
    if not (len(x_in), len(y_in)) == np.shape(samples):
        raise AssertionError('Dimensions of x_in, y_in and samples do ' + \
            'not match.')
    xi = np.array(x_in)
    yi = np.array(y_in)
    xo = np.array(x_out)
    yo = np.array(y_out)
    zi = np.array(samples)
    
    # convert datetime_list into a numerical list:
    for c in [xi, yi, xo, yo]:
        if c[0].__class__ == dt.datetime:
            c[:] = np.array(du.datetime_to_seconds(c))

    # 2D linear interpolation function:
    f = interpolate_2d_one_point

    # out of bounds value:
    if out_of_bounds in ['zero', 'Zero', 'zeros', 'Zeros', '0', 0]:
        oob = 0
    elif out_of_bounds in ['nan', 'nans', 'NaN', 'NaNs', np.nan]:
        oob = np.nan

    X = len(xo)
    Y = len(yo)
    zo = np.zeros([X, Y], dtype=dtype)

    Ntotal = X * Y
    header = 'interpolate 2D'
    silent = not info_on_screen
    with Chronometer(Ntotal, header=header, silent=silent) as chrono:
        if silent:
            chrono.exit()

        for i, j in itertools.product(range(X), range(Y)):
            if not silent:
                chrono.show().loop()

            z = f(xi, yi, zi, xo[i], yo[j], oob, assume_sorted=assume_sorted)
            zo[i, j] = dtype(z)
                
    return zo 
示例#5
0
def interpolate_1d_linear(
        x_in,
        val_in,
        x_out,
        x_tolerance=0,
        out_of_bounds='nearest',
        assume_sorted=True,
        many_nans=False,
        ):
    """Linear 1D-interpolation for numbers or datetime.

        x_in and x_out can be floats or datetime.datetime instances.
       
        Parameters
        ----------
        x_in : array of floats or list of datetime.datetime
        x_out : array of floats or list of datetime.datetime
        val_in : array of floats
            same length as x_in
        x_tolerance : float or datetime.timedelta, optional
            only nearest neighbours that are close than x_tolerance are
            considered. 0 causes infinite tolerance! Default: 0.
        out_of_bounds : {'zero', 'nan', 'nearest', 'ext', float}
            out of bounds value are replaced by this
            'ext' : extrapolate
        assume_sorted : bool
            if True, the function assumes that x_in be sorted in rising order
        many_nans : bool
            no effect (kept for backwards compatibility)
        
        Returns
        -------
        val_out : array
            The output is a linear interpolation between the sample values
            given in values_sample at the times given in val_in.     

        Notes
        -----
        float vs datetime.datetime:
            If some of x_in, x_out, or x_tolerance are given in numerical
            values and others as instances of datetime.datetime (or
            datetime.timedelta in the case of x_tolerance), the numericals are
            considered as seconds (since 1970).

        x_tolercance:
            if 0, this is interpreted as infinite tolerance!
       
        Author
        ------
        Written in 2014-2016
        by Andreas Anhaeuser
        Insitute for Geophysics and Meteorology
        University of Cologne
        Germany
        <*****@*****.**>
    """
    ###################################################
    # INPUT CHECK                                     #
    ###################################################
    assert len(x_in) == len(val_in)

    ###################################################
    # CONVERSIONS                                     #
    ###################################################
    if isinstance(x_in[0], dt.datetime):
        x_in = du.datetime_to_seconds(x_in)
    if isinstance(x_out[0], dt.datetime):
        x_out = du.datetime_to_seconds(x_out)
    if isinstance(x_tolerance, dt.timedelta):
        x_tolerance = x_tolerance.total_seconds()

    xis = np.array(x_in)
    xos = np.array(x_out)
    vis = np.array(val_in)
    tol = x_tolerance

    # tol: 0 --> inf
    if tol == 0:
        tol = np.inf

    ###################################################
    # SORT                                            #
    ###################################################
    # (recursively call function if unsorted)
    if not assume_sorted:
        idx = np.argsort(xis)
        xis = xis[idx]
        vis  = vis[idx]
        
        return interpolate_1d_linear(xis=xis,
            vis=vis, xos=xos, x_tolerance=tol, 
            out_of_bounds=out_of_bounds, assume_sorted=True)
        
    ###################################################
    # OUT OF BOUNDS                                   #
    ###################################################
    if out_of_bounds in ['nan', 'NaN']:
        oob  = 'val'
        oobv = np.nan
    elif out_of_bounds in ['zero', 'zeros', '0']:
        oob  = 'val'
        oobv = 0.
    elif isinstance(out_of_bounds, numbers.Number):
        oob  = 'val'
        oobv = out_of_bounds
    elif out_of_bounds == 'nearest':
        oob  = 'nn'
        oobv = None
    elif out_of_bounds == 'ext':
        raise NotImplementedError(
                'Current implementation seems buggy (AA 2016-09-25.)')
        oob  = 'ex'
        oobv = None
    else:
        raise ValueError('Uncrecognized out_of_bounds value: ' + 
                str(out_of_bounds))
    
    ###################################################
    # SPECIAL CASES                                   #
    ###################################################
    # special case: empty input array:
    if len(x_in) == 0:
        return np.array([oobv] * len(x_out))
    
    # special case: empty output array:
    if len(xos) == 0:
        return np.array([])
    
    ###################################################
    # MANY                                            #
    ###################################################
    neg = np.isnan(xis) | np.isnan(vis)
    use = ~neg
    xis = xis[use]
    vis = vis[use]

    ###################################################
    # SPECIAL CASES                                   #
    ###################################################
    # special case: input array length 0
    if len(xis) == 0:
        return np.nan * xos

    # special case: input array length 1
    if len(xis) == 1:
        if oobv is not None:
            # idea: all entries which are close enough (indices `pos`), are
            # assigned vis[0], all others are oobv

            # default
            vos = oobv * np.ones(len(xos))

            # find positions close enough
            diff = np.abs(xos - xis)
            pos = diff <= tol

            # assign them the only existing value
            vos[pos] = vis[0]
        else:
            # all out-values are equal to the single in-value
            vos = vis[0] * np.ones(len(xos))
        return vos
            
    ###################################################
    # INTERPOLATION FUNCTION                          #
    ###################################################
    def f(x1, x2, y1, y2, x):
        """Linear interpolation function."""
        # special case
        if x1 == x2:
            return y1

        # regular case
        m = (y2 - y1) / (x2 - x1)
        return y1 + m * (x - x1)        

    ###################################################
    # INITIALIZATION                                  #
    ###################################################
    N = len(xos)
    I = len(xis)
    vos = np.nan * np.empty(N)
    xlos = np.nan * np.empty(N)
    xhis = np.nan * np.empty(N)

    chrono = Chronometer(N, header='Interpolation')
    for n in range(N):
        chrono.loop_and_show()
        ###################################################
        # FIND NEAREST NEIGHBOUR                          #
        ###################################################
        xo = xos[n]
        xlo, ilo, vlo = nearest_neighbour(
            xis, xo,
            direction='down', values=vis, skip_nans=False, assume_sorted=True)
            
        xhi, ihi, vhi = nearest_neighbour(
                xis, xo, direction='up', values=vis,
                skip_nans=True, assume_sorted=True)

        # out of bounds:
        if xlo is None:
            xlo = -np.inf
        if xhi is None:
            xhi = +np.inf

        # out of tolerance:
        if xo - xlo > tol:
            xlo = -np.inf
        if xhi - xo > tol:
            xhi = +np.inf
        
        # distances:
        dlo = xo - xlo
        dhi = xhi - xo

        ###################################################
        # REGULAR CASE                                    #
        ###################################################
        if np.isfinite(dlo) and np.isfinite(dhi):
            vos[n] = f(xlo, xhi, vlo, vhi, xo)
            continue
      
        ###################################################
        # OUT OF TOLERANCE ON BOTH SIDES                  #
        ###################################################
        if ~np.isfinite(dlo) and ~np.isfinite(dhi):
            if oob == 'val':
                vos[n] = oobv
            continue

        ###################################################
        # OUT OF BOUNDS ON LOWER SIDE                     #
        ###################################################
        if ~np.isfinite(dlo) and np.isfinite(dhi):
            # constant value
            if oob == 'val':
                vos[n] = oobv
                continue

            # nearest neighbour
            elif oob == 'nn':
                vos[n] = vhi
                continue

            # extrapolate
            elif oob  == 'ex':
                # upper bound
                if ihi == I - 1:
                    vos[n] = vhi
                    continue

                # get next higher x
                xhi2 = xis[ihi+1]
                vhi2 = vis[ihi+1]
                dhi2 = xhi2 - xhi

                # xhi2 out of tolerance
                if dhi2 > tol:
                    vos[n] = vhi
                    continue

                # extrapolate
                vos[n] = f(xhi, xhi2, vhi, vhi2, xo)
                continue

        ###################################################
        # OUT OF BOUNDS ON UPPER SIDE                     #
        ###################################################
        if np.isfinite(dlo) and ~np.isfinite(dhi):
            # constant value
            if oob == 'val':
                vos[n] = oobv
                continue

            # nearest neighbour
            elif oob == 'nn':
                vos[n] = vlo
                continue

            # extrapolate
            elif oob  == 'ex':
                # lower bound
                if ilo == 0:
                    vos[n] = vlo
                    continue

                # get next higher x
                xlo2 = xis[ilo-1]
                vlo2 = vis[ilo-1]
                dlo2 = xlo - xlo2

                # xhi2 out of tolerance
                if dlo2 > tol:
                    vos[n] = vlo
                    continue

                # extrapolate
                vos[n] = f(xlo, xlo2, vlo, vlo2, xo)
                continue
    chrono.resumee()

    return vos