Как реализовать линейную интерполяцию? - PullRequest
25 голосов
/ 08 сентября 2011

Я довольно новичок в программировании и подумал, что попробую написать функцию линейной интерполяции.

Скажем, мне даны следующие данные:

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

Я хочу разработатьфункция, которая будет интерполировать линейно от 1 до 2,5, от 2,5 до 3,4 и т. д. с использованием Python.

Я попытался просмотреть Учебник по Python , но я все еще не могу разобраться с этим.

Ответы [ 6 ]

37 голосов
/ 03 декабря 2012
import scipy.interpolate
y_interp = scipy.interpolate.interp1d(x, y)
print y_interp(5.0)

scipy.interpolate.interp1d выполняет линейную интерполяцию и может быть настроена для обработки ошибок.

17 голосов
/ 08 сентября 2011

Как я понимаю ваш вопрос, вы хотите написать какую-то функцию 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, либо это ошибка, либо вы можете интерполировать «назад», предполагая, что наклон равен первому / последнему интервалу.

Это помогло, или вам нужен был более конкретный совет?

14 голосов
/ 08 сентября 2011

Я придумал довольно элегантное решение (ИМХО), поэтому не могу удержаться от публикации:

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 во всех случаях, но это оставлено как упражнение для читателя.:)

2 голосов
/ 24 апреля 2014

Вместо экстраполяции с концов, вы можете вернуть экстенты y_list.В большинстве случаев ваше приложение ведет себя хорошо, и Interpolate[x] будет в x_list.(Предположительно) линейные эффекты экстраполяции с концов могут ввести вас в заблуждение, полагая, что ваши данные хорошо себя ведут.и y_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])
1 голос
/ 14 июня 2013

Ваше решение не работает в Python 2.7.Произошла ошибка при проверке порядка элементов x.Я должен был изменить код на это, чтобы заставить его работать:

0 голосов
/ 21 мая 2019

Опираясь на ответ Лаурица, вот версия со следующими изменениями

  • Обновлено до 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...