# Joining and interpolating data points

I wonder what is the best practice for interpolating curves? Usually, I'm using BSplineCurve and adjusting SplineWeights so it would fit better (and assigning more weight around the sharp edges to drag the curve closer to it). Or if I can guess what formula describes the points, I use FindFit. But often, I can't guess the formula and adjusting weights is very tedious, so it's easier to just manually draw the curve along the points. So what is the best way to join points in Mathematica?

Consider these five sets of points describing five resonance curves for example:

 data = {{{100.434, 0.}, {102.461, 0.0909091}, {104.392, 0.318182}, {105.321, 0.545455},
{106.226, 1.}, {107.108, 0.545455}, {107.965, 0.318182}, {109.608, 0.136364},
{111.154, 0.0909091}},
{{100.434, 0.}, {102.461, 0.06}, {104.392, 0.22}, {105.321, 0.46}, {106.226, 1.},
{107.108, 0.4}, {107.965, 0.12}, {111.154, 0.02}, {113.958, 0.}},
{{100.434, 0.030303}, {102.461, 0.0505051}, {104.392, 0.0909091},
{105.321, 0.272727}, {105.867, 0.494949}, {106.226, 1.}, {106.582, 0.636364},
{107.108, 0.212121}, {107.965, 0.0505051}, {111.154, 0.}},
{{100.434, 0.}, {102.461, 0.0555556}, {104.392, 0.333333}, {105.321, 0.611111},
{105.867, 1.}, {106.226, 0.944444}, {106.405, 0.583333}, {106.582, 0.777778},
{106.933, 1.}, {107.965, 0.444444}, {109.608, 0.166667}, {111.154, 0.0555556}},
{{100.434, 0.0188679}, {102.461, 0.0566038}, {104.392, 0.}, {105.321, 0.54717},
{105.867, 0.849057}, {106.226, 1.}, {106.405, 0.433962}, {106.582, 0.886792},
{106.933, 0.924528}, {107.281, 0.660377}, {107.965, 0.320755},
{111.154, 0.0566038}}}

