Periodic SuperSmoother

One feature of the SuperSmoother is its ability to handle periodic signals. We'll demonstrate this here:


In [1]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

# Use seaborn for plotting defaults.
# This can be safely commented-out if seaborn is not installed
import seaborn; seaborn.set()

In [2]:
# Create a periodic function with interesting structure

def func(t, period):
    phase = t / period
    modphase = phase + 0.1 * np.sin(2 * np.pi * phase)
    return np.sin(2 * np.pi * modphase)

In [3]:
rng = np.random.RandomState(42)
t = 50 * rng.rand(100)
period = 10
dy = 0.1
y = func(t, period) + dy * rng.randn(len(t))

phase = t % period

In [4]:
def plot_data():
    fig, ax = plt.subplots(2)
    fig.subplots_adjust(hspace=0.25)

    # Plot the data first
    ax[0].errorbar(t, y, dy, fmt='.')
    ax[0].set_xlabel('t')

    # Plot the phased data below
    ax[1].errorbar(phase, y, dy, fmt='.')
    ax[1].set_xlabel('phase');
    return ax

plot_data();


Let's take a look at how supersmoother handles this. Using the standard algorithm to smooth each plot, we can do the following:


In [5]:
from supersmoother import SuperSmoother

phase_fit = np.linspace(0, period, 1000)
t_fit = np.linspace(0, 50, 1000)

model = SuperSmoother()
y_t = model.fit(t, y, dy).predict(t_fit)
y_phase = model.fit(phase, y, dy).predict(phase_fit)

ax = plot_data()
ax[0].plot(t_fit, y_t, '-k', lw=1)
ax[1].plot(phase_fit, y_phase, '-k', lw=1);


Notice there are some issues here. In the upper plot, the data: the data is sparse in some locations, which makes for a poor model. Also, at the edges there is some extrapolation which does not look good.

In the bottom, phased plot, things look a bit better. But there is a discontinuity between the left and right edges because the algorithm is not considering points across the boundary.

We can improve on these by passing a period argument, which enforces periodicity of the smoothed model:


In [6]:
model = SuperSmoother(period=period)

y_t = model.fit(t, y, dy).predict(t_fit)
y_phase = model.fit(phase, y, dy).predict(phase_fit)

ax = plot_data()
ax[0].plot(t_fit, y_t, '-k', lw=1)
ax[1].plot(phase_fit, y_phase, '-k', lw=1);