# How to smooth zig-zag?

How to smooth a zig-zag-line with only a few points?
I have read the following topics here in this forum:
smoothing-interpolation-for-tachometer-data
smoothing-data-with-dates
But I am still struggling with my sort of zig-zag-lines. I achieved the best results so far with the package `bsplinekit`, but the result is still not what I am looking for.
Any ideas? Here my `BSplineKit.jl`-example:

``````begin
using PlotlyJS
using BSplineKit, LinearAlgebra, SparseArrays
# ---
case_ = 1
if case_ == 1 # real data
x_in = [0.114, 0.112, 0.11, 0.107, 0.105, 0.103, 0.102, 0.0998, 0.0983, 0.097, 0.0969, 0.094, 0.0935, 0.0928,
0.0928, 0.0917, 0.093, 0.0918, 0.092, ]
y_in = [-0.0126, -0.0116, -0.0119, -0.0113, -0.0106, -0.00912, -0.00981, -0.0089, -0.00685, -0.00702, -0.00574,
-0.00603, -0.00516, -0.00431, -0.00328, -0.00135, -0.000356, -0.000367, 0.00181,]
elseif case_ == 2 # synthetic data
x_in = collect(1:1.0:10000)
y_in = [x^1.35 + x*rand() for x in 1:length(x_in)]
else
error("Case not defined!")
end
end
# ---
begin
# --- code taken from:
# --- https://discourse.julialang.org/t/smoothing-interpolation-for-tachometer-data/56117/20
n_breakpoints = 4
spline_order  = 3 # order = 4 is equal to cubic splines
# Create B-spline basis of "spline_order" order (spline_order = 4 <=> cubic splines) with chosen breakpoints
xbreaks = LinearAlgebra.LinRange(extrema(x_in)..., n_breakpoints)
B_ = BSplineBasis(BSplineOrder(spline_order), xbreaks)
# Construct spline from the given data by @jipolanco
C_ = BSplineKit.collocation_matrix(B_, x_in, SparseMatrixCSC{Float64})  # evaluates basis functions at data points
coefs = LinearAlgebra.qr(C_) \ y_in       # spline coefficients
fspline = BSplineKit.Spline(B_, coefs)
y_smoothed = fspline.(x_in)
# --- plot:
fn_ = joinpath(raw"C:\tmp\plt", string("smoothing_spline_order_", spline_order, ".svg"))
fig_hdl = PlotlyJS.Plot([PlotlyJS.scatter(; x= x_in, y= y_in, name= "data", mode= "markers+lines"),
PlotlyJS.scatter(; x= x_in, y= y_smoothed, name= "smoothed")],
PlotlyJS.Layout(;title_text = string("Data Points: ", length(x_in), ", Break Points: ", n_breakpoints,
", Spline Order: ", spline_order))        )
PlotlyJS.savefig(fig_hdl, fn_)
end
``````

You want the number of B-spline breakpoints (`n_breakpoints`) to be smaller than the number of data points, otherwise you won’t get a smoothing spline. This is not the case in your case 1.

Also, I’m not sure if this is intentional, but your `x_in` data is not monotonously increasing, which may cause some issues. Maybe you intended to fit a parametric spline instead?

PS: for next time, It would be easier to help if you showed us the resulting plot.

1 Like

Thanks for your comment, indeed with 5 breakpoints and 19 data points I can clearly see the smoothing effect and if I reduce the spline order to 3, it looks really nice:

Here my proposal for a smoothing function, comments are welcome!

``````using BSplineKit, LinearAlgebra, SparseArrays
# ---
function _MyLibSmoothing(_x_vec::Vector{<:Number}, _y_vec::Vector{<:Number}, _x_smooth::Union{Nothing, <:Number, Vector{<:Number}}=nothing;
_spline_order::Int=3, _n_breakpts::Int=0)
if length(_x_vec) == length(_y_vec)
_n_pts = length(_x_vec)
if _n_pts < 5
error("Input vectors x and y too short, min values are 5 points!")
end
else
error("Input vectors x and y must have the same length!")
end
if _n_breakpts == 0
if _n_pts < 10
_n_breakpts = floor(Int, _n_pts / 2)
elseif _n_pts < 50
_n_breakpts = floor(Int, _n_pts / 4)
else
_n_breakpts = floor(Int, _n_pts / 10)
end
else
if _n_breakpts > _n_pts
error("Reduce braekpoints! - num Breakpoints: ", _n_breakpts, " - num data points: ", _n_pts, "!")
elseif _n_breakpts > floor(Int, _n_pts / 2)
@warn("Too many breakpoints for significant smoothing!")
end
end
if _spline_order != 3
if _spline_order < 2 || _spline_order > 4
error("Spline Order must be in the range of [2 .. 4], specified is: ", _spline_order, "!")
end
end
if isnothing(_x_smooth)
_x_smooth = _x_vec
else
if minimum(_x_smooth) < minimum(_x_vec)
error("Only interpolation allowed: X-values for smoothing below range!")
end
if maximum(_x_smooth) > maximum(_x_vec)
error("Only interpolation allowed: X-values for smoothing above range!")
end
end
# ---
_xbreaks        = LinearAlgebra.LinRange(extrema(_x_vec)..., _n_breakpts)
_BSpl_base      = BSplineBasis(BSplineOrder(_spline_order), _xbreaks)
# --- Construct spline from the given data by @jipolanco
_colloc_matrix  = BSplineKit.collocation_matrix(_BSpl_base, _x_vec, SparseMatrixCSC{Float64})  # evaluates basis functions at data points
_spline_coeffs  = LinearAlgebra.qr(_colloc_matrix) \ _y_vec       # spline coefficients
_fspline        = BSplineKit.Spline(_BSpl_base, _spline_coeffs)
# ---
return _x_smooth, _fspline.(_x_smooth)
end
``````