Polynomial Degree

Overview

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
Degree comparison
Degree Local Fit Captures Risk
0 / "constant" Weighted mean Level only Over-smooth; edge bias
1 / "linear" Weighted line Trend (default) Rarely overfits
2 / "quadratic" Parabola Curvature Overfits with small fraction
3 / "cubic" Cubic curve Inflections Requires larger fraction
4 / "quartic" Quartic curve Fine structure High variance, rare

Degree can be specified as an integer (0L–4L) or as a string ("constant", "linear", "quadratic", "cubic", "quartic").


Degree 0 — Local Constant

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

Use when: Maximum smoothness matters more than accuracy; computationally cheapest.

library(rfastloess)
set.seed(42)
x <- seq(0, 2 * pi, length.out = 100)
y <- sin(x) + rnorm(100, sd = 0.3)

model <- Loess(fraction = 0.5, degree = "constant")
result <- fit(model, x, y)
cat("First 6 smoothed values (constant/Nadaraya-Watson):\n")
#> First 6 smoothed values (constant/Nadaraya-Watson):
print(head(result$y))
#> [1] 0.4694689 0.4748509 0.4785029 0.4798484 0.4836073 0.4936813

Degree 1 — Local Linear (Default)

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.

model <- Loess(fraction = 0.5, degree = "linear")
result <- fit(model, x, y)
cat("First 6 smoothed values (linear local regression):\n")
#> First 6 smoothed values (linear local regression):
print(head(result$y))
#> [1] 0.4758389 0.4846388 0.4944609 0.5054102 0.5169758 0.5286606

Degree 2 — Local Quadratic

Fits a weighted parabola. Captures curvature in the data and reduces bias in regions with strong local curvature, at the cost of higher variance.

Use when: Data has visible curvature or local extrema; use a larger fraction to stabilise the fit.

model <- Loess(fraction = 0.5, degree = "quadratic")
result <- fit(model, x, y)
cat("First 6 smoothed values (quadratic local regression):\n")
#> First 6 smoothed values (quadratic local regression):
print(head(result$y))
#> [1] 0.4043043 0.4131994 0.4244604 0.4384709 0.4565813 0.4791656

Degree 3 — Local Cubic

Fits a weighted cubic polynomial. Captures inflections and fine structure but requires a sufficiently large neighbourhood to remain stable.

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

model <- Loess(fraction = 0.6, degree = "cubic")
result <- fit(model, x, y)
cat("First 6 smoothed values (cubic local regression):\n")
#> First 6 smoothed values (cubic local regression):
print(head(result$y))
#> [1] 0.4183075 0.4299695 0.4442197 0.4608883 0.4798054 0.5008015

Degree 4 — Local Quartic

Fits a weighted quartic polynomial. Rarely needed in practice; only useful for capturing highly oscillatory local structure. Very prone to overfitting.

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

model <- Loess(fraction = 0.7, degree = "quartic")
result <- fit(model, x, y)
cat("First 6 smoothed values (quartic local regression):\n")
#> First 6 smoothed values (quartic local regression):
print(head(result$y))
#> [1] 0.3964750 0.4064069 0.4182002 0.4319700 0.4478315 0.4658997

Degree and Boundary Fallback

At the dataset boundary, the neighbourhood is one-sided and may not contain enough points to support the requested degree. Setting boundary_degree_fallback = TRUE automatically reduces the degree at boundaries to avoid instability.

model <- Loess(degree = 2L, boundary_degree_fallback = TRUE)
result <- fit(model, x, y)
cat("First 6 smoothed values (quadratic, boundary fallback):\n")
#> First 6 smoothed values (quadratic, boundary fallback):
print(head(result$y))
#> [1] 0.4577829 0.4682789 0.4793020 0.4908341 0.5028573 0.5153538

Choosing the Right Degree

Situation Recommended Degree
Monotone trend, general purpose 1 (default)
Maximum smoothness, speed 0
Clear peaks / valleys / inflections 2 (with fraction >= 0.4)
S-shaped curves, multiple inflections 3 (with fraction >= 0.5)
Fine oscillatory structure (rare) 4 (fraction >= 0.6, CV)
Boundary accuracy is critical 1 or 2 (not 0)
Very small dataset (n < 50) 1

Rule of thumb: Start with degree = 1 (default). Move to degree = 2 only if you see systematic bias in regions of high curvature, and increase fraction at the same time.


Higher Degree Effects

Higher Degree Comparison
Higher Degree Comparison

Surface Mode

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.

Mode Behaviour Speed Accuracy
"interpolation" (default) Anchor + Hermite blend Faster Approx
"direct" Evaluate at every query point Exact Full precision
Surface Mode Comparison
Surface Mode Comparison
Degree x Interpolation
Degree x Interpolation

See vignette("dimensions", package = "rfastloess") for runnable surface_mode examples.

sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04.1 LTS
#> 
#> Matrix products: default
#> BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so;  LAPACK version 3.12.0
#> 
#> locale:
#>  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
#>  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
#>  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
#>  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
#>  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
#> 
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] rfastloess_3.0.0 rmarkdown_2.32  
#> 
#> loaded via a namespace (and not attached):
#>  [1] digest_0.6.39    R6_2.6.1         fastmap_1.2.0    xfun_0.61       
#>  [5] maketools_1.3.2  cachem_1.1.0     knitr_1.52       htmltools_0.5.9 
#>  [9] buildtools_1.0.0 lifecycle_1.0.5  cli_3.6.6        sass_0.4.10     
#> [13] jquerylib_0.1.4  compiler_4.6.1   sys_3.4.3        tools_4.6.1     
#> [17] evaluate_1.0.5   bslib_0.12.0     yaml_2.3.12      otel_0.2.0      
#> [21] jsonlite_2.0.0   rlang_1.3.0