Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Modify signal_interpolate for input with NaNs and extrapolation #666

Merged
26 changes: 26 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,32 @@ News
=====





0.2.1
-------------------
New Features
+++++++++++++

* Allow for input with NaNs and extrapolation in `signal_interpolate()`
* Add argument `method` in `find_outliers()`





0.2.0
-------------------
New Features
+++++++++++++

* Add new time-domain measures in `hrv_time()`: `Prc20NN`, `Prc80NN`, `MinNN`, and `MaxNN`





0.1.6
-------------------

Expand Down
42 changes: 27 additions & 15 deletions neurokit2/signal/signal_interpolate.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
import scipy.interpolate


def signal_interpolate(x_values, y_values, x_new=None, method="quadratic"):
def signal_interpolate(
x_values=None, y_values=None, x_new=None, method="quadratic", fill_value=None
):
"""**Interpolate a signal**

Interpolate a signal using different methods.

Parameters
----------
x_values : Union[list, np.array, pd.Series]
Expand All @@ -29,27 +29,28 @@ def signal_interpolate(x_values, y_values, x_new=None, method="quadratic"):
order of the spline interpolator to use.
See `here <https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.
PchipInterpolator.html>`_ for details on the ``"monotone_cubic"`` method.

fill_value : float or tuple or str
If a ndarray (or float), this value will be used to fill in for
requested points outside of the data range.
If a two-element tuple, then the first element is used as a fill value
for x_new < x[0] and the second element is used for x_new > x[-1].
If “extrapolate”, then points outside the data range will be extrapolated.
danibene marked this conversation as resolved.
Show resolved Hide resolved
If not provided, then the default is ([y_values[0]], [y_values[-1]]).
Returns
-------
array
Vector of interpolated samples.

Examples
--------
.. ipython:: python

import numpy as np
import neurokit2 as nk
import matplotlib.pyplot as plt

# Generate Simulated Signal
signal = nk.signal_simulate(duration=2, sampling_rate=10)

# We want to interpolate to 2000 samples
x_values = np.linspace(0, 2000, num=len(signal), endpoint=False)
x_new = np.linspace(0, 2000, num=2000, endpoint=False)

# Visualize all interpolation methods
@savefig p_signal_interpolate1.png scale=100%
nk.signal_plot([
Expand All @@ -65,9 +66,22 @@ def signal_interpolate(x_values, y_values, x_new=None, method="quadratic"):
plt.scatter(x_values, signal, label="original datapoints", zorder=3)
@suppress
plt.close()

"""
# Sanity checks
if x_values is None and y_values is None:
raise ValueError(
"NeuroKit error: signal_interpolate(): x_values or y_values must be provided."
)
elif x_values is None or y_values is None:
# for interpolating NaNs
if y_values is None:
y_values = x_values
x_values = np.arange(0, len(y_values))
if x_new is None:
x_new = x_values
y_finite = np.where(np.invert(np.isnan(y_values)))[0]
x_values = x_values[y_finite]
y_values = y_values[y_finite]
if len(x_values) != len(y_values):
raise ValueError("x_values and y_values must be of the same length.")

Expand All @@ -77,23 +91,22 @@ def signal_interpolate(x_values, y_values, x_new=None, method="quadratic"):
else:
if len(x_values) == len(x_new):
return y_values

if method == "monotone_cubic":
interpolation_function = scipy.interpolate.PchipInterpolator(
x_values, y_values, extrapolate=True
)
else:
if fill_value is None:
fill_value = ([y_values[0]], [y_values[-1]])
interpolation_function = scipy.interpolate.interp1d(
x_values,
y_values,
kind=method,
bounds_error=False,
fill_value=([y_values[0]], [y_values[-1]]),
fill_value=fill_value,
)

if isinstance(x_new, int):
x_new = np.linspace(x_values[0], x_values[-1], x_new)

interpolated = interpolation_function(x_new)

if method == "monotone_cubic":
Expand All @@ -102,5 +115,4 @@ def signal_interpolate(x_values, y_values, x_new=None, method="quadratic"):
# scipy.interpolate.interp1d with fill_value=([y_values[0]], [y_values[-1]].
interpolated[: int(x_values[0])] = interpolated[int(x_values[0])]
interpolated[int(x_values[-1]) :] = interpolated[int(x_values[-1])]

return interpolated