I am needing to detrend flux time series data (light curves), but I'm running into a problem when the time series data doesn't have a simple linear trend.
I've been using scipy.signal.detrend() for the detrending of linear cases, but that isn't sufficient here.
I've used numpy.polyfit() to attempt polynomial detrending, but I'm not sure what to do with the polynomial coefficients it returns.
Can someone advise me as to the next intelligent step to take? Or, if someone has a better method for detrending non-linear data, I'd be delighted to hear that as well.
Perhaps the simplest method to detrend a time series is by differencing. Specifically, a new series is constructed where the value at the current time step is calculated as the difference between the original observation and the observation at the previous time step.
To eliminate the nonlinear trend, fit a low-order polynomial to the signal and subtract it. In this case, the polynomial is of order 6. Plot the two new signals. The trends have been effectively removed.
To detrend linear data, remove the differences from the regression line. You must know the underlying structure of the trend in order to detrend it. For example, if you have a simple linear trend for the mean, calculate the least squares regression line to estimate the growth rate, r.
If the trend is deterministic (e.g. a linear trend) you could run a regression of the data on the deterministic trend (e.g. a constant plus time index) to estimate the trend and remove it from the data. If the trend is stochastic you should detrend the series by taking first differences on it.
In a nutshell, you take the coefficients that polyfit
returns and pass them to polyval
to evaluate the polynomial at the observed "x" locations.
As a stand-alone example, let's say we have something similar to the following:
import numpy as np
import matplotlib.pyplot as plt
num = 1000
x = np.linspace(0, 10, num)
y = np.exp(x)
# Add some non-stationary noise that's hard to see without de-trending
noise = 100 * np.exp(0.2 * x) * np.random.normal(0, 1, num)
y += noise
fig, ax = plt.subplots()
ax.plot(x, y, 'ro')
plt.show()
Note that I haven't used a polynomial function here to create y
. That's deliberate. Otherwise, we'd get an exact fit and wouldn't need to "play around" with the order of the polynomial.
Now let's try detrending it with a 2nd order polynomial function (note the 2 in the line model = np.polyfit(x, y, 2)
):
import numpy as np
import matplotlib.pyplot as plt
num = 1000
x = np.linspace(0, 10, num)
y = np.exp(x)
# Add some non-stationary noise that's hard to see without de-trending
noise = 100 * np.exp(0.2 * x) * np.random.normal(0, 1, num)
y += noise
# Detrend with a 2d order polynomial
model = np.polyfit(x, y, 2)
predicted = np.polyval(model, x)
fig, axes = plt.subplots(nrows=2, sharex=True)
axes[0].plot(x, y, 'ro')
axes[0].plot(x, predicted, 'k-')
axes[0].set(title='Original Data and 2nd Order Polynomial Trend')
axes[1].plot(x, y - predicted, 'ro')
axes[1].set(title='Detrended Residual')
plt.show()
Notice that we didn't fit the data exactly. It's an exponential function and we're using a polynomial. However, as we increase the order of the polynomial, we'll fit the function more precisely (at the risk of starting to fit noise):
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With