Skip to content

Polynomial Degree

Degree of the local polynomial fitted at each point.

At each target point, LOESS fits a polynomial to the neighbouring data using weighted least squares. The degree parameter controls the order of that polynomial.

Degree Comparison

DegreeLocal FitCapturesRisk
0ConstantLevel onlyOver-smooth, biased at edges
1LinearTrend (default)Rarely overfits
2QuadraticCurvatureOverfits with small fraction
3CubicInflectionsRequires larger fraction
4QuarticFine structureHigh variance, rarely needed

y^(x0)=arg⁡min⁡a∑iwi(x0) (yi−a)2\hat{y}(x_0) = \arg\min_a \sum_i w_i(x_0)\,(y_i - a)^2

The fit at each point is simply a weighted mean. Produces very smooth results but ignores local slope, introducing bias wherever the true function changes.

Use when: Maximum smoothness is more important than accuracy; computationally cheapest option.

const { Loess } = require('fastloess-wasm');
const n = 100;
const x = Float64Array.from({ length: n }, (_, i) => i * 2 * Math.PI / (n - 1));
const y = Float64Array.from(x, (xi, i) => Math.sin(xi) + (((i * 7 + 3) % 17) / 17 - 0.5) * 0.6);
const model = new Loess({ degree: "constant", fraction: 0.5 });
const result = model.fit(x, y);
console.log("y[0]:", result.y[0].toFixed(4));
y[0]: 0.2935

y^(x0)=arg⁡min⁡a,b∑iwi(x0) (yi−a−bxi)2\hat{y}(x_0) = \arg\min_{a,b} \sum_i w_i(x_0)\,(y_i - a - b x_i)^2

Fits a weighted line through the neighbourhood. Removes first-order bias and handles boundary regions correctly. The right choice for the vast majority of applications.

Use when: Default; monotone or gently curved data; boundary accuracy matters.

const { Loess } = require('fastloess-wasm');
const n = 100;
const x = Float64Array.from({ length: n }, (_, i) => i * 2 * Math.PI / (n - 1));
const y = Float64Array.from(x, (xi, i) => Math.sin(xi) + (((i * 7 + 3) % 17) / 17 - 0.5) * 0.6);
const model = new Loess({ degree: "linear", fraction: 0.5 });
const result = model.fit(x, y);
console.log("y[0]:", result.y[0].toFixed(4));
y[0]: 0.1118

y^(x0)=arg⁡min⁡a,b,c∑iwi(x0) (yi−a−bxi−cxi2)2\hat{y}(x_0) = \arg\min_{a,b,c} \sum_i w_i(x_0)\,(y_i - a - b x_i - c x_i^2)^2

Fits a weighted parabola through the neighbourhood. Removes second-order bias and captures local curvature more faithfully, but requires more data per neighbourhood — pair with a larger fraction (≥ 0.4) to avoid overfitting.

Use when: Data with pronounced peaks, valleys, or curvature; fraction ≥ 0.4.

const { Loess } = require('fastloess-wasm');
const n = 100;
const x = Float64Array.from({ length: n }, (_, i) => i * 2 * Math.PI / (n - 1));
const y = Float64Array.from(x, (xi, i) => Math.sin(xi) + (((i * 7 + 3) % 17) / 17 - 0.5) * 0.6);
const model = new Loess({ degree: "quadratic", fraction: 0.5 });
const result = model.fit(x, y);
console.log("y[0]:", result.y[0].toFixed(4));
y[0]: 0.0272

y^(x0)=arg⁡min⁡a,b,c,d∑iwi(x0) (yi−a−bxi−cxi2−dxi3)2\hat{y}(x_0) = \arg\min_{a,b,c,d} \sum_i w_i(x_0)\,(y_i - a - b x_i - c x_i^2 - d x_i^3)^2

Fits a weighted cubic polynomial. Captures inflection points and S-shaped local behaviour. Requires a substantially larger neighbourhood than degree 2 — use fraction ≥ 0.5 and verify visually for overfitting.

Use when: Data has clear S-shaped curves or multiple inflection points; fraction ≥ 0.5.

const { Loess } = require('fastloess-wasm');
const n = 100;
const x = Float64Array.from({ length: n }, (_, i) => i * 2 * Math.PI / (n - 1));
const y = Float64Array.from(x, (xi, i) => Math.sin(xi) + (((i * 7 + 3) % 17) / 17 - 0.5) * 0.6);
const model = new Loess({ degree: "cubic", fraction: 0.6 });
const result = model.fit(x, y);
console.log("y[0]:", result.y[0].toFixed(4));
y[0]: 0.0292

y^(x0)=arg⁡min⁡a,...,e∑iwi(x0) (yi−a−bxi−⋯−exi4)2\hat{y}(x_0) = \arg\min_{a,...,e} \sum_i w_i(x_0)\,(y_i - a - b x_i - \cdots - e x_i^4)^2

Fits a weighted quartic polynomial. Rarely needed in practice; only useful for capturing highly oscillatory local structure. Very prone to overfitting — require fraction ≥ 0.6 and cross-validate.

Use when: Fine oscillatory structure is physically meaningful and the dataset is large; always cross-validate.

const { Loess } = require('fastloess-wasm');
const n = 100;
const x = Float64Array.from({ length: n }, (_, i) => i * 2 * Math.PI / (n - 1));
const y = Float64Array.from(x, (xi, i) => Math.sin(xi) + (((i * 7 + 3) % 17) / 17 - 0.5) * 0.6);
const model = new Loess({ degree: "quartic", fraction: 0.7 });
const result = model.fit(x, y);
console.log("y[0]:", result.y[0].toFixed(4));
y[0]: 0.0327

SituationRecommended Degree
Monotone trend, general purpose1 (default)
Maximum smoothness, speed0
Clear peaks / valleys / inflections2 (with fraction ≥ 0.4)
S-shaped curves, multiple inflections3 (with fraction ≥ 0.5)
Fine oscillatory structure (rare)4 (with fraction ≥ 0.6, cross-validate)
Boundary accuracy is critical1 or 2 (not 0)
Very small dataset (n < 50)1

Higher Degree Comparison


The surface_mode parameter controls whether LOESS evaluates the local polynomial at every query point or at a sparser grid of vertices with Hermite cubic interpolation in between.

ModeBehaviourSpeedAccuracy
"interpolation" (default)Evaluate at anchor vertices, blend via Hermite cubicFasterSlight approximation
"direct"Evaluate at every query pointExactFull precision

Surface Mode Comparison

Degree × Interpolation