2011-09-08 51 views
13

我對編程相當陌生,並且認爲我會嘗試編寫線性插值函數。線性插值 - Python

說我給定的數據,如下所示: X = [1,2.5,3.4,5.8,6] Y = [2,4,5.8,4.3,4]

我想設計的功能這將使用Python在1到2.5,2.5到3.4之間線性插入。

我已經試過 http://docs.python.org/tutorial/,但我仍然無法得到我的頭。

+0

這是......不容易。你有什麼嘗試? – zellio

+0

-1太一般了。你不懂如何編程,或者如何在Python中執行算法? – steabert

+0

作爲一個新的學習者,我已經把自己投入了深刻的目的。 我正在考慮在算法中使用'for'或'if'語句。所以在x的許多範圍之間。 – Helpless

回答

13

正如我理解你的問題,你想寫一些函數y = interpolate(x_values, y_values, x),這會給你y值在一些x?然後基本思想如下這些步驟:

  1. 查找值的x_values限定容納x的間隔的索引。例如,對於x=3與您的示例列表,含間隔將是[x1,x2]=[2.5,3.4],以及指數將是i1=1i2=2
  2. (y_values[i2]-y_values[i1])/(x_values[i2]-x_values[i1])(即dy/dx)計算在此時間間隔的斜率。
  3. x現在的值爲x1加上坡度乘以距離x1的距離。

您還需要決定是否xx_values區間外發生了什麼,無論它是一個錯誤,或者你可以插值「倒退」,假設坡度是一樣的第一/最後一個區間。

有幫助嗎,還是您需要更具體的建議?

+0

沒有那麼完美,非常感謝! – Helpless

+3

從自身中減去y_values [i2]是不對的。應該是'(y_values [i2] -y_values [i1])'? –

+3

@MartinBurch:幾年後,但是......謝謝,修好了! :) – carlpett

10

我想出了一個相當優雅的解決方案(恕我直言),所以我無法抗拒張貼:

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使整數除法(蟒蛇< = 2.7)不會踢並破壞的東西,如果x1,x2,y1y2都是一些iterval的整數。

__getitem__我走的事實,self.x_list以升序使用bisect_left來按順序排列的(非常)快速查找最大元素小於xself.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]對於x < 1將工作,就像從(2.5,4)到(1,2)的行已經擴展到負無窮大,而i[x]對於x == 1x > 6將引起IndexError。更好的辦法是在所有情況下引發IndexError,但這是留給讀者的一個練習。:)

+1

我會發現使用'__call__'而不是'__getitem__'通常是可取的,它通常是一個插值*函數*。 – Dave

23
import scipy.interpolate 
y_interp = scipy.interpolate.interp1d(x, y) 
print y_interp(5.0) 

scipy.interpolate.interp1d執行線性插值,可定製處理錯誤條件。

1

您的解決方案在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]) 
1

外推離端相反的,你可能會返回y_list的範圍。大部分時間你的申請表現良好,Interpolate[x]將在x_list。推斷出兩端的(可能是)線性影響可能會誤導您相信您的數據表現良好。

  • 返回非線性結果(通過x_listy_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]) 
    
+0

我會發現使用'__call__'而不是'__getitem__'通常是可取的,它通常是一個插值*函數*。 – Dave