Streaming and online LOWESS for live data.
Overview
When data arrives continuously—from sensors, logs, or streaming pipelines—you need incremental smoothing that doesn’t require reprocessing the entire dataset.
Online Mode: Point-by-Point
For true real-time applications where each point must be processed immediately.
window_capacity = 25 limits the internal buffer to the
25 most recent observations; each add_point() call costs
O(window) rather than growing with total history.
min_points = 5 suppresses output until the window holds
enough points for a stable fit — calls made before that threshold return
NULL. update_mode = "incremental" re-fits only
the most recent point rather than the full window, halving typical
latency at a modest accuracy cost.
Sensor Data Example
library(rfastlowess)
model <- OnlineLowess(
fraction = 0.3,
window_capacity = 25,
min_points = 5,
update_mode = "incremental"
)
# Simulate real-time data arrival
count <- 0
for (i in 0:99) {
yi <- 20.0 + 5.0 * sin(i / 10.0) + sin(i * 1.7) * 0.5
result <- add_point(model, i, yi)
if (!is.null(result)) {
if (count < 5) cat(sprintf("Time %d: smoothed = %.4f\n", i, result$y))
count <- count + 1
}
}
#> Time 4: smoothed = 22.1941
#> Time 5: smoothed = 22.7964
#> Time 6: smoothed = 22.4733
#> Time 7: smoothed = 22.9120
#> Time 8: smoothed = 24.0164
cat(sprintf("... (%d more)\n", count - 5))
#> ... (91 more)Streaming Mode: Chunk Processing
For large datasets that arrive in batches or files.
chunk_size controls how many data points are processed
in one pass; matching it to your file-read buffer or message-batch size
avoids unnecessary copying. overlap retains that many
points from the previous chunk as context so the neighbourhood at chunk
boundaries is not artificially truncated.
merge_strategy = "weighted_average" blends the overlapping
region smoothly; use "last" if chunk boundaries are
guaranteed to be well separated and no blending is needed.
Always call finalize(): The streaming adapter buffers overlap data. Call
finalize()after the last chunk to retrieve the buffered tail.
Log File Processing
library(rfastlowess)
chunk1_x <- 0:49
chunk1_y <- sin(chunk1_x) + 0.1
chunk2_x <- 50:99
chunk2_y <- sin(chunk2_x) + 0.1
model <- StreamingLowess(
fraction = 0.1,
iterations = 2,
chunk_size = 50,
overlap = 10,
merge_strategy = "weighted_average"
)
# Process chunks as they arrive
process_chunk(model, chunk1_x, chunk1_y)
#> <LowessResult>
#> Points: 40
#> Fraction Used: 0.1
#> Iterations Used: 2
process_chunk(model, chunk2_x, chunk2_y)
#> <LowessResult>
#> Points: 50
#> Fraction Used: 0.1
#> Iterations Used: 2
# CRITICAL: Get buffered overlap data
result <- finalize(model)
cat(sprintf("y[0]: %.6f\n", result$y[1]))
#> y[0]: 0.518853Real-Time Dashboard Example
The dashboard pattern uses a plain LOWESS fit on a manually managed
sliding window rather than OnlineLowess. This is the
simplest approach when your UI framework already owns the data buffer
and you only need the most recent smoothed value per frame. The
trade-off is a full O(window^2) refit on every tick; for high-frequency
streams prefer OnlineLowess with
update_mode = "incremental" to bound per-frame cost.
library(rfastlowess)
n <- 100
x <- seq(0, 2 * pi, length.out = n)
y <- sin(x) + 0.1
window_x <- numeric(0)
window_y <- numeric(0)
last_smoothed <- 0
for (i in seq_along(x)) {
window_x <- c(window_x, x[i])
window_y <- c(window_y, y[i])
if (length(window_x) > 50) {
window_x <- window_x[-1]
window_y <- window_y[-1]
}
if (length(window_x) < 2) next
model <- Lowess(fraction = 0.4)
result <- fit(model, window_x, window_y)
last_smoothed <- result$y[length(result$y)]
}
cat(sprintf("Smoothed (dashboard, latest tick): %.4f\n", last_smoothed))
#> Smoothed (dashboard, latest tick): -0.0678Choosing Parameters
Performance Considerations
| Mode | Memory | Latency | Use Case |
|---|---|---|---|
| Online | Fixed (window) | ~1ms/point | Sensors, dashboards |
| Streaming | ~chunk_size | ~100ms/chunk | Large files, ETL |
| Batch | Full dataset | N/A | Analysis, reports |
See Also
- Execution Modes — Detailed mode comparison
- Merge Strategies — Chunk reconciliation in depth
- Scaling Methods — Robustness scale estimation
- Time Series — General time series analysis
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