39

假设我得到如下数据:

x = [1, 2.5, 3.4, 5.8, 6]
y = [2, 4, 5.8, 4.3, 4]

我想设计一个函数,该函数将使用 Python在1and 2.52.5to等之间进行线性插值。3.4

我试过浏览这个 Python 教程,但我仍然无法理解它。

4

7 回答 7

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

scipy.interpolate.interp1d进行线性插值,并且可以定制以处理错误情况。

于 2012-12-03T17:44:05.557 回答
31

据我了解你的问题,你想写一些函数y = interpolate(x_values, y_values, x),这会给你y一些价值x?基本思想遵循以下步骤:

  1. x_values查找定义包含 的区间的值的索引x。例如,对于x=3您的示例列表,包含间隔将是[x1,x2]=[2.5,3.4],并且索引将是i1=1,i2=2
  2. (y_values[i2]-y_values[i1])/(x_values[i2]-x_values[i1])通过(ie dy/dx)计算此区间的斜率。
  3. 处的值x现在是处处的值x1加上斜率乘以距 的距离x1

如果斜率与第一个/最后一个间隔相同,您还需要确定如果x在 的区间之外会发生什么,或者这是一个错误,或者您可以“向后”插值。x_values

这有帮助吗,还是您需要更具体的建议?

于 2011-09-08T06:11:21.630 回答
18

我想出了一个相当优雅的解决方案(恕我直言),所以我忍不住发布它:

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])

我映射到整数除法(python <= 2.7)如果,和都是某个迭代的整数,则float不会启动并破坏事物。x1x2y1y2

__getitem__我利用 self.x_list 通过使用bisect_leftto (非常)快速找到小于xin的最大元素的索引以升序排序的事实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 == 1orx > 6将引发IndexError. 最好在所有情况下都提出 IndexError ,但这留给读者作为练习。:)

于 2011-09-08T09:10:56.573 回答
8

基于 Lauritz 的回答,这是一个具有以下更改的版本

  • 更新到 python3(地图给我带来了问题,没有必要)
  • 修复了边缘值的行为
  • 当 x 超出范围时引发异常
  • 使用__call__代替__getitem__
from bisect import bisect_right

class Interpolate:
    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!")
        self.x_list = x_list
        self.y_list = 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 __call__(self, x):
        if not (self.x_list[0] <= x <= self.x_list[-1]):
            raise ValueError("x out of bounds!")
        if x == self.x_list[-1]:
            return self.y_list[-1]
        i = bisect_right(self.x_list, x) - 1
        return self.y_list[i] + self.slopes[i] * (x - self.x_list[i])

示例用法:

>>> interp = Interpolate([1, 2.5, 3.4, 5.8, 6], [2, 4, 5.8, 4.3, 4])
>>> interp(4)
5.425
于 2019-05-21T07:53:04.927 回答
7
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)
于 2020-06-10T23:35:47.877 回答
4

您可以返回y_list. 大多数情况下,您的应用程序表现良好,并且Interpolate[x]将在x_list. 从末端推断的(大概)线性影响可能会误导您相信您的数据表现良好。

  • 返回一个非线性结果(由 和 的内容限制x_listy_list您的程序的行为可能会提醒您注意在 . 之外的值的问题x_list。(当给定非线性输入时,线性行为会变得很糟糕!)

  • 返回y_listfor Interpolate[x]outside ofx_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])
    
于 2014-04-24T13:16:57.463 回答
0

您的解决方案在 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])
于 2013-06-14T18:57:52.020 回答