Standard LOESS neighbourhoods become asymmetric at the boundaries:
fewer points exist on one side, pulling the local fit toward the data
interior. The boundary_policy parameter controls how the
data is padded to mitigate this effect.
| Policy | Padding Strategy | Best For |
|---|---|---|
"extend" |
Repeat first / last value | Most datasets (default) |
"reflect" |
Mirror data at boundaries | Periodic or symmetric data |
"zero" |
Pad with zeros | Data known to approach zero |
"noboundary" |
No padding (Cleveland original) | Reference behaviour |
Pads beyond both endpoints by replicating the first and last observed values. Prevents the fit from curling toward zero and is a safe default for nearly all use cases.
Use when: No strong prior on boundary behaviour; general-purpose smoothing.
library(rfastloess)
set.seed(42)
x <- seq(0, 2 * pi, length.out = 100)
y <- sin(x) + rnorm(100, sd = 0.3)
model <- Loess(boundary_policy = "extend")
result <- fit(model, x, y)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 0.4897657Mirrors the data about both endpoints before fitting, then discards the reflected region from the output. Preserves continuity of derivatives at the endpoints.
Use when: Circular data (e.g., angle, day-of-year), symmetric physical quantities, or when the derivative at the boundary is expected to be zero.
model <- Loess(boundary_policy = "reflect")
result <- fit(model, x, y)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 0.6080511Pads beyond both endpoints with zeros before fitting. Appropriate when the underlying process is known to be zero outside the observed range.
Use when: Signal decays to zero at both ends; zero is a meaningful boundary value.
model <- Loess(boundary_policy = "zero")
result <- fit(model, x, y)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 0.253663Applies no padding. Each local fit uses only the points that are actually available, which may be fewer than the requested neighbourhood at the endpoints. This reproduces the original Cleveland (1979) algorithm exactly.
Use when: Reproducing reference results; you prefer the raw LOESS boundary behaviour.
Note: Without padding, boundary fits can have higher variance and visible edge artefacts, particularly with small
fractionvalues.
model <- Loess(boundary_policy = "noboundary")
result <- fit(model, x, y)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 0.6893051library(rfastloess)
set.seed(42)
x <- seq(0, 2 * pi, length.out = 100)
y <- sin(x) + rnorm(100, sd = 0.3)
policies <- c("extend", "reflect", "zero", "noboundary")
colors <- c("blue", "red", "green", "purple")
plot(x, y, pch = 16, col = "gray",
main = "Boundary Policy Comparison")
for (i in seq_along(policies)) {
model <- Loess(boundary_policy = policies[i])
result <- fit(model, x, y)
lines(result$x, result$y, col = colors[i], lwd = 2)
}
legend("topright", policies, col = colors, lwd = 2)| Situation | Recommended Policy |
|---|---|
| General purpose | "extend" (default) |
| Periodic signal (angle, day-of-year) | "reflect" |
| Signal known to be zero at boundaries | "zero" |
| Replicating original Cleveland behaviour | "noboundary" |
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