2011-09-08 50 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