私は、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関数を使用していないことに注意してください。これは説明のためだけです。
Scipyからinterp
関数を使用できます。これは、範囲を超える定数として左右の値を外挿します。
>>> 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])
線形外挿を処理する補間関数の周りにラッパーを書くことができます。例えば:
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]
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()
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
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)なので、あなたが望むことをするようです。
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)
編集:マーク・ミコフスキーが提案した「エクストラップ」機能の修正:
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
boolean indexing with large datasetsを使用する方が速い場合があります。これは、アルゴリズムがすべてのポイントが間隔の外側にあるかどうかをチェックするためです。
例えば:
# 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倍以上高速です。
最初の配列にポイントを追加することでそれを行いました。このようにして、私は自作の関数を定義することを避け、線形外挿(下の例では右外挿)は問題ないように見えます。
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)
私の知る限り、Scipyでこれを行うのは簡単ではないのではないかと心配しています。あなたが知っていることはかなり確信しているので、境界エラーをオフにし、範囲を超えるすべての関数値を定数で埋めることができますが、それは実際には役立ちません。他のアイデアについては、メーリングリストの この質問 を参照してください。ある種の区分的機能を使用できるかもしれませんが、それは大きな苦痛のようです。
標準内挿+線形外挿:
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)
以下のコードは、簡単な外挿モジュールを提供します。 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)