Custom Weights

How Custom Weights Work

Standard LOESS assigns equal prior trust to all observations. Custom weights let you override this assumption point by point — before any distance or robustness weighting is applied.

The effective weight of observation \(j\) in a local fit centred at \(x_i\) is:

\[w_{ij} = \text{custom\_weights}[j] \times K\!\left(\frac{d_{ij}}{h_i}\right) \times r_j\]

where \(K\) is the distance kernel, \(h_i\) is the local bandwidth, and \(r_j\) is the robustness weight from the current iteration.

custom_weights supplies one finite non-negative prior weight per observation. Batch accepts weights in fit; Streaming accepts process_chunk(..., custom_weights=...); Online accepts add_point(..., weight=...). Streaming preserves weights through overlap buffers, and Online evicts each weight with its observation. Existing unweighted calls use weight 1.0.


When to Use Custom Weights

Situation Recommended weight
Point known to be erroneous 0.0 — fully excluded
Unreliable sensor / low precision 0.1 – 0.5
Standard observation 1.0 (default)
Carefully calibrated measurement > 1.0
Measurement uncertainty \(\sigma_i\) \(1 / \sigma_i^2\)

Custom Weights vs. Robustness Iterations

Both mechanisms handle unreliable data, but they serve different purposes:

Custom Weights Robustness Iterations
When known Before fitting Computed from residuals
Knowledge required Prior knowledge of quality None — data-driven
Effect Fixed throughout fit Adapts each iteration
Use case Known bad sensors, calibration Unknown contamination

They compose: you can use both simultaneously. Custom weights suppress a priori bad points; robustness iterations then handle any residual outliers that remain.


Basic Usage

Suppress a Known Outlier

Set the weight to 0 at the bad point — it is excluded from every local fit that would otherwise include it.

library(rfastloess)

x <- 0:9
y <- x * 2.0
y[6] <- 100.0               # spike at index 5 (0-based)

weights <- rep(1.0, 10)
weights[6] <- 0.0            # exclude the spike

model <- Loess(fraction = 0.5, iterations = 0L)
result <- fit(model, x, y, custom_weights = weights)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 0.572621

Emphasize Important Points

Assign high weights to measurements you trust most — calibration standards, reference instruments, or low-noise observations.

library(rfastloess)
set.seed(42)
x <- seq(0, 2 * pi, length.out = 100)
y <- sin(x) + rnorm(100, sd = 0.3)
calibration_indices <- c(6, 21, 41, 61, 81)

weights <- rep(1.0, length(x))
weights[calibration_indices] <- 10.0   # trust calibration 10x more

model <- Loess(fraction = 0.5)
result <- fit(model, x, y, custom_weights = weights)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 0.4228696

Propagate Measurement Uncertainty

If each observation has a known standard deviation \(\sigma_i\), set \(w_i = 1 / \sigma_i^2\) to give the fit information-theoretically optimal weighting.

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

sigma <- runif(100, 0.1, 0.5)
weights <- 1 / sigma^2

model <- Loess(fraction = 0.5)
result <- fit(model, x, y, custom_weights = weights)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 0.4414661

Combined with Robustness Iterations

Custom weights and robustness iterations compose naturally: use custom weights for known bad points and robustness for unknown contamination.

library(rfastloess)

x <- 0:19
y <- x * 1.5
y[4] <- -50.0    # known bad
y[13] <- 80.0    # unknown outlier

weights <- rep(1.0, 20)
weights[4] <- 0.0

model <- Loess(fraction = 0.4, iterations = 3)
result <- fit(model, x, y, custom_weights = weights)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 0.8673742

Validation Rules

Rule Effect
Length must equal n Error at fit time if mismatched
All values must be ≥ 0 Negative weights are rejected
All-zero weight vector Error: no points remain for any local fit
Uniform weights (1.0 everywhere) Identical result to omitting weights

Zero-weight windows: If a local neighbourhood contains only zero-weight points, the fit at that centre point falls back to the behaviour specified by zero_weight_fallback (default: "use_local_mean").


See Also

  • vignette("robustness", package = "rfastloess") — adaptive outlier downweighting via IRLS
  • ?Loess — full parameter reference
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