This example shows how to use the csaps
and spaps
commands from Curve Fitting Toolbox™ to construct cubic smoothing splines.
The command csaps
provides the smoothing spline. This is a cubic spline that more or less follows the presumed underlying trend in noisy data. A smoothing parameter, to be chosen by you, determines just how closely the smoothing spline follows the given data. Here is the basic information, an abbreviated version of the documentation:
CSAPS Cubic smoothing spline.
VALUES = CSAPS(X, Y, P, XX)
Returns the values at XX of the cubic smoothing spline for the
given data (X,Y) and depending on the smoothing parameter P, chosen
from the interval [0 .. 1]. This smoothing spline f minimizes
P * sum_i W(i)(Y(i) - f(X(i)))^2 + (1-P) * integral (D^2 f)^2
Here are some trial runs. We start with data from a simple cubic, q(x) := x^3
, contaminate the values with some noise, and choose the value of the smoothing parameter to be .5. Then plot the resulting smoothed values, along with the underlying cubic, and the contaminated data.
xi = (0:.05:1); q = @(x) x.^3; yi = q(xi); randomStream = RandStream.create( 'mcg16807', 'Seed', 23 ); ybad = yi+.3*(rand(randomStream, size(xi))-.5); p = .5; xxi = (0:100)/100; ys = csaps(xi,ybad,p,xxi); plot(xi,yi,':',xi,ybad,'x',xxi,ys,'r-') title('Clean Data, Noisy Data, Smoothed Values') legend( 'Exact', 'Noisy', 'Smoothed', 'Location', 'NorthWest' )
The smoothing is way overdone here. By choosing the smoothing parameter p
closer to 1, we obtain a smoothing spline closer to the given data. We try p = .6, .7, .8, .9, 1
, and plot the resulting smoothing splines.
yy = zeros(5,length(xxi)); p = [.6 .7 .8 .9 1]; for j=1:5 yy(j,:) = csaps(xi,ybad,p(j),xxi); end hold on plot(xxi,yy); hold off title('Smoothing Splines for Various Values of the Smoothing Parameter') legend({'Exact','Noisy','p = 0.5','p = 0.6','p = 0.7','p = 0.8', ... 'p = 0.9', 'p = 1.0'}, 'Location', 'NorthWest' )
We see that the smoothing spline can be very sensitive to the choice of the smoothing parameter. Even for p
= 0.9, the smoothing spline is still far from the underlying trend, while for p
= 1, we get the interpolant to the (noisy) data.
In fact, the formulation used by csapi
(p.235ff of A Practical Guide to Splines) is very sensitive to scaling of the independent variable. A simple analysis of the equations used shows that the sensitive range for p
is around 1/(1+epsilon)
, with epsilon := h^3/16
, and h
the average difference between neighboring sites. Specifically, you would expect a close following of the data when p = 1/(1+epsilon/100)
and some satisfactory smoothing when p = 1/(1+epsilon*100)
.
The plot below shows the smoothing spline for values of p
near this magic number 1/(1+epsilon)
. For this case, it is more informative to look at 1-p
since the magic number, 1/(1+epsilon)
, is very close to 1.
epsilon = ((xi(end)-xi(1))/(numel(xi)-1))^3/16; 1 - 1/(1+epsilon)
ans = 7.8124e-06
plot(xi,yi,':',xi,ybad,'x') hold on labels = cell(1,5); for j=1:5 p = 1/(1+epsilon*10^(j-3)); yy(j,:) = csaps(xi,ybad,p,xxi); labels{j} = ['1-p= ',num2str(1-p)]; end plot(xxi,yy) title('Smoothing Splines for Smoothing Parameter Near Its ''Magic'' Value') legend( [{'Exact', 'Noisy'}, labels], 'Location', 'NorthWest' ) hold off
In this example, the smoothing spline is very sensitive to variation of the smoothing parameter near the magic number. The one farthest from 1 seems the best choice from these, but you may prefer the one beyond that.
p = 1/(1+epsilon*10^3); yy = csaps(xi,ybad,p,xxi); hold on plot( xxi, yy, 'y', 'LineWidth', 2 ) title( sprintf( 'The Smoothing Spline For 1-p = %s is Added, in Yellow', num2str(1-p) ) ) hold off
You can also supply csaps
with error weights, to pay more attention to some data points than others. Also, if you do not supply the evaluation sites xx
, then csaps
returns the ppform of the smoothing spline.
Finally, csaps
can also handle vector-valued data and even multivariate, gridded data.
The cubic smoothing spline provided by the command spaps
differs from the one constructed in csaps
only in the way it is selected. Here is an abbreviated version of the documentation for spaps
:
SPAPS Smoothing spline.
[SP,VALUES] = SPAPS(X,Y,TOL) returns the B-form and, if asked,
the values at X, of a cubic smoothing spline f for the given
data (X(i),Y(:,i)), i=1,2, ..., n.
The smoothing spline f minimizes the roughness measure
F(D^2 f) := integral ( D^2 f(t) )^2 dt on X(1) < t < X(n)
over all functions f for which the error measure
E(f) := sum_j { W(j)*( Y(:,j) - f(X(j)) )^2 : j=1,...,n }
is no bigger than the given TOL. Here, D^M f denotes the M-th
derivative of f. The weights W are chosen so that E(f) is the
Composite Trapezoid Rule approximation for F(y-f).
f is constructed as the unique minimizer of
rho*E(f) + F(D^2 f),
with the smoothing parameter RHO so chosen so that E(f) equals
TOL. Hence, FN2FM(SP,'pp') should be (up to roundoff) the same
as the output from CPAPS(X,Y,RHO/(1+RHO)).
It may be easier to supply a suitable tolerance for spaps
than the smoothing parameter p
required by csaps
. In our earlier example, we added uniformly-distributed random noise from the interval 0.3*[-0.5 .. 0.5]
. Hence, we can estimate a reasonable value for tol
as the value of the error measure e
at such noise.
tol = sum((.3*(rand(randomStream, size(yi))-.5)).^2);
This plot shows the resulting smoothing spline constructed by spaps
. Note that the error weights are specified to be uniform, which is their default value in csaps
.
[sp,ys,rho] = spaps(xi,ybad,tol,ones(size(xi))); plot(xi,yi,':',xi,ybad,'x',xi,ys,'r-') title( sprintf( 'Clean Data, Noisy Data, Smoothed Values (1-p = %s )', num2str(1/(1+rho)) ) ); legend( {'Exact','Noisy','Smoothed'}, 'location', 'NorthWest' )
The figure title shows the value of p
you would use in csaps
to obtain exactly this smoothing spline for these data.
Here, in addition, is the smoothing spline provided by csaps
when not given a smoothing parameter. In this case csaps
chooses the parameter by a certain ad hoc procedure that attempts to locate the region where the smoothing spline is most sensitive to the smoothing parameter (similar to the earlier discussion).
hold on plot(xxi,fnval(csaps(xi,ybad),xxi),'-') title('Clean Data, Noisy Data, Smoothed Values') legend({'Exact' 'Noisy' 'spaps, specified tolerance' ... 'csaps, default smoothing parameter'}, 'Location', 'NorthWest' ) hold off
The csaps
and spaps
commands differ in the way in which you specify a particular smoothing spline, via a smoothing parameter vs. a tolerance. Another difference is that spaps
can provide a linear or a quintic smoothing spline, in addition to the cubic smoothing spline.
The quintic smoothing spline is better than the cubic smoothing spline in the situation when you would like the second derivative to move as little as possible.