Cubic spline interpolation with PyDynamic.uncertainty.interpolate.interp1d_unc

Interpolate a non-equidistant sine signal using cubic / bspline method with uncertainty propagation. Comparing the resulting uncertainties to a Monte-Carlo experiment yields good overlap.

[1]:
import numpy as np
import plotly.graph_objs as go
from PyDynamic.uncertainty.interpolate import interp1d_unc
from scipy.interpolate import interp1d

Create non-equidistant sine-signal

[2]:
n_nodes = 10
t = np.cumsum(range(1, n_nodes))
x = np.sin(t)
ux = np.full_like(x, 0.2)
t, x, ux
[2]:
(array([ 1,  3,  6, 10, 15, 21, 28, 36, 45]),
 array([ 0.84147098,  0.14112001, -0.2794155 , -0.54402111,  0.65028784,
         0.83665564,  0.27090579, -0.99177885,  0.85090352]),
 array([0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2]))

Interpolate with PyDynamic

[3]:
ti = np.linspace(np.min(t), np.max(t), 60)
ti, xi, uxi = interp1d_unc(ti, t, x, ux, kind="cubic")
ti, xi, uxi
[3]:
(array([ 1.        ,  1.74576271,  2.49152542,  3.23728814,  3.98305085,
         4.72881356,  5.47457627,  6.22033898,  6.96610169,  7.71186441,
         8.45762712,  9.20338983,  9.94915254, 10.69491525, 11.44067797,
        12.18644068, 12.93220339, 13.6779661 , 14.42372881, 15.16949153,
        15.91525424, 16.66101695, 17.40677966, 18.15254237, 18.89830508,
        19.6440678 , 20.38983051, 21.13559322, 21.88135593, 22.62711864,
        23.37288136, 24.11864407, 24.86440678, 25.61016949, 26.3559322 ,
        27.10169492, 27.84745763, 28.59322034, 29.33898305, 30.08474576,
        30.83050847, 31.57627119, 32.3220339 , 33.06779661, 33.81355932,
        34.55932203, 35.30508475, 36.05084746, 36.79661017, 37.54237288,
        38.28813559, 39.03389831, 39.77966102, 40.52542373, 41.27118644,
        42.01694915, 42.76271186, 43.50847458, 44.25423729, 45.        ]),
 array([ 0.84147098,  0.5134559 ,  0.26887907,  0.09049061, -0.03895932,
        -0.13672062, -0.22004314, -0.30603185, -0.40015754, -0.48792672,
        -0.55288113, -0.57856251, -0.54851261, -0.45094022, -0.29697194,
        -0.10475754,  0.10755139,  0.32180327,  0.51984651,  0.68357697,
         0.80217466,  0.87988176,  0.92280604,  0.93705524,  0.92873714,
         0.90395947,  0.86883   ,  0.82944871,  0.78981465,  0.74899325,
         0.70534253,  0.65722049,  0.60298513,  0.54099446,  0.4696065 ,
         0.38717924,  0.29207069,  0.18306475,  0.06213925, -0.0672781 ,
        -0.20175235, -0.33784857, -0.4721318 , -0.6011671 , -0.72151951,
        -0.8297541 , -0.92243592, -0.99613001, -1.04740144, -1.07281526,
        -1.06893652, -1.03233027, -0.95956157, -0.84719547, -0.69179702,
        -0.48993128, -0.23816331,  0.06694186,  0.42881915,  0.85090352]),
 array([0.2       , 0.16837861, 0.19741966, 0.1962444 , 0.17596306,
        0.16866971, 0.18622166, 0.20285793, 0.19817887, 0.1825655 ,
        0.17473489, 0.18389472, 0.1992225 , 0.20369264, 0.19571168,
        0.18332587, 0.17637735, 0.1804066 , 0.19195061, 0.20145338,
        0.20182147, 0.19446415, 0.18421328, 0.1767848 , 0.17639785,
        0.18326204, 0.19338735, 0.20091735, 0.20172299, 0.19643529,
        0.18769373, 0.17895702, 0.17368016, 0.17410604, 0.1801646 ,
        0.1894922 , 0.1985253 , 0.20383972, 0.20425977, 0.20029238,
        0.1931453 , 0.18453319, 0.17654847, 0.17142144, 0.17103588,
        0.17629981, 0.18680117, 0.20105082, 0.21705456, 0.23277969,
        0.24637648, 0.25624082, 0.26102733, 0.25968413, 0.25156834,
        0.23674281, 0.21668996, 0.19594729, 0.18494793, 0.2       ]))

Interpolate with Monte Carlo

[4]:
X_mc = []
for i in range(2000):
    interp_x = interp1d(t, x + ux * np.random.randn(len(x)), kind="cubic")
    xm = interp_x(ti)
    X_mc.append(xm)
x_mc = np.mean(X_mc, axis=0)
ux_mc = np.std(X_mc, axis=0);

Compare results

Visual

[5]:
# NBVAL_IGNORE_OUTPUT
# interpolated signal
fig = go.Figure(
    [
        go.Scatter(
            name="PyDynamic interpolation",
            x=ti,
            y=xi,
            mode="lines",
            marker_color="rgba(168, 68, 68, 0.8)",
        ),
        go.Scatter(
            hoverinfo="x",
            x=ti,
            y=xi + uxi,
            mode="lines",
            line=dict(width=0),
            showlegend=False,
        ),
        go.Scatter(
            hoverinfo="x",
            x=ti,
            y=xi - uxi,
            line=dict(width=0),
            mode="lines",
            fillcolor="rgba(168, 68, 68, 0.3)",
            fill="tonexty",
            showlegend=False,
        ),
        go.Scatter(
            name="Monte Carlo interpolation",
            x=ti,
            y=x_mc,
            mode="lines",
            marker_color="rgba(68, 68, 168, 0.8)",
        ),
        go.Scatter(
            hoverinfo="x",
            x=ti,
            y=x_mc + ux_mc,
            mode="lines",
            line=dict(width=0),
            showlegend=False,
        ),
        go.Scatter(
            hoverinfo="x",
            x=ti,
            y=x_mc - ux_mc,
            line=dict(width=0),
            mode="lines",
            fillcolor="rgba(68, 68, 168, 0.3)",
            fill="tonexty",
            showlegend=False,
        ),
        go.Scatter(
            name="original",
            x=t,
            y=x,
            mode="lines",
            marker_color="rgba(68, 168, 68, 0.8)",
        ),
        go.Scatter(
            hoverinfo="x",
            x=t,
            y=x + ux,
            mode="lines",
            line=dict(width=0),
            showlegend=False,
        ),
        go.Scatter(
            hoverinfo="x",
            x=t,
            y=x - ux,
            line=dict(width=0),
            mode="lines",
            fillcolor="rgba(68, 168, 68, 0.3)",
            fill="tonexty",
            showlegend=False,
        ),
    ]
)
fig.update_layout(
    yaxis_title="Amplitude (a.u.)",
    xaxis_title="Time (a.u.)",
    title="Comparison between MonteCarlo simulation and PyDynamic interpolation",
    hovermode="x",
)
fig.show()

Numerical

[6]:
tolerance = 1e-1
[7]:
np.allclose(xi, x_mc, atol=tolerance, rtol=tolerance)
[7]:
True
[8]:
np.allclose(uxi, ux_mc, atol=tolerance, rtol=tolerance)
[8]:
True