代码之家  ›  专栏  ›  技术社区  ›  Salim Fadhley

如何使scipy.interpolate给出超出输入范围的外推结果?

  •  67
  • Salim Fadhley  · 技术社区  · 16 年前

    我正在尝试移植一个程序,它使用一个手动滚动的插值程序(由一个数学家学院开发)来使用scipy提供的插值程序。我想使用或包装scipy插值程序,使其具有尽可能接近旧插值程序的行为。

    这两个函数之间的一个关键区别是,在我们的原始插值-如果输入值高于或低于输入范围,我们的原始插值将外推结果。如果使用scipy插值器尝试此操作,它将引发 ValueError . 以这个程序为例:

    import numpy as np
    from scipy import interpolate
    
    x = np.arange(0,10)
    y = np.exp(-x/3.0)
    f = interpolate.interp1d(x, y)
    
    print f(9)
    print f(11) # Causes ValueError, because it's greater than max(x)
    

    有没有一个合理的方法使它不是崩溃,而是最终的线将简单地做一个线性外推,继续由第一个和最后两个点定义的梯度到无穷大。

    注意,在真正的软件中,我并没有实际使用exp函数-这里只是为了说明!

    10 回复  |  直到 9 年前
        1
  •  32
  •   sastanin    16 年前

    1。常数外推

    你可以使用 interp 函数来自scipy,它将左右值外推为超出范围的常量:

    >>> from scipy import interp, arange, exp
    >>> x = arange(0,10)
    >>> y = exp(-x/3.0)
    >>> interp([9,10], x, y)
    array([ 0.04978707,  0.04978707])
    

    2。线性(或其他自定义)外推

    你可以在一个插值函数周围写一个包装器来处理线性外推。例如:

    from scipy.interpolate import interp1d
    from scipy import arange, array, exp
    
    def extrap1d(interpolator):
        xs = interpolator.x
        ys = interpolator.y
    
        def pointwise(x):
            if x < xs[0]:
                return ys[0]+(x-xs[0])*(ys[1]-ys[0])/(xs[1]-xs[0])
            elif x > xs[-1]:
                return ys[-1]+(x-xs[-1])*(ys[-1]-ys[-2])/(xs[-1]-xs[-2])
            else:
                return interpolator(x)
    
        def ufunclike(xs):
            return array(map(pointwise, array(xs)))
    
        return ufunclike
    

    extrap1d 获取一个插值函数并返回一个也可以进行外推的函数。你可以这样使用它:

    x = arange(0,10)
    y = exp(-x/3.0)
    f_i = interp1d(x, y)
    f_x = extrap1d(f_i)
    
    print f_x([9,10])
    

    输出:

    [ 0.04978707  0.03009069]
    
        2
  •  71
  •   Display Name    11 年前

    你可以看看 InterpolatedUnivariateSpline

    下面是一个使用它的示例:

    import matplotlib.pyplot as plt
    import numpy as np
    from scipy.interpolate import InterpolatedUnivariateSpline
    
    # given values
    xi = np.array([0.2, 0.5, 0.7, 0.9])
    yi = np.array([0.3, -0.1, 0.2, 0.1])
    # positions to inter/extrapolate
    x = np.linspace(0, 1, 50)
    # spline order: 1 linear, 2 quadratic, 3 cubic ... 
    order = 1
    # do inter/extrapolation
    s = InterpolatedUnivariateSpline(xi, yi, k=order)
    y = s(x)
    
    # example showing the interpolation for linear, quadratic and cubic interpolation
    plt.figure()
    plt.plot(xi, yi)
    for order in range(1, 4):
        s = InterpolatedUnivariateSpline(xi, yi, k=order)
        y = s(x)
        plt.plot(x, y)
    plt.show()
    
        3
  •  50
  •   David Nehme    9 年前

    从scipy版本0.17.0开始,有一个新的选项 scipy.interpolate.interp1d 这允许推断。只需在调用中设置fill_value=“extrapolate”。通过这种方式修改代码可以:

    import numpy as np
    from scipy import interpolate
    
    x = np.arange(0,10)
    y = np.exp(-x/3.0)
    f = interpolate.interp1d(x, y, fill_value='extrapolate')
    
    print f(9)
    print f(11)
    

    结果是:

    0.0497870683679
    0.010394302658
    
        4
  •  8
  •   subnivean Bryan Oakley    14 年前

    那么scipy.interpolate.splrep(使用1度且不平滑)如何:

    >> tck = scipy.interpolate.splrep([1, 2, 3, 4, 5], [1, 4, 9, 16, 25], k=1, s=0)
    >> scipy.interpolate.splev(6, tck)
    34.0
    

    因为34=25+(25-16),它似乎可以做你想做的事情。

        5
  •  6
  •   ryggyr    13 年前

    这里有一个只使用numpy包的替代方法。它利用了numpy的数组函数,因此在插值/外推大型数组时可能更快:

    import numpy as np
    
    def extrap(x, xp, yp):
        """np.interp function with linear extrapolation"""
        y = np.interp(x, xp, yp)
        y = np.where(x<xp[0], yp[0]+(x-xp[0])*(yp[0]-yp[1])/(xp[0]-xp[1]), y)
        y = np.where(x>xp[-1], yp[-1]+(x-xp[-1])*(yp[-1]-yp[-2])/(xp[-1]-xp[-2]), y)
        return y
    
    x = np.arange(0,10)
    y = np.exp(-x/3.0)
    xtest = np.array((8.5,9.5))
    
    print np.exp(-xtest/3.0)
    print np.interp(xtest, x, y)
    print extrap(xtest, x, y)
    

    编辑:马克·米科夫斯基建议修改“extap”函数:

    def extrap(x, xp, yp):
        """np.interp function with linear extrapolation"""
        y = np.interp(x, xp, yp)
        y[x < xp[0]] = yp[0] + (x[x<xp[0]]-xp[0]) * (yp[0]-yp[1]) / (xp[0]-xp[1])
        y[x > xp[-1]]= yp[-1] + (x[x>xp[-1]]-xp[-1])*(yp[-1]-yp[-2])/(xp[-1]-xp[-2])
        return y
    
        6
  •  5
  •   Iñigo Hernáez Corres    13 年前

    使用起来可能更快 布尔索引 具有 大型数据集 ,因为该算法检查每个点是否在间隔之外,而布尔索引允许更容易和更快的比较。

    例如:

    # Necessary modules
    import numpy as np
    from scipy.interpolate import interp1d
    
    # Original data
    x = np.arange(0,10)
    y = np.exp(-x/3.0)
    
    # Interpolator class
    f = interp1d(x, y)
    
    # Output range (quite large)
    xo = np.arange(0, 10, 0.001)
    
    # Boolean indexing approach
    
    # Generate an empty output array for "y" values
    yo = np.empty_like(xo)
    
    # Values lower than the minimum "x" are extrapolated at the same time
    low = xo < f.x[0]
    yo[low] =  f.y[0] + (xo[low]-f.x[0])*(f.y[1]-f.y[0])/(f.x[1]-f.x[0])
    
    # Values higher than the maximum "x" are extrapolated at same time
    high = xo > f.x[-1]
    yo[high] = f.y[-1] + (xo[high]-f.x[-1])*(f.y[-1]-f.y[-2])/(f.x[-1]-f.x[-2])
    
    # Values inside the interpolation range are interpolated directly
    inside = np.logical_and(xo >= f.x[0], xo <= f.x[-1])
    yo[inside] = f(xo[inside])
    

    在我的例子中,数据集是300000点,这意味着速度从25.8秒提高到0.094秒,这是 速度快250倍以上 .

        7
  •  2
  •   bananafish    12 年前

    我是通过在初始数组中添加一个点来实现的。这样我就避免了定义自制函数,线性外推(在下面的例子中:右外推)看起来没问题。

    import numpy as np  
    from scipy import interp as itp  
    
    xnew = np.linspace(0,1,51)  
    x1=xold[-2]  
    x2=xold[-1]  
    y1=yold[-2]  
    y2=yold[-1]  
    right_val=y1+(xnew[-1]-x1)*(y2-y1)/(x2-x1)  
    x=np.append(xold,xnew[-1])  
    y=np.append(yold,right_val)  
    f = itp(xnew,x,y)  
    
        8
  •  1
  •   Justin Peel    16 年前

    据我所知,恐怕做这件事不容易。我确信您已经知道,可以关闭边界错误并用常量填充超出范围的所有函数值,但这并没有真正的帮助。见 this question 在邮件列表上寻找更多的想法。也许你可以使用某种分段函数,但这似乎是一个很大的痛苦。

        9
  •  0
  •   Federico González Tamez    11 年前

    标准内插+线性外推:

        def interpola(v, x, y):
            if v <= x[0]:
                return y[0]+(y[1]-y[0])/(x[1]-x[0])*(v-x[0])
            elif v >= x[-1]:
                return y[-2]+(y[-1]-y[-2])/(x[-1]-x[-2])*(v-x[-2])
            else:
                f = interp1d(x, y, kind='cubic') 
                return f(v)
    
        10
  •  0
  •   tripleee    9 年前

    下面的代码提供了简单的外推模块。 K 是数据集的值 Y 必须根据数据集进行外推 X。 这个 numpy 模块是必需的。

     def extrapol(k,x,y):
            xm=np.mean(x);
            ym=np.mean(y);
            sumnr=0;
            sumdr=0;
            length=len(x);
            for i in range(0,length):
                sumnr=sumnr+((x[i]-xm)*(y[i]-ym));
                sumdr=sumdr+((x[i]-xm)*(x[i]-xm));
    
            m=sumnr/sumdr;
            c=ym-(m*xm);
            return((m*k)+c)