如何实现线性插值?

Hel*_*ess 27 python interpolation linear-interpolation

我对编程很新,并且认为我会尝试编写线性插值函数.

说我给出的数据如下:

x = [1, 2.5, 3.4, 5.8, 6]
y = [2, 4, 5.8, 4.3, 4]
Run Code Online (Sandbox Code Playgroud)

我想设计一个函数,它将使用Python在1和2.5之间,2.5到3.4之间线性插值,依此类推.

我试过通过Python教程,但我仍然无法理解它.

Dav*_*ave 38

import scipy.interpolate
y_interp = scipy.interpolate.interp1d(x, y)
print y_interp(5.0)
Run Code Online (Sandbox Code Playgroud)

scipy.interpolate.interp1d 进行线性插值并可以自定义以处理错误情况.

  • 问题问的是如何实现该功能,而不是哪个库提供了该功能。 (8认同)

car*_*ett 17

正如我理解你的问题,你想写一些函数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])(即dy/dx)计算此间隔的斜率.
  3. 处的值x现在是在值x1加上斜率乘以从距离x1.

假设斜率与第一个/最后一个间隔相同,您还需要确定x在超出间隔的情况下会发生什么x_values,或者它是错误,或者您可以"向后"插值.

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


Lau*_*low 14

我想到了一个相当优雅的解决方案(恕我直言),所以我无法抗拒发布它:

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])
Run Code Online (Sandbox Code Playgroud)

我映射到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]
Run Code Online (Sandbox Code Playgroud)

为简单起见,我根本没有处理边境条件.因为它是,i[x]对于x < 1将作为如果线路工作从(2.5,4)至(1,2)已扩大到负无穷大,而i[x]用于x == 1x > 6将引发IndexError.更好的是在所有情况下都会引发IndexError,但这仍然是读者的练习.:)

  • 我发现使用`__call__`而不是`__getitem__`一般都是首选,它通常是插值*函数*. (5认同)

Kar*_*tel 7

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

  • 更新到 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])
Run Code Online (Sandbox Code Playgroud)

用法示例:

>>> interp = Interpolate([1, 2.5, 3.4, 5.8, 6], [2, 4, 5.8, 4.3, 4])
>>> interp(4)
5.425
Run Code Online (Sandbox Code Playgroud)


小智 5

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)
Run Code Online (Sandbox Code Playgroud)

  • 真丢脸。这是每个搜索者都想要的“lerp”函数,在纯 Python 中,甚至有一个文档字符串——一个完美的答案。我必须滚动才能看到它。 (5认同)