Skip to contents

Overview

Boundary policy comparison
Boundary policy comparison

Standard LOWESS 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

Extend (Default)

Pads beyond both endpoints by replicating the first and last observed values. Prevents the fit from curling toward zero.

Use when: No strong prior on boundary behaviour; general-purpose smoothing.

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

model <- Lowess(boundary_policy = "extend")
result <- fit(model, x, y)
cat("First 6 smoothed values (extend policy):\n")
#> First 6 smoothed values (extend policy):
print(head(result$y))
#> [1] 0.5023423 0.5118995 0.5218795 0.5322473 0.5429093 0.5537345

Reflect

Mirrors data at both endpoints. Prevents inflection artifacts when the signal is periodic or symmetric.

Use when: Signal is periodic (e.g. time-of-day) or the boundary is a known symmetry point.

model <- Lowess(boundary_policy = "reflect")
result <- fit(model, x, y)
cat("First 6 smoothed values (reflect policy):\n")
#> First 6 smoothed values (reflect policy):
print(head(result$y))
#> [1] 0.6183818 0.6174389 0.6174211 0.6183044 0.6200242 0.6224897

Zero

Pads with zeros beyond the endpoints.

Use when: The signal is known to decay to zero at the boundaries (e.g. impulse responses, genomic signals at chromosome ends).

model <- Lowess(boundary_policy = "zero")
result <- fit(model, x, y)
cat("First 6 smoothed values (zero policy):\n")
#> First 6 smoothed values (zero policy):
print(head(result$y))
#> [1] 0.2889433 0.3090667 0.3296141 0.3505350 0.3717128 0.3929858

No Boundary Padding

Reproduces Cleveland’s original behaviour — no padding applied.

Use when: Comparing against reference implementations; the original Cleveland algorithm behaviour is required.

Note: Without padding, boundary fits can have higher variance and visible edge artefacts, particularly with small fraction values.

model <- Lowess(boundary_policy = "noboundary")
result <- fit(model, x, y)
cat("First 6 smoothed values (noboundary policy):\n")
#> First 6 smoothed values (noboundary policy):
print(head(result$y))
#> [1] 0.6766520 0.6769305 0.6769029 0.6765617 0.6759035 0.6749280

Comparing Policies

library(rfastlowess)
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  <- Lowess(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)

sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.5 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.26.so;  LAPACK version 3.12.0
#> 
#> locale:
#>  [1] LC_CTYPE=C.UTF-8       LC_NUMERIC=C           LC_TIME=C.UTF-8       
#>  [4] LC_COLLATE=C.UTF-8     LC_MONETARY=C.UTF-8    LC_MESSAGES=C.UTF-8   
#>  [7] LC_PAPER=C.UTF-8       LC_NAME=C              LC_ADDRESS=C          
#> [10] LC_TELEPHONE=C         LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C   
#> 
#> time zone: UTC
#> tzcode source: system (glibc)
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] rfastlowess_5.0.0
#> 
#> loaded via a namespace (and not attached):
#>  [1] digest_0.6.39     desc_1.4.3        R6_2.6.1          fastmap_1.2.0    
#>  [5] xfun_0.61         cachem_1.1.0      knitr_1.52        htmltools_0.5.9  
#>  [9] rmarkdown_2.32    lifecycle_1.0.5   cli_3.6.6         sass_0.4.10      
#> [13] pkgdown_2.2.1     textshaping_1.0.5 jquerylib_0.1.4   systemfonts_1.3.2
#> [17] compiler_4.6.1    tools_4.6.1       ragg_1.5.2        bslib_0.12.0     
#> [21] evaluate_1.0.5    yaml_2.3.12       otel_0.2.0        jsonlite_2.0.0   
#> [25] rlang_1.3.0       fs_2.1.0          htmlwidgets_1.6.4