LOESS provides flexible trend extraction from time series without parametric assumptions.
fraction = 0.1 sizes the neighbourhood as 10% of the
data at each evaluation point — narrow enough to follow a slowly varying
trend without smearing periodic variation. Three robustness
iterations down-weight noise spikes so they cannot bias the
fitted curve; this is especially important when the signal-to-noise
ratio is low or when occasional outliers are expected.
library(rfastloess)
set.seed(42)
t <- seq(0, 100, length.out = 500)
trend <- 10 + 0.5 * t + 3 * sin(t / 10)
noise <- rnorm(500, sd = 3)
y <- trend + noise
model <- Loess(fraction = 0.1, iterations = 3)
result <- fit(model, t, y)
plot(t, y, col = "gray", pch = ".",
xlab = "Time", ylab = "Value", main = "Trend Extraction")
lines(result$x, result$y, col = "blue", lwd = 2)
legend("topleft", c("Observed", "Trend (LOESS)"),
pch = c(1, NA), lty = c(NA, 1), col = c("gray", "blue"))Setting outputs = "residuals" stores
observed − smoothed alongside the smooth. A slightly wider
fraction = 0.4 produces a smoother baseline trend, so
short-duration oscillations end up in the residuals rather than being
absorbed into the trend component. The residual series is then ready for
spectral analysis, seasonality detection, or change-point methods.
library(rfastloess)
set.seed(42)
t <- 1:120
trend <- 100 + 0.5 * t
seasonal <- 15 * sin(2 * pi * t / 12)
noise <- rnorm(120, sd = 5)
y <- trend + seasonal + noise
model <- Loess(fraction = 0.4, iterations = 2, outputs = "residuals")
result <- fit(model, t, y)
residual <- result$residuals
par(mfrow = c(2, 1), mar = c(4, 4, 2, 0.5))
plot(t, y, type = "l", col = "gray",
main = "Original + LOESS Trend", xlab = "Month", ylab = "Value")
lines(result$x, result$y, col = "blue", lwd = 2)
plot(t, residual, type = "l", col = "darkgreen",
main = "Detrended Residual (Seasonal Component)",
xlab = "Month", ylab = "Residual")
abline(h = 0, lty = 2, col = "red")library(rfastloess)
set.seed(42)
t_irregular <- c(sort(runif(200, 0, 50)), sort(runif(50, 50, 100)))
y_irregular <- sin(t_irregular / 10) + rnorm(length(t_irregular), sd = 0.5)
model <- Loess(fraction = 0.2, iterations = 2)
result <- fit(model, t_irregular, y_irregular)
plot(t_irregular, y_irregular, pch = 16, cex = 0.4, col = "gray",
xlab = "Time", ylab = "Value", main = "Irregularly Sampled Time Series")
lines(result$x, result$y, col = "blue", lwd = 2)Prediction intervals widen the uncertainty band to include both the
uncertainty in the fitted curve (confidence interval) and the expected
scatter of new observations around it. fraction = 0.2
offers a balance between local detail and stable interval width — too
small a fraction produces jagged interval edges; too large a fraction
underestimates local variance near turning points.
library(rfastloess)
set.seed(42)
t <- seq(0, 100, length.out = 500)
y <- 10 + 0.3 * t + sin(t / 5) + rnorm(500, sd = 2)
model <- Loess(
fraction = 0.2,
iterations = 3,
intervals = intervals_opts(confidence = 0.95, prediction = 0.95)
)
result <- fit(model, t, y)
plot(t, y, pch = ".", col = "gray",
xlab = "Time", ylab = "Value",
main = "Trend with 95% Prediction Interval")
polygon(c(result$x, rev(result$x)),
c(result$prediction_upper, rev(result$prediction_lower)),
col = rgb(0, 0, 1, 0.15), border = NA)
lines(result$x, result$y, col = "blue", lwd = 2)LOESS naturally handles irregular time sampling:
library(rfastloess)
set.seed(42)
t_irregular <- sort(runif(200, 0, 100))
y_irregular <- 10 + t_irregular * 0.3 + rnorm(200, sd = 2)
model <- Loess(fraction = 0.2)
result <- fit(model, t_irregular, y_irregular)
cat("Smoothed y[0]:", result$y[1], "\n")
#> Smoothed y[0]: 12.10549Use different fractions to extract features at different scales:
library(rfastloess)
set.seed(42)
t <- seq(0, 100, length.out = 500)
trend_true <- 10 + 0.5 * t + 3 * sin(t / 10)
y <- trend_true + rnorm(length(t), sd = 3)
fractions <- c(0.05, 0.2, 0.5)
plot(t, y, col = "gray", pch = ".",
xlab = "Time", ylab = "Value", main = "Multi-Scale LOESS")
for (f in fractions) {
model <- Loess(fraction = f)
result <- fit(model, t, y)
lines(result$x, result$y, lwd = 2)
}
legend("topleft", paste0("fraction=", fractions), lty = 1)Biological application:
library(rfastloess)
set.seed(42)
hours <- seq(0, 24, by = 0.5)
expression <- 100 * (1 + 0.5 * sin(hours * pi / 12)) +
rnorm(length(hours), sd = 10)
model <- Loess(
fraction = 0.3,
iterations = 3,
outputs = "diagnostics",
intervals = intervals_opts(confidence = 0.95)
)
result <- fit(model, hours, expression)
cat("R2:", result$diagnostics$r_squared, "\n")
#> R2: 0.9096872| Data Type | Recommended Fraction | Rationale |
|---|---|---|
| Daily data (years) | 0.3–0.5 | Capture annual trends |
| Hourly data (days) | 0.1–0.2 | Capture daily patterns |
| Sensor data (minutes) | 0.05–0.1 | Preserve short-term features |
| Noisy data | Higher | Reduce noise impact |
| Clean data | Lower | Preserve detail |
vignette("use-case-real-time", package = "rfastloess")
— For streaming time seriesvignette("cross-validation", package = "rfastloess") —
Optimal fraction selectionvignette("degree", package = "rfastloess") — Degree 2
for curved trendsvignette("boundary", package = "rfastloess") — Edge
bias in trend extraction?Loess — Full parameter referencesessionInfo()
#> 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