次のようなデータが与えられたとします。
x = [1, 2.5, 3.4, 5.8, 6]
y = [2, 4, 5.8, 4.3, 4]
Python を使用して、1
and 2.5
、2.5
toなどの間を線形補間する関数を設計したいと考えています。3.4
この Python チュートリアルを調べてみましたが、まだ理解できません。
次のようなデータが与えられたとします。
x = [1, 2.5, 3.4, 5.8, 6]
y = [2, 4, 5.8, 4.3, 4]
Python を使用して、1
and 2.5
、2.5
toなどの間を線形補間する関数を設計したいと考えています。3.4
この Python チュートリアルを調べてみましたが、まだ理解できません。
import scipy.interpolate
y_interp = scipy.interpolate.interp1d(x, y)
print y_interp(5.0)
scipy.interpolate.interp1d
によって線形補間を行い、エラー状態を処理するようにカスタマイズできます。
あなたの質問を理解しているので、いくつかの関数を書きたいと思っていますy = interpolate(x_values, y_values, x)
。基本的な考え方は、次の手順に従います。y
x
x_values
を含む区間を定義する値のインデックスを見つけますx
。たとえば、x=3
リストの例では、含まれる間隔は[x1,x2]=[2.5,3.4]
、インデックスはi1=1
、i2=2
(y_values[i2]-y_values[i1])/(x_values[i2]-x_values[i1])
(つまりdy/dx
) で計算します。x
は、 での値にx1
勾配を加えて からの距離を掛けたものになりましたx1
。x
さらに、 が の間隔の外にある場合に何が起こるかを決定する必要がありx_values
ます。これはエラーであるか、勾配が最初/最後の間隔と同じであると仮定して「逆方向」に補間できます。
これは役に立ちましたか、それとももっと具体的なアドバイスが必要でしたか?
私はかなりエレガントな解決策(IMHO)を考え出したので、投稿せずにはいられません:
from bisect import bisect_left
class Interpolate(object):
def __init__(self, x_list, y_list):
if any(y - x <= 0 for x, y in zip(x_list, x_list[1:])):
raise ValueError("x_list must be in strictly ascending order!")
x_list = self.x_list = map(float, x_list)
y_list = self.y_list = map(float, y_list)
intervals = zip(x_list, x_list[1:], y_list, y_list[1:])
self.slopes = [(y2 - y1)/(x2 - x1) for x1, x2, y1, y2 in intervals]
def __getitem__(self, x):
i = bisect_left(self.x_list, x) - 1
return self.y_list[i] + self.slopes[i] * (x - self.x_list[i])
、 、、およびがすべてある間隔の整数である場合に、float
整数除算 (python <= 2.7) が作動して台無しにならないように、にマップします。x1
x2
y1
y2
でself.x_list__getitem__
が昇順でソートされているという事実を利用して、 inbisect_left
よりも小さい最大の要素のインデックスを (非常に) すばやく見つけます。x
self.x_list
次のようにクラスを使用します。
i = Interpolate([1, 2.5, 3.4, 5.8, 6], [2, 4, 5.8, 4.3, 4])
# Get the interpolated value at x = 4:
y = i[4]
簡単にするために、ここでは境界条件をまったく扱いませんでした。そのままでは、i[x]
forx < 1
は (2.5, 4) から (1, 2) への直線が負の無限大に拡張されたかのように機能しますが、i[x]
for x == 1
orx > 6
はIndexError
. すべての場合に IndexError を送出する方がよいでしょうが、これは読者の課題として残されています。:)
def interpolate(x1: float, x2: float, y1: float, y2: float, x: float):
"""Perform linear interpolation for x between (x1,y1) and (x2,y2) """
return ((y2 - y1) * x + x2 * y1 - x1 * y2) / (x2 - x1)
両端を外挿する代わりに、 の範囲を返すことができますy_list
。ほとんどの場合、アプリケーションは正常に動作しており、Interpolate[x]
はx_list
. 端から外挿することの (おそらく) 線形効果により、データが正常に動作していると誤解される可能性があります。
非線形の結果 ( と の内容によって制限される) を返すx_list
とy_list
、プログラムの動作によって、 の外側にある値の問題が警告される場合がありますx_list
。(非線形入力が与えられると、線形動作はバナナになります!)
の範囲外をy_list
返すことは、出力値の範囲を知っていることも意味します。大幅に下回る、または大幅に上回るに基づいて外挿すると、返される結果が期待値の範囲外になる可能性があります。Interpolate[x]
x_list
x
x_list[0]
x
x_list[-1]
def __getitem__(self, x):
if x <= self.x_list[0]:
return self.y_list[0]
elif x >= self.x_list[-1]:
return self.y_list[-1]
else:
i = bisect_left(self.x_list, x) - 1
return self.y_list[i] + self.slopes[i] * (x - self.x_list[i])
あなたのソリューションは Python 2.7 では機能しませんでした。x 要素の順序を確認中にエラーが発生しました。これを機能させるには、コードを次のように変更する必要がありました。
from bisect import bisect_left
class Interpolate(object):
def __init__(self, x_list, y_list):
if any([y - x <= 0 for x, y in zip(x_list, x_list[1:])]):
raise ValueError("x_list must be in strictly ascending order!")
x_list = self.x_list = map(float, x_list)
y_list = self.y_list = map(float, y_list)
intervals = zip(x_list, x_list[1:], y_list, y_list[1:])
self.slopes = [(y2 - y1)/(x2 - x1) for x1, x2, y1, y2 in intervals]
def __getitem__(self, x):
i = bisect_left(self.x_list, x) - 1
return self.y_list[i] + self.slopes[i] * (x - self.x_list[i])