Я придумал довольно элегантное решение (ИМХО), поэтому не могу удержаться от публикации:
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
, так что целочисленное деление (python <= 2.7) выиграло 'не пускать и разрушать вещи, если <code>x1, x2
, y1
и y2
- все целые числа для некоторого итерала.
В __getitem__
Я пользуюсь тем фактом, что self.x_list сортируется в порядке возрастания с помощью bisect_left
, чтобы (очень) быстро найти индекс самого большого элемента, меньшего x
в 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]
для x < 1
будет работать так, как если бы линия от (2.5, 4) до (1, 2) была расширена до минус бесконечности, в то время как i[x]
для x == 1
или x > 6
повыситсяIndexError
.Лучше было бы вызвать IndexError во всех случаях, но это оставлено как упражнение для читателя.:)