Home > Net >  Extrapolate NaN values in a numpy array
Extrapolate NaN values in a numpy array

Time:07-01

I have two values and know their index in an array full of nans. I want to interpolate / extrapolate all the nan's

import numpy as np
y = np.array([np.nan, np.nan, 0.75, np.nan, np.nan, np.nan, np.nan, np.nan, 2.25])

With the help of this answer I write the following:

nans, x = np.isnan(y), lambda z: z.nonzero()[0]
y[nans] = np.interp(x(nans), x(~nans), y[~nans])

My output looks as follows:

[0.75 0.75 0.75 1. 1.25 1.5 1.75 2. 2.25]

However, I would like it to be:

[0.25 0.5 0.75 1. 1.25 1.5 1.75 2. 2.25]

The increment is always a constant. When I read the documentation of np.iterp I see that I can specify the input-parameters left and right. If I don't specify left, the value to return for x < xp[0] is fp[0].

How can I specify left and right in order to get the desired output?

CodePudding user response:

np.interp doesn't seem to make extrapolation, but scipy.interpolate.interp1d does.

The arguments to give are a bit different, you can make it a function for your particular case:

import numpy as np
from scipy.interpolate import interp1d


def interp_nans(y, x=None):
    if x is None:
        x = np.arange(len(y))
    nans = np.isnan(y)

    interpolator = interp1d(
        x[~nans],
        y[~nans],
        kind="linear",
        fill_value="extrapolate",
        assume_sorted=True,
    )

    return interpolator(x)

Check that it works:

y = np.array([np.nan, np.nan, 0.75, np.nan, np.nan, np.nan, np.nan, np.nan, 2.25])
expected = np.array([0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25])

result = interp_nans(y)

assert np.array_equal(result, expected)

CodePudding user response:

You can't specify left and right to achieve extrapolation with interp, they are just constant values.

If you prefer a pure numpy solution, you can linearly extrapolate based on the first/last two values of the interpolated array:

def extrap(x, xp, fp):
    m = (fp[1] - fp[0]) / (xp[1] - xp[0])
    n = fp[0] - m * xp[0]
    result = m * x[x < xp[0]]   n
    m = (fp[-1] - fp[-2]) / (xp[-1] - xp[-2])
    n = fp[-1] - m * xp[-1]
    return np.concatenate([result, m * x[x > xp[-1]]   n])

(you may want to add verification of len(xp) > 1 and len(xp) == len(yp))

Example:

y = np.array([np.nan, np.nan, 0.75, np.nan, np.nan, np.nan, np.nan, np.nan, 2.25, np.nan])

nans, x = np.isnan(y), lambda z: z.nonzero()[0]
y[nans] = np.interp(x(nans), x(~nans), y[~nans], np.nan, np.nan)

nans, x = np.isnan(y), lambda z: z.nonzero()[0]
y[nans] = extrap(x(nans), x(~nans), y[~nans])

Result

array([0.25, 0.5 , 0.75, 1.  , 1.25, 1.5 , 1.75, 2.  , 2.25])
  • Related