Skip to content

Smoothers

Ivan Svetunkov edited this page Jun 25, 2026 · 3 revisions

Smoothers — LOWESS and SuperSmoother

Function signatures

R

# Both smoothers are part of base R (the stats package)
lowess(x, y = NULL, f = 2/3, iter = 3, delta = 0.01 * diff(range(x)))
supsmu(x, y, wt, span = "cv", periodic = FALSE, bass = 0, trace = FALSE)

Python

def lowess(x, y=None, f=2/3, iter=3, delta=None): ...          # returns {"x", "y"}
def supsmu(x, y, wt=None, span="cv", periodic=False, bass=0.0): ...  # returns {"x", "y"}

Overview

greybox exposes two non-parametric scatterplot smoothers that mirror R's stats::lowess() and stats::supsmu() exactly:

  • lowess() — Cleveland's locally-weighted scatterplot smoother (1979). Robust to outliers via iterative reweighting.
  • supsmu() — Friedman's variable-span SuperSmoother (1984). Picks the bandwidth locally via cross-validated residuals.

In R both are part of base stats; in Python they are implemented as native pybind11 extensions inside greybox and reproduce R outputs to ~1e-15 (machine precision). They are also used internally by AID: LOWESS smooths the inter-demand intervals for stockout detection and SuperSmoother builds the demand-size regressor.

lowess — Cleveland's LOWESS

Description

A weighted local linear regression at each point uses a tricube kernel

$$w(u) = (1 - |u|^3)^3$$

over a span of f * n neighbours. After the initial fit, iter robustness iterations downweight large residuals using a bisquare weight, making the smoother resilient to outliers.

Parameters

Parameter R Python Type Default Description
x x x numeric / array_like Abscissa values. In Python may also be a 2-D array whose first column is x and second is y.
y y y numeric / array_like Ordinate values. Optional in Python if x already contains both columns.
span f f numeric / float 2/3 Smoother span — fraction of points in each local fit. Larger values produce smoother curves.
iterations iter iter integer / int 3 Number of robustness iterations.
delta delta delta numeric / float 0.01 * diff(range(x)) Distance threshold within which the fit is linearly interpolated.

Return Value

Implementation Return
R list with $x (sorted x) and $y (smoothed)
Python dict with keys "x" and "y" (both numpy.ndarray)

Examples

Smooth a noisy sine wave

# R
set.seed(0)
x <- seq(0, 6, length.out = 60)
y <- sin(x) + rnorm(60, 0, 0.2)
sm <- lowess(x, y, f = 0.4)
plot(x, y); lines(sm$x, sm$y, col = "red")
# Python
import numpy as np
from greybox import lowess

rng = np.random.default_rng(0)
x = np.linspace(0, 6, 60)
y = np.sin(x) + rng.normal(0, 0.2, 60)
sm = lowess(x, y, f=0.4)
# sm["x"] holds sorted x; sm["y"] holds the smoothed values

Smooth a noisy linear trend

# R
x <- 1:100
y <- 0.05 * x + rnorm(100, 0, 1)
sm <- lowess(x, y)
# Python
import numpy as np
from greybox import lowess

x = np.arange(100, dtype=float)
y = 0.05 * x + np.random.default_rng(1).normal(0, 1, 100)
sm = lowess(x, y)

supsmu — Friedman's SuperSmoother

Description

SuperSmoother evaluates three running linear smoothers ("tweeters") with spans 0.05, 0.2, and 0.5 of the sample size. At each abscissa it picks the span minimising the smoothed cross-validated residual. A final pass with the smallest span produces the output. This yields a variable-bandwidth fit that adapts to the local signal-to-noise ratio.

The Python port is a direct translation of R's FORTRAN supsmu (from stats/src/ppr.f).

Parameters

Parameter R Python Type Default Description
x x x numeric / array_like Abscissa values; sorted internally if not already ascending.
y y y numeric / array_like Ordinate values; same length as x.
weights wt wt numeric / array_like uniform Per-observation weights.
span span span numeric or "cv" / float or "cv" "cv" Fixed span in (0, 1], or "cv" (alias 0) for cross-validated automatic selection.
periodic periodic periodic logical / bool FALSE / False If TRUE, treat x as periodic in [0, 1].
bass tone bass bass numeric / float 0 Bass-tone control in [0, 10]; larger values bias span selection toward more smoothing. Values outside the range disable the adjustment.

Return Value

Implementation Return
R list with $x (sorted x) and $y (smoothed)
Python dict with keys "x" and "y" (both numpy.ndarray)

Examples

Default cross-validated span

# R
set.seed(1)
x <- seq(0, 6, length.out = 100)
y <- sin(x) + rnorm(100, 0, 0.3)
sm <- supsmu(x, y)
# Python
import numpy as np
from greybox import supsmu

rng = np.random.default_rng(1)
x = np.linspace(0, 6, 100)
y = np.sin(x) + rng.normal(0, 0.3, 100)
sm = supsmu(x, y)

Fixed span

# R
sm_fixed <- supsmu(x, y, span = 0.3)
# Python
sm_fixed = supsmu(x, y, span=0.3)

Bass-tone control (heavier smoothing in noisy regions)

# R
sm_heavy <- supsmu(x, y, bass = 5)
# Python
sm_heavy = supsmu(x, y, bass=5.0)

Plotting the smoother output

Neither lowess() nor supsmu() has a dedicated method on either side — the return value is just list(x, y) in R / {"x", "y"} in Python. Plotting is therefore a standard one-liner.

# R
plot(x, y)
sm <- lowess(x, y)
lines(sm$x, sm$y, col = "red")
# Python
import matplotlib.pyplot as plt
from greybox import lowess, supsmu

plt.scatter(x, y, s=10, alpha=0.5, label="data")
lo = lowess(x, y, f=0.4)
sm = supsmu(x, y)
plt.plot(lo["x"], lo["y"], color="red",  label="LOWESS")
plt.plot(sm["x"], sm["y"], color="blue", label="SuperSmoother")
plt.legend()
plt.show()

Notes on the Python implementation

  • Top-level import: from greybox import lowess, supsmu.
  • Both functions are thin Python wrappers around C++/pybind11 extensions (greybox._native_lowess, greybox._native_supsmu). They reproduce R's outputs to within ~1e-15 on the same inputs.
  • The Python API returns a dict rather than an R-style list; access values with result["x"] and result["y"].

References

  • Cleveland, W. S. (1979). "Robust Locally Weighted Regression and Smoothing Scatterplots". Journal of the American Statistical Association, 74(368), 829–836. DOI: 10.1080/01621459.1979.10481038
  • Friedman, J. H. (1984). "A Variable Span Smoother". Technical Report 5 (SLAC-PUB-3477; STAN-LCS-005), Laboratory for Computational Statistics, Department of Statistics, Stanford University. OSTI 1447470

Clone this wiki locally