-
Are you aware of Interpolation[]? – VLC Nov 2 '12 at 16:53
@VLC It produces very not smooth curve. – swish Nov 2 '12 at 17:00
@VLC With Method -> "Spline" it produces quite smooth curve but still not like I would join it manually. Is there a better way? – swish Nov 2 '12 at 17:07
@PlatoManiac As I said it's five functions. – swish Nov 2 '12 at 17:16
@swish Show[ListLinePlot[#, InterpolationOrder -> 3, Method -> "Spline", PlotRange -> All, PlotStyle -> Hue[RandomReal[]], Frame -> True, Axes -> None] & /@ data, Epilog -> {Red, PointSize[Medium], Point[Flatten[data, 1]]}] how do you find the interpolation? I guess this is quite a nice interpolation as the functions are at least twice differentiable. – PlatoManiac Nov 2 '12 at 17:29

So, what is the best way to join points in Mathematica?

There is no one "best way" (not only in Mathematica, but in general); an interpolation scheme that behaves nicely for data set A might be a crapshoot when applied to data set B. It depends on the configuration of your points, and impositions you have on the interpolant (e.g. $C^1$/$C^2$ continuity, preservation of monotonicity/convexity, etc.), with these impositions not always being achievable all at the same time.

Having said this, if you're fine with a $C^1$ interpolant (interpolant and first derivative are continuous), one possibility is to use Akima interpolation. It is not always guaranteed to preserve shape (unless your points are specially configured), but at least for your data set, it does a decent job:

AkimaInterpolation[data_] := Module[{dy}, dy = #2/#1 & @@@ Differences[data];
Interpolation[Transpose[{List /@ data[[All, 1]], data[[All, -1]],
With[{wp = Abs[#4 - #3], wm = Abs[#2 - #1]},
If[wp + wm == 0, (#2 + #3)/2, (wp #2 + wm #3)/(wp + wm)]] & @@@
Partition[Join[{{3, -2}, {2, -1}}.Take[dy, 2], dy,
{{-1, 2}, {-2, 3}}.Take[dy, -2]], 4, 1]}],
InterpolationOrder -> 3, Method -> "Hermite"]]

MapIndexed[(h[#2[[1]]] = AkimaInterpolation[#1]) &, data];

Partition[Table[Plot[{h[k][u]}, {u, 100.434, 111.154}, Axes -> None,
Epilog -> {Directive[Red, AbsolutePointSize[4]], Point[data[[k]]]},
Frame -> True, PlotRange -> All],
{k, 5}], 2, 2, 1, SpanFromBoth] // GraphicsGrid


Note that in the fifth plot, the Akima interpolant has a slight wiggle before hitting the third point; this, as I said, is due to the fact that Akima's scheme does not guarantee that it will respect the monotonicity of the data. If you want something that fits a bit more snugly, one scheme you can use is Steffen interpolation, which is also a $C^1$ interpolation method:

SteffenEnds[h1_, h2_, d1_, d2_] :=
With[{p = d1 + h1 (d1 - d2)/(h1 + h2)}, (Sign[p] + Sign[d1]) Min[Abs[p]/2, Abs[d1]]]

SteffenInterpolation[data_?MatrixQ] := Module[{del, h, pp, xa, ya},
{xa, ya} = Transpose[data]; del = Differences[ya]/(h = Differences[xa]);
pp = MapThread[Reverse[#1].#2 &, Map[Partition[#, 2, 1] &, {h, del}]]/
ListConvolve[{1, 1}, h];
Interpolation[Transpose[{List /@ xa, ya,
Join[{SteffenEnds[h[[1]], h[[2]], del[[1]], del[[2]]]},
ListConvolve[{1, 1}, 2 UnitStep[del] - 1] *
{SteffenEnds[h[[-1]], h[[-2]], del[[-1]], del[[-2]]]}]}],
InterpolationOrder -> 3, Method -> "Hermite"]]

MapIndexed[(w[#2[[1]]] = SteffenInterpolation[#1]) &, data]

Partition[Table[Plot[{w[k][u]}, {u, 100.434, 111.154}, Axes -> None,
Epilog -> {Directive[Red, AbsolutePointSize[4]], Point[data[[k]]]},
Frame -> True, PlotRange -> All],
{k, 5}], 2, 2, 1, SpanFromBoth] // GraphicsGrid


Note that the interpolants from Steffen's method are a lot less wiggly, though the interpolant turns more sharply at its extrema. The advantage of using Steffen is that it is guaranteed to preserve the shape of the data, which might be more important than a smooth turn in some applications.

My point, now, is that sometimes, one must try a number of interpolation schemes to see what is most suitable for the data at hand, for which plotting the interpolant along with the data it is interpolating is paramount.

-
This is a fantastic answer! Thank you. – Gleno Sep 29 '13 at 14:28
Well, I did my best. :) i'll note that the "Akima interpolation" implemented here is his older method; he has a newer method, but I haven't gotten around to writing a Mathematica implementation. – J. M. May 2 '15 at 3:19
It's been almost two years now, and I have to thank you again - because this answer has been very influential on what I've been using (Akima, and Steffen interpolations mostly). I wrote some C code for it that my employer is using now. I'll check out this newer method as well, thanks. :) – Gleno May 2 '15 at 23:21
Here is a comparison of your Akima scheme and global B-spline interpolation with centripetal parametrization and SplineDegree->2 – Shutao Tang Jun 6 at 13:56
@Shutao, yes, I'm familiar with your framework. :) My point was that there are applications where $y=f(x)$ is needed, and the thing with B-splines is that they are of the form $x=f(t),\;y=g(t)$. – J. M. Jun 6 at 14:31

May not turn out to be a very general method but here I will adapt the Interpolation function of MMA in a way so that smoother result can be obtained for your specific data set.

## Interpolation vs. ListLinePlot

First lets see how the default Interpolation behaves compared to the interpolating function used when we call ListLinePlot with same interpolation specification. We use Method-> "Spline" in both cases and vary the InterpolationOrder from 1 to 4.

GraphicsGrid[{
Table[ff =Interpolation[#, Method -> "Spline", InterpolationOrder -> i] & /@data;
Plot[#[x] & /@ ff // Release, {x, 101, 111},Frame -> None, Axes -> True,
Epilog -> {Red, PointSize[Medium], Point[Flatten[data, 1]]}],
{i, 1, 4}],
Table[Show[
ListLinePlot[#, InterpolationOrder -> i, Method -> "Spline",PlotRange -> All,
PlotStyle -> Hue[RandomReal[]],Frame -> None, Axes -> True] & /@ data,
Epilog -> {Red, PointSize[Medium], Point[Flatten[data, 1]]}],
{i,1, 4}]
}, Frame -> All, AspectRatio -> .5]


The plots in the first column are using default Interpolation function and they starts to oscillate quite naturally as from left to right higher order splines are used. We notice the plots using ListLinePlot are definitely more similar to what the OP is aspiring to achieve. We can get a less bumpy interpolation that still belongs to the $C^{3}(X)$ space of three times differentiable functions as follows.

Function I (ListPlotInterpolation):

Options[ListPlotInterpolation] = {InterpolationOrder -> 3,Method -> Automatic};
ListPlotInterpolation[data_, OptionsPattern[]] := Module[{pic, pts},
pic = ListLinePlot[data, Method -> OptionValue[Method],
InterpolationOrder -> OptionValue[InterpolationOrder],PlotRange -> All];
Interpolation[First@Cases[pic, Line[pts : {{_, _} ..}] -> pts, Infinity],
InterpolationOrder -> OptionValue[InterpolationOrder],
Method -> OptionValue[Method]
]
];


Usage:

ListPlotInterpolation[data[[5]], Method -> "Spline",InterpolationOrder -> 3]


InterpolatingFunction[{{100.434,111.154}},<>]

In the following pic the plot in the left shows interpolation of the five given data sets using the above function and all of them are indeed less oscillating than the default Interpolation results with same options at the right .

## Taming the derivatives!

We can make the above situation a little better by taking the design advantage of the magic function Interpolation. We can always specify the derivatives numerically at the grid points to control the bumpiness of the interpolating function. We will first use ListPlotInterpolation'[] to estimate the derivative at the given grid points. Then we use the TotalVariationFilter to regularize the discrete gradient further on.

Function II (InterpolationReduced):

Options[InterpolationReduced] = {InterpolationOrder -> 3, Method -> Automatic};
InterpolationReduced[data_, OptionsPattern[]] :=
Module[{fun, coords, value},
fun = ListPlotInterpolation[data, Method -> OptionValue[Method],
InterpolationOrder -> OptionValue[InterpolationOrder]];
coords = data // Transpose // First;
value = Transpose@{data // Transpose // Last};
Interpolation[Transpose[{Transpose@{coords}, value,
(* You can change the filter parameter here *)
TotalVariationFilter[fun'[coords], 1, Method -> "Laplacian",
MaxIterations -> 200]}],
InterpolationOrder -> OptionValue[InterpolationOrder]
]
]


Usage: One can call the above function in similar fashion as the first one.

Now lets see how the gradients looks in case of three methods (default,ListLinePlot based and the TotalVariationFilter based). Each row of he following figure represent the methods respectively. From let to right each column represent InterpolationOrder of 1 to 4.

## Compare!

Below in the last figure we show how all the three methods compare in terms of irregularity/bumpiness of their resulting interpolation output given the output needs to be a $C^{3}(X)$ class function. See the most irregular fifth data set separately.

BR

-
What a great answer! – Lou Nov 3 '12 at 8:06
Your ListPlotInterpolation-Method and the Improvement with the TotalVariationFilter works wonders with my data Sets as well! Great and very detailed answer! – Martin May 28 '14 at 6:41