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.
- en.wikipedia.org/wiki/Linear_least_squares_(mathematics)warvariuc– warvariuc2011-09-08 06:39:43 +00:00CommentedSep 8, 2011 at 6:39
- If you want to only interpolate between 2 points, you can use numpy:docs.scipy.org/doc/numpy/reference/generated/numpy.interp.html Otherwise, Dave's answer below is best.wordsforthewise– wordsforthewise2019-08-07 21:16:00 +00:00CommentedAug 7, 2019 at 21:16
- I found this:gist.github.com/laundmo/b224b1f4c8ef6ca5fe47e132c8deab56 It has lerp, inverse lerp and remapping. It’s pure python but it only works in one dimension. The code isn’t too complex though, you could probably add 2d lerping.DeepThought42– DeepThought422024-12-21 03:06:56 +00:00CommentedDec 21, 2024 at 3:06
7 Answers7
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.
3 Comments
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:
- Find the indices of the values in
x_valueswhich define an interval containingx. For instance, forx=3with your example lists, the containing interval would be[x1,x2]=[2.5,3.4], and the indices would bei1=1,i2=2 - Calculate the slope on this interval by
(y_values[i2]-y_values[i1])/(x_values[i2]-x_values[i1])(iedy/dx). - The value at
xis now the value atx1plus 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?
Comments
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)1 Comment
lerp function everyone who searches for wants, in pure Python, even has a docstring—a perfect answer. And I had to scroll to see it.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. :)
4 Comments
__call__ instead of__getitem__ to be preferrable in general, its usually an interpolationfunction.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.425Comments
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 of
x_listandy_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 the
y_listforInterpolate[x]outside ofx_listalso means you know the range of your output value. If you extrapolate based onxmuch, much less thanx_list[0]orxmuch, 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])
1 Comment
__call__ instead of__getitem__ to be preferrable in general, its usually an interpolationfunction.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])1 Comment
Explore related questions
See similar questions with these tags.


