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


82

我正在尝试移植一个使用手推插值器(由数学家colleage开发)的程序,以使用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函数-此处仅用于说明!


2
scipy.interpolate.UnivariateSpline似乎可以毫无问题地推断出来。
heltonbiker 2012年

Answers:


37

1.恒定外推

您可以使用interpscipy中的函数,它将左值和右值推断为超出范围的常数:

>>> 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(list(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]

1
在Python 3.6中,我必须添加listreturn: return array(list(map(pointwise, array(xs)))) 来解决迭代器。
user21387

该解决方案比fill_value =“ extrapolate”选项更加灵活。我能够“按需”调整内部函数以适应我的需要,我在上面的注释中添加了第二个并在需要时插入列表。话虽如此,有时您可能只想拥有一个发电机。
Wilmer E. Henao

1
请注意,scipy.interp不再建议使用基于的第一个解决方案,因为它已过时,它将在SciPy 2.0.0中消失。他们建议numpy.interp改用,但如问题所述,此处将不起作用
Yosko

86

您可以看一下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()

2
这是最好的答案。那就是我所做的。I used k=1 (order),因此它成为线性插值,并且I used bbox=[xmin-w, xmax+w] where w is my tolerance
eusoubrasileiro 2014年

76

从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

外推类型是否类似于内插类型?例如,我们可以使用最近点外推法进行线性插值吗?
a.sam

如果kind ='cubic',则fill_value ='extrapolate'不起作用。
vlmercado

@ a.sam:我不确定您的意思是...大概,如果您将kind ='linear'与fill_value ='interpolation'配合使用,则会得到线性插值,而如果将其与fill_value ='extrapolation'结合使用那么你会得到线性外推法,不是吗?
模拟辩论

@vlmercado:您能以什么方式解释它不起作用吗?我尝试运行上面的示例,并添加了kind ='cubic',对我来说效果很好。
模拟辩论

@Moot,使用scipy 0.18.1,我得到以下信息:ValueError:外推不适用于kind = spline
vlmercado

8

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),所以它似乎可以满足您的要求。


7

这是仅使用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)

编辑:Mark Mikofski建议对“ extrap”功能进行修改:

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

2
实际示例为+1,但是您也可以使用布尔值索引,并且此处 y[x < xp[0]] = fp[0] + (x[x < xp[0]] - xp[0]) / (xp[1] - xp[0]) * (fp[1] - fp[0])y[x > xp[-1]] = fp[-1] + (x[x > xp[-1]] - xp[-1]) / (xp[-2] - xp[-1]) * (fp[-2] - fp[-1])而不是)使用np.where,因为该False选项y不会更改。
Mark Mikofski 2012年

6

布尔索引用于大型数据集可能会更快,因为该算法会检查每个点是否都在区间之外,而布尔索引则允许更轻松,更快速地进行比较。

例如:

# 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倍以上


这很好,但是如果x0是浮点型,y [0]是np.nan或y [-1]是np.nan则不起作用。
伸展

2

我是通过在初始数组中添加一个点来实现的。这样,我避免定义自定义函数,并且线性外推​​(在下面的示例中为:右外推)看起来还可以。

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)  

1

据我所知,恐怕在Scipy中很难做到这一点。我可以确定,您可以关闭边界错误,并用常量填充超出范围的所有函数值,但这并没有真正的帮助。有关更多想法,请参阅邮件列表中的此问题。也许您可以使用某种分段功能,但这似乎很麻烦。


这就是我至少在scipy 0.7时得出的结论,但是21个月前编写的本教程建议interp1d函数具有可以设置为“线性”的高和低属性,该教程尚不清楚此版本的scipy适用于: projects.scipy.org/scipy/browser/branches/Interpolate1D/docs/...
萨利姆Fadhley

看来这是一个分支的一部分,尚未被吸收到主版本中,因此它可能仍然存在一些问题。当前的代码位于projects.scipy.org/scipy/browser/branches/interpolate/…,尽管您可能想滚动到页面底部并单击以纯文本格式下载。我认为这看起来很有希望,尽管我自己还没有尝试过。
贾斯汀·皮

1

以下代码为您提供了简单的外推模块。k是必须根据数据集x将数据集y外推到的值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)

0

标准插值+线性插值:

    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)

1
嘿,费德里科!如果您想知道为什么不满意,请注意,在回答问题时,您需要实际解释它是如何解决问题的。实际上,此答案只是一个代码转储,并且至少应包含几句话来说明其用途和/或用法。谢谢!
费利克斯·加侬-格尼尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.