49

Say I am given data as follows:

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

I want to design a function that will interpolate linearly between1 and2.5,2.5 to3.4, and so on using Python.

I have tried looking throughthis Python tutorial, but I am still unable to get my head around it.

double-beep's user avatar
double-beep
5,67019 gold badges43 silver badges50 bronze badges
askedSep 8, 2011 at 5:58
Helpless's user avatar
3

7 Answers7

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

scipy.interpolate.interp1d does linear interpolation by and can be customized to handle error conditions.

answeredDec 3, 2012 at 17:44
Dave's user avatar
Sign up to request clarification or add additional context in comments.

3 Comments

The question asks how to implement the function, not what library provides it.
And the answer to such question then - you don't. There are wheels you don't reinvent for a very good reason - if you seek first account understanding of them you go to the source, i.e. math book and try them out using best tools you have. You don't seek a painting lesson in a brush shop.
fyi link is broken
38

As I understand your question, you want to write some functiony = interpolate(x_values, y_values, x), which will give you they value at somex? The basic idea then follows these steps:

  1. Find the indices of the values inx_values which define an interval containingx. For instance, forx=3 with your example lists, the containing interval would be[x1,x2]=[2.5,3.4], and the indices would bei1=1,i2=2
  2. Calculate the slope on this interval by(y_values[i2]-y_values[i1])/(x_values[i2]-x_values[i1]) (iedy/dx).
  3. The value atx is now the value atx1 plus the slope multiplied by the distance fromx1.

You will additionally need to decide what happens ifx is outside the interval ofx_values, either it's an error, or you could interpolate "backwards", assuming the slope is the same as the first/last interval.

Did this help, or did you need more specific advice?

answeredSep 8, 2011 at 6:11
carlpett's user avatar

Comments

33
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)
answeredJun 10, 2020 at 23:35
jlady's user avatar

1 Comment

What a shame. This is thelerp function everyone who searches for wants, in pure Python, even has a docstring—a perfect answer. And I had to scroll to see it.
21

I thought up a rather elegant solution (IMHO), so I can't resist posting it:

from bisect import bisect_leftclass 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])

I map tofloat so that integer division (python <= 2.7) won't kick in and ruin things ifx1,x2,y1 andy2 are all integers for some iterval.

In__getitem__ I'm taking advantage of the fact that self.x_list is sorted in ascending order by usingbisect_left to (very) quickly find the index of the largest element smaller thanx inself.x_list.

Use the class like this:

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've not dealt with the border conditions at all here, for simplicity. As it is,i[x] forx < 1 will work as if the line from (2.5, 4) to (1, 2) had been extended to minus infinity, whilei[x] forx == 1 orx > 6 will raise anIndexError. Better would be to raise an IndexError in all cases, but this is left as an exercise for the reader. :)

answeredSep 8, 2011 at 9:10
Lauritz V. Thaulow's user avatar

4 Comments

I'd find using__call__ instead of__getitem__ to be preferrable in general, its usually an interpolationfunction.
Does not work with Python3, as indexing a map is not supported anymore
inventing a wheel. What's needed is a analogue of C++ std::lerp
It seems a little over complicated. This can (I’m not saying it should) be written in one line, one expression even.
10

Building on Lauritz` answer, here's a version with the following changes

  • Updated to python3 (the map was causing problems for me and is unnecessary)
  • Fixed behavior at edge values
  • Raise exception when x is out of bounds
  • Use__call__ instead of__getitem__
from bisect import bisect_rightclass 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])

Example usage:

>>> interp = Interpolate([1, 2.5, 3.4, 5.8, 6], [2, 4, 5.8, 4.3, 4])>>> interp(4)5.425
answeredMay 21, 2019 at 7:53
Karl Bartel's user avatar

Comments

4

Instead of extrapolating off the ends, you could return the extents of they_list. Most of the time your application is well behaved, and theInterpolate[x] will be in thex_list. The (presumably) linear affects of extrapolating off the ends may mislead you to believe that your data is well behaved.

  • Returning a non-linear result (bounded by the contents ofx_list andy_list) your program's behavior may alert you to an issue for values greatly outsidex_list. (Linear behavior goes bananas when given non-linear inputs!)

  • Returning the extents of they_list forInterpolate[x] outside ofx_list also means you know the range of your output value. If you extrapolate based onx much, much less thanx_list[0] orx much, much greater thanx_list[-1], your return result could be outside of the range of values you expected.

    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])
answeredApr 24, 2014 at 13:16
robertberrington's user avatar

1 Comment

I'd find using__call__ instead of__getitem__ to be preferrable in general, its usually an interpolationfunction.
0

Your solution did not work in Python 2.7. There was an error while checking for the order of the x elements. I had to change to code to this to get it to work:

from bisect import bisect_leftclass 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])
answeredJun 14, 2013 at 18:57
Leo's user avatar

1 Comment

I get an error.... TypeError 'Interpolate' object is not callable ?? What is the solution?

Your Answer

Sign up orlog in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

By clicking “Post Your Answer”, you agree to ourterms of service and acknowledge you have read ourprivacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.