LOWESS for trend extraction and temporal smoothing.
Time series data often contains noise, seasonality, and trends. LOWESS provides flexible trend extraction 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(rfastlowess)
n <- 500
t <- seq(0, 100, length.out = n)
i <- 0:(n - 1)
y <- 10 + 0.5 * t + 3 * sin(t / 10) + ((i * 7 + 3) %% 1.7 - 0.85) * 3
# t and y are your time series vectors
model <- Lowess(
fraction = 0.1,
iterations = 3
)
result <- fit(model, t, y)
cat(sprintf("y[0]: %.4f\n", result$y[1]))
#> y[0]: 11.3116Remove trend to analyze residual patterns.
Setting outputs = "residuals" stores
observed - smoothed alongside the smooth. A slightly wider
fraction = 0.3 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(rfastlowess)
n <- 100
t <- seq(0, 2 * pi, length.out = n)
y <- sin(t) + 0.1
model <- Lowess(
fraction = 0.3,
iterations = 3,
outputs = "residuals"
)
result <- fit(model, t, y)
trend <- result$y
detrended <- result$residuals
cat(sprintf("Trend y[0]: %.4f residual: %.4f\n", trend[1], detrended[1]))
#> Trend y[0]: 0.2582 residual: -0.1582Prediction 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(rfastlowess)
n <- 100
t <- seq(0, 2 * pi, length.out = n)
y <- sin(t) + 0.1
model <- Lowess(
fraction = 0.2,
iterations = 3,
intervals = intervals_opts(confidence = 0.90, prediction = 0.99)
)
result <- fit(model, t, y)
cat(sprintf(
"95%% PI: [%.4f, %.4f]\n",
result$prediction_lower[1], result$prediction_upper[1]
))
#> 95% PI: [0.1337, 0.3213]LOWESS naturally handles irregular time sampling:
library(rfastlowess)
t_irregular <- vapply(
0:99, function(i) i * 1.0 + (i * 31 %% 10) * 0.1, numeric(1)
)
y_irregular <- 10 + 0.3 * t_irregular + 2.0 * sin(t_irregular * 0.1)
# No special handling needed for irregular spacing
model <- Lowess(fraction = 0.2)
result <- fit(model, t_irregular, y_irregular)
cat(sprintf("y[0]: %.4f\n", result$y[1]))
#> y[0]: 11.4517Use different fractions to extract features at different scales:
library(rfastlowess)
n <- 100
t <- seq(0, 2 * pi, length.out = n)
y <- sin(t) + 0.1
scales <- c(0.05, 0.2, 0.5)
trends <- lapply(scales, function(f) {
model <- Lowess(fraction = f)
fit(model, t, y)$y
})
cat("Trend y[0] values:", vapply(trends, function(tr) tr[1], numeric(1)), "\n")
#> Trend y[0] values: 0.131712 0.2275244 0.3325414Biological application:
library(rfastlowess)
hours <- seq(0, 24, by = 0.5)
i <- seq_along(hours) - 1
expression <- 100 * (1 + 0.5 * sin(hours * pi / 12)) +
((i * 7 + 3) %% 1.7 - 0.85) * 10
model <- Lowess(
fraction = 0.3,
iterations = 3,
outputs = "diagnostics",
intervals = intervals_opts(confidence = 0.95)
)
result <- fit(model, hours, expression)
cat(sprintf("R2: %.3f\n", result$diagnostics$r_squared))
#> R2: 0.976| 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 |
?Lowess — 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] rfastlowess_5.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