Spline smoothing of motorcycle data (DK §8.5)#

Head acceleration against time in a simulated motorcycle accident (Silverman 1985): 133 observations, irregularly spaced, some at the same time. The cubic smoothing spline is the smoothed level of a continuous-time smooth trend plus an irregular (DK §3.8.2, §3.9.2), and its smoothing parameter \(\lambda = \sigma^2_\zeta / \sigma^2_\varepsilon\) is estimated by maximum likelihood.

Results#

quantity

DK

this library

\(\psi = \log\lambda\)

-3.59 (0.22)

-2.36 (0.44)

\(\lambda\)

0.0275

0.0945

AIC

9.43

9.42

The AIC agrees but \(\lambda\) does not. The profile log likelihood over \(\psi\) has a single maximum, at -2.36; at DK’s -3.59 it is 4.3 lower, which would give an AIC of 9.49. DK’s AIC is therefore that of this optimum, and their printed \(\lambda\) comes from a scaling we could not identify. The spline itself matches SciPy’s cubic smoothing spline in the tests.

Program#

!> DK 8.5: cubic smoothing spline for the motorcycle acceleration data (133
!> observations, irregularly spaced in time, some at the same time point),
!> as the continuous-time smooth trend model (DK 3.8.2, 3.9.2, eq. 8.1)
!> plus an irregular. The smoothness parameter lambda = sigma2_zeta /
!> sigma2_eps is estimated by maximum likelihood.
!>
!> DK report psi = log lambda = -3.59 (standard error 0.22), lambda =
!> 0.0275 with a 95% interval of 0.018 to 0.043, and AIC 9.43 (DK 7.4 with
!> diffuse initialization: n^-1 [-2 log L_d + 2 (q + w)], q = 2 diffuse
!> states, w = 2 parameters).
!>
!> Our AIC is 9.42, matching DK's 9.43, but our estimate is psi = -2.36
!> (se 0.44), lambda = 0.094. The profile log likelihood over psi has a
!> single maximum there; at DK's psi = -3.59 it is about 4.3 lower (-626.9),
!> which would give AIC 9.49. So DK's AIC is that of this optimum, and their
!> printed lambda seems to come from a different scaling that we couldn't
!> identify. The spline model itself is checked against SciPy's
!> smoothing spline in the tests.
!>
!> Run from the repo root, after `.venv/bin/python data/fetch_dk_data.py`:
!>     fpm run --example dk_8_5_spline
program dk_8_5_spline
  use statespace
  use csv_io, only: read_csv, column
  implicit none

  character(len=32), allocatable :: names(:)
  real(dp), allocatable :: data(:, :), times(:), y(:, :), g(:)
  type(component_holder_t) :: comps(2)
  type(structural_model_t) :: model
  type(fit_result_t) :: res
  type(fit_options_t) :: opts
  type(filter_result_t) :: fres
  type(smoother_result_t) :: sres
  real(dp) :: psi, se, aic
  integer :: info, n, t

  call read_csv("data/mcycle.csv", names, data)
  times = column(names, data, "times")
  n = size(times)
  allocate (y(1, n))
  y(1, :) = column(names, data, "accel")

  allocate (irregular_t :: comps(1)%c)
  comps(2)%c = continuous_trend_t(times=times)
  model = structural_model(y, comps, info)
  if (info /= SS_OK) error stop "model"
  opts%factr = 10.0_dp
  opts%pgtol = 1.0e-9_dp
  call fit(model, res, options=opts, info=info)
  if (info /= SS_OK) error stop "fit"

  ! psi = log sigma2_zeta - log sigma2_eps, its variance by the delta method
  psi = log(res%params(2) / res%params(1))
  g = [-1.0_dp / res%params(1), 1.0_dp / res%params(2)]
  se = sqrt(dot_product(g, matmul(res%cov_params, g)))
  aic = (-2 * res%llf + 2 * (model%rep%k_diffuse() + model%k_params)) / n
  print '(a, 2es12.4)', "sigma2_eps, sigma2_zeta: ", res%params
  print '(a, f8.3, a, f6.3, a)', "psi = log lambda: ", psi, "  (se ", se, ")"
  print '(a, f8.4, a, f6.3, a, f6.3)', "lambda: ", exp(psi), "  95% interval ", &
    exp(psi - 1.96_dp * se), " to ", exp(psi + 1.96_dp * se)
  print '(a, f8.3)', "AIC: ", aic

  ! The spline (the smoothed level) with 95% intervals, every 10th point
  call model%smooth(res%params, fres, sres, info)
  print '(/, a8, 4a10)', "time", "accel", "spline", "lower", "upper"
  do t = 1, n, 10
    print '(f8.1, 4f10.2)', times(t), y(1, t), sres%alphahat(1, t), &
      sres%alphahat(1, t) - 1.96_dp * sqrt(sres%V(1, 1, t)), &
      sres%alphahat(1, t) + 1.96_dp * sqrt(sres%V(1, 1, t))
  end do
end program dk_8_5_spline

Output#

sigma2_eps, sigma2_zeta:   5.0972E+02  4.8174E+01
psi = log lambda:   -2.359  (se  0.441)
lambda:   0.0945  95% interval  0.040 to  0.224
AIC:    9.421

    time     accel    spline     lower     upper
     2.4      0.00     -1.08    -26.06     23.89
     8.8     -1.30     -1.45    -16.19     13.28
    13.8      0.00     -8.08    -20.78      4.61
    15.4    -53.50    -32.36    -41.34    -23.38
    16.2    -61.70    -49.52    -58.33    -40.72
    17.6   -101.90    -80.01    -90.38    -69.65
    20.4   -117.90   -114.89   -128.30   -101.48
    24.6    -53.50    -77.48    -89.48    -65.47
    27.0    -16.00    -21.90    -33.47    -10.34
    30.2     36.20     31.03     15.66     46.41
    35.2    -16.00     20.76      7.77     33.75
    39.4     -1.30      4.19    -11.87     20.26
    44.4      0.00      2.35    -14.48     19.17
    55.0     10.70      1.41    -18.61     21.42