Standard LOESS can be biased by outliers. Robustness iterations downweight points with large residuals:
Smooth downweighting. Points transition gradually from full weight to zero.
\[w(u) = \begin{cases} (1 - u^2)^2 & |u| < 1 \\ 0 & |u| \geq 1 \end{cases}\]
Use when: General purpose, balanced approach.
library(rfastloess)
set.seed(42)
x <- seq(0, 2 * pi, length.out = 100)
y <- sin(x) + rnorm(100, sd = 0.3)
model <- Loess(iterations = 3, robustness_method = "bisquare")
result <- fit(model, x, y)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 0.4897657Linear penalty beyond threshold. Less aggressive than Bisquare.
\[w(u) = \begin{cases} 1 & |u| \leq k \\ k/|u| & |u| > k \end{cases}\]
Use when: Moderate outliers, want to retain some influence.
model <- Loess(iterations = 3, robustness_method = "huber")
result <- fit(model, x, y)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 0.4999039Hard threshold. Points are either fully weighted or completely excluded.
\[w(u) = \begin{cases} 1 & |u| \leq k \\ 0 & |u| > k \end{cases}\]
Use when: Extreme outliers, want binary exclusion.
model <- Loess(iterations = 3, robustness_method = "talwar")
result <- fit(model, x, y)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 0.4730281| Method | Transition | Aggressiveness | Use Case |
|---|---|---|---|
| Bisquare | Smooth | Moderate | General purpose |
| Huber | Gradual | Mild | Preserve influence |
| Talwar | Hard | Strong | Extreme contamination |
Use robustness weights to identify potential outliers:
library(rfastloess)
set.seed(42)
x <- seq(0, 2 * pi, length.out = 100)
y <- sin(x) + rnorm(100, sd = 0.3)
y[c(20, 50, 80)] <- y[c(20, 50, 80)] + 5 # inject outliers
model <- Loess(iterations = 5, outputs = "weights")
result <- fit(model, x, y)
shown <- 0
for (i in seq_along(result$robustness_weights)) {
if (result$robustness_weights[i] < 0.5 && shown < 5) {
cat(sprintf("Potential outlier at index %d: weight = %.3f\n",
i, result$robustness_weights[i]))
shown <- shown + 1
}
}
#> Potential outlier at index 2: weight = 0.245
#> Potential outlier at index 9: weight = 0.363
#> Potential outlier at index 12: weight = 0.052
#> Potential outlier at index 18: weight = 0.383
#> Potential outlier at index 20: weight = 0.000Residuals are scaled before computing robustness weights. Two methods:
| Method | Formula | Robustness |
|---|---|---|
| MAD | median(\|r - median(r)\|) |
Very robust (default) |
| MAR | median(\|r\|) |
Robust, uncentered |
| Mean | mean(\|r\|) |
Less robust, fastest |
library(rfastloess)
set.seed(42)
x <- seq(0, 2 * pi, length.out = 100)
y <- sin(x) + rnorm(100, sd = 0.3)
model <- Loess(iterations = 3, scaling_method = "mad")
result <- fit(model, x, y)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 0.4897657Stop iterations early when weights stabilize:
Performance: Auto-convergence can significantly reduce computation when weights stabilize before reaching max iterations.
library(rfastloess)
set.seed(42)
x <- seq(0, 2 * pi, length.out = 100)
y <- sin(x) + rnorm(100, sd = 0.3)
model <- Loess(iterations = 10, auto_converge = 1e-6)
result <- fit(model, x, y)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 0.4777674sessionInfo()
#> 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