Natasha
Natasha

Reputation: 1521

Interpolation function for single data point

I'm using the interp1d function for interpolation

from scipy.interpolate import interp1d
x = [0, 3, 6, 10, 15, 20]
y = [1.87, 1.76, 1.27, 1.185, 0.995, 0.855]
f = interp1d(x, y, bounds_error=False)

x_find = [0, 5, 8, 10, 28]
print(f(x_find))

Using bounds_error=False in f = interp1d(x, y, bounds_error=False) returns nan value for x=28 in x_find. Since interp1d raises an error for single datapoints, I tried the following for single datapoint.

x0 = [1.87]
y0 = [0.93]
f0 = lambda x: y0[0] if np.isclose(x, x0[0]) else np.NaN
print(f0(x0[0]))

This doesn't work when I try f0(x_find).

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Expected output: f0(x_find) returns nan for values of x in x_find not present in x0like how bounds_error works.

Suggestions on how to do this will be really helpful

EDIT:

Question:

Would it be possible to modify the interpolation function of the single datapoint so that we could do just f(x_find), something similar to the one returned by f = interp1d() ?

Upvotes: 0

Views: 648

Answers (1)

user7711283
user7711283

Reputation:

I just only guess that you are missing a very simple thing: to put a single value to the f0() function using a list comprehension to get all values in case of a list with values.

Does the following code:

import numpy as np
from scipy.interpolate import interp1d
x = [0, 3, 6, 10, 15, 20]
y = [1.87, 1.76, 1.27, 1.185, 0.995, 0.855]
f = interp1d(x, y, bounds_error=False)

x_find = [0, 5, 8, 10, 28]
print(f(x_find))
x0 = [1.87]
y0 = [0.93]
f0 = lambda x: y0[0] if np.isclose(x, x0[0]) else np.NaN
print(f0(x0[0]))

print([f0(x) for x in x_find])

which prints:

[1.87       1.43333333 1.2275     1.185             nan]
0.93
[nan, nan, nan, nan, nan]

meet your expectations?

You can also redefine f0 to cover the case of passing a list of values to it as follows:

def f0(x): 
    import numpy as np
    x0 = [1.87]
    y0 = [0.93]
    f = lambda x: y0[0] if np.isclose(x, x0[0]) else np.NaN
    if isinstance(x, list):
        return [f(z) for z in x]
    elif isinstance(x, float):
        return f(x)
    else:
        return "f0() accepts only float and lists of floats as parameter"
print('---')
print(f0(1.87))
print(f0(x_find))
print(f0("5"))

The output of the code above is:

---
0.93
[nan, nan, nan, nan, nan]
f0() accepts only float and lists of floats as parameter

FINALLY you can also redefine f0 as f_i which is a bit complex code simulating the behavior of scipy interp1d as follows:

def f_i(X=[0, 1.87], Y=[1.87, 0.93], bounds_error=False):
    # ToDo: implementation of bounds_error evaluation

    def f_interpolate(u):
        assert len(X) > 1
        XY = list(zip(X, Y))
        XY.sort()

        if not (XY[0][0] <= u <= XY[-1][0]): 
            return None
        
        x_new = u
        for i in range(len(XY)-1):
            if XY[i][0] <= u <= XY[i+1][0]:
                x_lo = XY[i  ][0]
                x_hi = XY[i+1][0]
                y_lo = XY[i  ][1]
                y_hi = XY[i+1][1]
                if x_new == x_lo:
                    return y_lo
                if x_new == x_hi:
                    return y_hi
                slope = (y_hi - y_lo) / (x_hi - x_lo)
                y_new =  y_lo + slope*(x_new - x_lo)
                return y_new
        return None

    def f(v):
        assert len(X) == 1
        if v == X[0]:
            return Y[0]
        else:
            return None

    def r_f(w): 
        f_l = f_interpolate if len(X) > 1 else f
        if isinstance(w, list):
            return [f_l(z) for z in w]
        elif isinstance(w, float):
            return f_l(w)
        else:
            return "ValueErrorMessage: param. not float or list of floats"
            
    return r_f
    
y = [1.87, 1.76, 1.27, 1.185, 0.995, 0.855]
x = [ 0,    3,    6,     10,    15,    20 ]

y0 = [0.93]
x0 = [1.87]

print('---')
f = f_i(x0, y0)
print(f(1.87))

f = f_i(x, y)
x_find = [0, 5, 8, 10, 28]
print(f(x_find))
print(f("5"))

which gives following output:

---
0.93
[1.87, 1.4333333333333333, 1.2275, 1.185, None]
ValueErrorMessage: param. not float or list of floats

Upvotes: 1

Related Questions