scipy.interpolateが入力範囲を超えて外挿された結果を出すようにするにはどうすればよいですか?


82

手巻き補間器(数学者の大学によって開発された)を使用するプログラムを移植して、scipyが提供する補間器を使用しようとしています。scipy補間器を使用またはラップして、古い補間器に可能な限り近い動作をさせたいと思います。

2つの関数の主な違いは、元の補間器では、入力値が入力範囲より上または下の場合、元の補間器が結果を外挿することです。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)

クラッシュする代わりに、最後の行が単純に線形外挿を行い、最初と最後の2つのポイントで定義された勾配を無限に続けるようにするための賢明な方法はありますか?

実際のソフトウェアでは、実際にはexp関数を使用していないことに注意してください。これは説明のためだけのものです。


2
scipy.interpolate.UnivariateSpline問題なく外挿しているようです。
heltonbiker 2012

回答:


38

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では 、イテレータを解決するためlistに、returnに追加する必要がありました return array(list(map(pointwise, array(xs))))
主にライト

このソリューションは、fill_value = "extrapolate"オプションよりも柔軟性があります。内部関数を「ポイントワイズ」で自分のニーズに適合させることができました。上のコメントを2番目に、必要に応じてリストを挿入します。そうは言っても、ジェネレーターが欲しいだけの場合もあります。
Wilmer E.Henao20年

1
に基づく最初のソリューションscipy.interpは非推奨であり、SciPy 2.0.0で表示されなくなるため、推奨されなくなったことに注意してください。彼らはnumpy.interp代わりに使用することをお勧めしますが、質問で述べられているように、ここでは機能しません
Yosko 2010

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

78

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 2017年

kind = 'cubic'の場合、fill_value = 'extrapolate'は機能しません。
vlmercado 2017年

@ a.sam:どういう意味かわかりません...おそらく、kind = 'linear'をfill_value = 'interpolation'と一緒に使用すると線形補間が得られ、fill_value = 'extrapolation'と一緒に使用すると線形外挿が得られますね
Moot 2017年

@vlmercado:それが機能しない方法を説明できますか?kind = 'cubic'を追加して上記の例を実行してみましたが、問題なく動作します。
Moot 2017年

@ Moot、scipy 0.18.1を使用すると、次のようになります。ValueError:外挿はkind = splineでは機能しません
vlmercado 2017年

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)

編集:MarkMikofskiが提案した「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.whereFalsey
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がfloatの場合、y [0]がnp.nanの場合、またはy [-1]がnp.nanの場合は機能しません。
2013年

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
ねえフェデリコ!なぜ反対票を投じたのか疑問に思われる場合は、質問に答えるときに、問題がどのように解決されるかを実際に説明する必要があることに注意してください。この回答は、それ自体がコードダンプにすぎず、その理由や有用性を説明する文を少なくとも数文含める必要があります。ありがとう!
フェリックス・ガニオン-グルニエ

0

コメントするのに十分な評判はありませんが、誰かがscipyを使用した線形2d補間の外挿ラッパーを探している場合に備えて、ここで示した回答を1d補間に適合させました。

def extrap2d(interpolator):
xs = interpolator.x
ys = interpolator.y
zs = interpolator.z
zs = np.reshape(zs, (-1, len(xs)))
def pointwise(x, y):
    if x < xs[0] or y < ys[0]:
        x1_index = np.argmin(np.abs(xs - x))
        x2_index = x1_index + 1
        y1_index = np.argmin(np.abs(ys - y))
        y2_index = y1_index + 1
        x1 = xs[x1_index]
        x2 = xs[x2_index]
        y1 = ys[y1_index]
        y2 = ys[y2_index]
        z11 = zs[x1_index, y1_index]
        z12 = zs[x1_index, y2_index]
        z21 = zs[x2_index, y1_index]
        z22 = zs[x2_index, y2_index]

        return (z11 * (x2 - x) * (y2 - y) +
        z21 * (x - x1) * (y2 - y) +
        z12 * (x2 - x) * (y - y1) +
        z22 * (x - x1) * (y - y1)
       ) / ((x2 - x1) * (y2 - y1) + 0.0)


    elif x > xs[-1] or y > ys[-1]:
        x1_index = np.argmin(np.abs(xs - x))
        x2_index = x1_index - 1
        y1_index = np.argmin(np.abs(ys - y))
        y2_index = y1_index - 1
        x1 = xs[x1_index]
        x2 = xs[x2_index]
        y1 = ys[y1_index]
        y2 = ys[y2_index]
        z11 = zs[x1_index, y1_index]
        z12 = zs[x1_index, y2_index]
        z21 = zs[x2_index, y1_index]
        z22 = zs[x2_index, y2_index]#

        return (z11 * (x2 - x) * (y2 - y) +
        z21 * (x - x1) * (y2 - y) +
        z12 * (x2 - x) * (y - y1) +
        z22 * (x - x1) * (y - y1)
        ) / ((x2 - x1) * (y2 - y1) + 0.0)
    else:
        return interpolator(x, y)
def ufunclike(xs, ys):
    if  isinstance(xs, int) or isinstance(ys, int) or isinstance(xs, np.int32) or isinstance(ys, np.int32):
        res_array = pointwise(xs, ys)
    else:
        res_array = np.zeros((len(xs), len(ys)))
        for x_c in range(len(xs)):
            res_array[x_c, :] = np.array([pointwise(xs[x_c], ys[y_c]) for y_c in range(len(ys))]).T

    return res_array
return ufunclike

私はあまりコメントしていませんが、コードが非常にクリーンではないことを認識しています。誰かがエラーを見つけたら、私に知らせてください。私の現在のユースケースでは、問題なく動作しています:)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.