假设我得到如下数据:
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])
通过(ie dy/dx
)计算此区间的斜率。x
现在是处处的值x1
加上斜率乘以距 的距离x1
。如果斜率与第一个/最后一个间隔相同,您还需要确定如果x
在 的区间之外会发生什么,或者这是一个错误,或者您可以“向后”插值。x_values
这有帮助吗,还是您需要更具体的建议?
我想出了一个相当优雅的解决方案(恕我直言),所以我忍不住发布它:
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
不会启动并破坏事物。x1
x2
y1
y2
在__getitem__
我利用 self.x_list 通过使用bisect_left
to (非常)快速找到小于x
in的最大元素的索引以升序排序的事实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 ,但这留给读者作为练习。:)
基于 Lauritz 的回答,这是一个具有以下更改的版本
__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
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
for 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])
您的解决方案在 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])