Source code for neurokit2.stats.fit_loess
# -*- coding: utf-8 -*-
import numpy as np
import scipy.linalg
[docs]
def fit_loess(y, X=None, alpha=0.75, order=2):
"""**Local Polynomial Regression (LOESS)**
Performs a LOWESS (LOcally WEighted Scatter-plot Smoother) regression.
Parameters
----------
y : Union[list, np.array, pd.Series]
The response variable (the y axis).
X : Union[list, np.array, pd.Series]
Explanatory variable (the x axis). If ``None``, will treat y as a continuous signal (useful
for smoothing).
alpha : float
The parameter which controls the degree of smoothing, which corresponds to the proportion
of the samples to include in local regression.
order : int
Degree of the polynomial to fit. Can be 1 or 2 (default).
Returns
-------
array
Prediction of the LOESS algorithm.
dict
Dictionary containing additional information such as the parameters (``order`` and ``alpha``).
See Also
----------
signal_smooth, signal_detrend, fit_error
Examples
---------
.. ipython:: python
import pandas as pd
import neurokit2 as nk
# Simulate Signal
signal = np.cos(np.linspace(start=0, stop=10, num=1000))
# Add noise to signal
distorted = nk.signal_distort(signal,
noise_amplitude=[0.3, 0.2, 0.1],
noise_frequency=[5, 10, 50])
# Smooth signal using local regression
@savefig p_fit_loess1.png scale=100%
pd.DataFrame({ "Raw": distorted, "Loess_1": nk.fit_loess(distorted, order=1)[0],
"Loess_2": nk.fit_loess(distorted, order=2)[0]}).plot()
@suppress
plt.close()
References
----------
* https://simplyor.netlify.com/loess-from-scratch-in-python-animation.en-us/
"""
if X is None:
X = np.linspace(0, 100, len(y))
assert order in [1, 2], "Deg has to be 1 or 2"
assert 0 < alpha <= 1, "Alpha has to be between 0 and 1"
assert len(X) == len(y), "Length of X and y are different"
X_domain = X
n = len(X)
span = int(np.ceil(alpha * n))
y_predicted = np.zeros(len(X_domain))
x_space = np.zeros_like(X_domain)
for i, val in enumerate(X_domain):
distance = abs(X - val)
sorted_dist = np.sort(distance)
ind = np.argsort(distance)
Nx = X[ind[:span]]
Ny = y[ind[:span]]
delx0 = sorted_dist[span - 1]
u = distance[ind[:span]] / delx0
w = (1 - u**3) ** 3
W = np.diag(w)
A = np.vander(Nx, N=1 + order)
V = np.matmul(np.matmul(A.T, W), A)
Y = np.matmul(np.matmul(A.T, W), Ny)
Q, R = scipy.linalg.qr(V)
p = scipy.linalg.solve_triangular(R, np.matmul(Q.T, Y))
y_predicted[i] = np.polyval(p, val)
x_space[i] = val
return y_predicted, {"alpha": alpha, "order": order}