WebAssembly Examples¶
Complete WebAssembly examples demonstrating fastloess-wasm for browser-based smoothing.
Batch Smoothing¶
Process complete datasets in the browser.
<!--
fastloess WASM Batch Smoothing - Comprehensive Examples
17 examples covering the full Loess API:
1. Basic smoothing
2. Robust smoothing with outliers
3. Uncertainty quantification (confidence/prediction intervals)
4. Cross-validation (K-Fold)
5. Complete diagnostic analysis
6. Different weight functions (kernels)
7. Robustness methods comparison
8. Benchmark
9. Scaling methods (MAR, MAD, Mean)
10. Boundary policies
11. Zero-weight fallback strategies
12. Polynomial degrees + iterations_used
13. Distance metrics
14. Surface modes and standard errors
15. Additional weight functions
16. LOOCV and auto-converge
17. Interpolation tuning (surface_mode effects)
-->
<!DOCTYPE html>
<html>
<head>
<title>fastloess WASM - Batch Smoothing Examples</title>
<style>
body {
font-family: monospace;
max-width: 900px;
margin: 0 auto;
padding: 20px;
background: #1e1e1e;
color: #d4d4d4;
}
h1 {
color: #569cd6;
}
#output {
white-space: pre-wrap;
font-family: 'Consolas', 'Monaco', monospace;
}
</style>
</head>
<body>
<h1>fastloess WASM - Batch Smoothing Examples</h1>
<div id="output">Initializing...</div>
<script type="module">
import init, { Loess } from "../../bindings/wasm/pkg-web/fastloess_wasm.js";
const out = document.getElementById('output');
function log(msg) { out.innerText += msg + "\n"; console.log(msg); }
function clearLog() { out.innerText = ""; }
function makeLinear(n) {
const x = new Float64Array(n), y = new Float64Array(n);
for (let i = 0; i < n; i++) { x[i] = i; y[i] = 2 * i + 1; }
return { x, y };
}
// ── Example 1: Basic Smoothing ────────────────────────────────────────
function example_1_basic_smoothing() {
log("Example 1: Basic Smoothing");
const x = new Float64Array([1, 2, 3, 4, 5]);
const y = new Float64Array([2.0, 4.1, 5.9, 8.2, 9.8]);
const r = new Loess({ fraction: 0.5, iterations: 3 }).fit(x, y);
log(` fraction_used=${r.fraction_used}`);
log(` Smoothed: [${Array.from(r.y).map(v => v.toFixed(3)).join(', ')}]`);
log("");
}
// ── Example 2: Robust Smoothing with Outliers ─────────────────────────
function example_2_robust_with_outliers() {
log("Example 2: Robust Smoothing with Outliers");
const x = new Float64Array([1, 2, 3, 4, 5, 6, 7, 8]);
const y = new Float64Array([2.1, 4.0, 5.9, 25.0, 10.1, 12.0, 14.1, 15.9]);
const r = new Loess({
fraction: 0.5, iterations: 5, robustness_method: "bisquare",
return_robustness_weights: true, return_residuals: true
}).fit(x, y);
const w = r.robustness_weights;
if (w) for (let i = 0; i < w.length; i++) {
if (w[i] < 0.5) log(` Outlier at index ${i} (y=${y[i]}): weight=${w[i].toFixed(3)}`);
}
log(` Smoothed: [${Array.from(r.y).map(v => v.toFixed(2)).join(', ')}]`);
log("");
}
// ── Example 3: Uncertainty Quantification ─────────────────────────────
function example_3_uncertainty_quantification() {
log("Example 3: Uncertainty Quantification");
const x = new Float64Array([1, 2, 3, 4, 5, 6, 7, 8]);
const y = new Float64Array([2.1, 3.8, 6.2, 7.9, 10.3, 11.8, 14.1, 15.7]);
const r = new Loess({
fraction: 0.5, iterations: 3,
confidence_intervals: 0.95, prediction_intervals: 0.95
}).fit(x, y);
log(" x\ty_smooth\tconf_low\tconf_high\tpred_low\tpred_high");
for (let i = 0; i < r.y.length; i++) {
log(` ${r.x[i].toFixed(0)}\t${r.y[i].toFixed(4)}\t${r.confidence_lower[i].toFixed(4)}\t${r.confidence_upper[i].toFixed(4)}\t${r.prediction_lower[i].toFixed(4)}\t${r.prediction_upper[i].toFixed(4)}`);
}
log("");
}
// ── Example 4: Cross-Validation ───────────────────────────────────────
function example_4_cross_validation() {
log("Example 4: Cross-Validation");
const n = 20, x = new Float64Array(n), y = new Float64Array(n);
for (let i = 0; i < n; i++) { x[i] = i + 1; y[i] = 2 * x[i] + 1 + Math.sin(x[i] * 0.5); }
const r = new Loess({
cv_fractions: [0.2, 0.3, 0.5, 0.7], cv_method: "kfold", cv_k: 5,
iterations: 2, return_diagnostics: true
}).fit(x, y);
log(` Selected fraction: ${r.fraction_used}`);
if (r.cv_scores) {
const fs = [0.2, 0.3, 0.5, 0.7];
log(" CV Scores:");
for (let i = 0; i < fs.length; i++) log(` fraction=${fs[i]}: ${r.cv_scores[i].toFixed(4)}`);
}
log("");
}
// ── Example 5: Complete Diagnostic Analysis ───────────────────────────
function example_5_complete_diagnostics() {
log("Example 5: Complete Diagnostic Analysis");
const x = new Float64Array([1, 2, 3, 4, 5, 6, 7, 8]);
const y = new Float64Array([2.1, 3.8, 6.2, 7.9, 10.3, 11.8, 14.1, 15.7]);
const r = new Loess({
fraction: 0.5, iterations: 3, confidence_intervals: 0.95,
prediction_intervals: 0.95, return_diagnostics: true,
return_residuals: true, return_robustness_weights: true
}).fit(x, y);
const d = r.diagnostics;
if (d) {
log(" Diagnostics:");
log(` RMSE: ${d.rmse.toFixed(6)}`);
log(` MAE: ${d.mae.toFixed(6)}`);
log(` R²: ${d.r_squared.toFixed(6)}`);
log(` Residual SD: ${d.residual_sd.toFixed(6)}`);
if (d.aic != null) log(` AIC: ${d.aic.toFixed(2)}`);
if (d.aicc != null) log(` AICc: ${d.aicc.toFixed(2)}`);
if (d.effective_df != null) log(` Eff. DF: ${d.effective_df.toFixed(2)}`);
}
log(` smoothed[0]: ${r.y[0].toFixed(5)}`);
if (r.residuals) log(` residuals[0]: ${r.residuals[0].toFixed(5)}`);
if (r.robustness_weights) log(` robWeight[0]: ${r.robustness_weights[0].toFixed(4)}`);
log("");
}
// ── Example 6: Different Weight Functions ─────────────────────────────
function example_6_different_kernels() {
log("Example 6: Different Weight Functions (Kernels)");
const x = new Float64Array([1, 2, 3, 4, 5]);
const y = new Float64Array([2.0, 4.1, 5.9, 8.2, 9.8]);
for (const k of ["tricube", "epanechnikov", "gaussian", "biweight"]) {
const r = new Loess({ fraction: 0.5, weight_function: k }).fit(x, y);
log(` ${k}: [${Array.from(r.y).map(v => v.toFixed(3)).join(', ')}]`);
}
log("");
}
// ── Example 7: Robustness Methods ─────────────────────────────────────
function example_7_robustness_methods() {
log("Example 7: Robustness Methods Comparison");
const x = new Float64Array([1, 2, 3, 4, 5]);
const y = new Float64Array([2.0, 4.1, 20.0, 8.2, 9.8]);
for (const m of ["bisquare", "huber", "talwar"]) {
const r = new Loess({
fraction: 0.5, iterations: 5, robustness_method: m,
return_robustness_weights: true
}).fit(x, y);
const ws = r.robustness_weights ? Array.from(r.robustness_weights).map(v => v.toFixed(3)).join(', ') : 'N/A';
log(` ${m}:`);
log(` Smoothed: [${Array.from(r.y).map(v => v.toFixed(2)).join(', ')}]`);
log(` Weights: [${ws}]`);
}
log("");
}
// ── Example 8: Benchmark ──────────────────────────────────────────────
function example_8_benchmark() {
log("Example 8: Benchmark");
const n = 1000, x = new Float64Array(n), y = new Float64Array(n);
for (let i = 0; i < n; i++) { x[i] = i; y[i] = Math.sin(i * 0.1) + Math.cos(i * 0.01); }
const t0 = performance.now();
const r = new Loess({ parallel: true }).fit(x, y);
const ms = (performance.now() - t0).toFixed(2);
log(` ${n} points in ${ms}ms, fraction_used=${r.fraction_used}, y[0]=${r.y[0].toFixed(4)}`);
log("");
}
// ── Example 9: Scaling Methods ────────────────────────────────────────
function example_9_scaling_methods() {
log("Example 9: Scaling Methods (MAR, MAD, Mean)");
const { x, y } = makeLinear(20);
for (const m of ["mar", "mad", "mean"]) {
const r = new Loess({ fraction: 0.5, scaling_method: m }).fit(x, y);
log(` ${m}: y[0]=${r.y[0].toFixed(3)}`);
}
log("");
}
// ── Example 10: Boundary Policies ────────────────────────────────────
function example_10_boundary_policies() {
log("Example 10: Boundary Policies");
const { x, y } = makeLinear(30);
for (const p of ["extend", "reflect", "zero", "noboundary"]) {
const r = new Loess({ fraction: 0.5, boundary_policy: p }).fit(x, y);
log(` ${p}: first=${r.y[0].toFixed(2)}, last=${r.y[r.y.length - 1].toFixed(2)}`);
}
log("");
}
// ── Example 11: Zero-Weight Fallback ─────────────────────────────────
function example_11_zero_weight_fallback() {
log("Example 11: Zero-Weight Fallback Strategies");
const { x, y } = makeLinear(20);
for (const fb of ["use_local_mean", "return_original", "return_none"]) {
const r = new Loess({ fraction: 0.5, zero_weight_fallback: fb }).fit(x, y);
log(` ${fb}: y[0]=${r.y[0].toFixed(3)}`);
}
log("");
}
// ── Example 12: Polynomial Degrees ────────────────────────────────────
function example_12_polynomial_degrees() {
log("Example 12: Polynomial Degrees + iterations_used");
const { x, y } = makeLinear(30);
for (const deg of ["constant", "linear", "quadratic", "cubic", "quartic"]) {
const r = new Loess({ fraction: 0.5, iterations: 2, degree: deg }).fit(x, y);
log(` ${deg}: y[0]=${r.y[0].toFixed(3)}, iterations_used=${r.iterations_used}`);
}
log("");
}
// ── Example 13: Distance Metrics ──────────────────────────────────────
function example_13_distance_metrics() {
log("Example 13: Distance Metrics");
const { x, y } = makeLinear(20);
for (const m of ["euclidean", "normalized", "manhattan", "chebyshev"]) {
const r = new Loess({ fraction: 0.5, distance_metric: m }).fit(x, y);
log(` ${m}: y[0]=${r.y[0].toFixed(3)}`);
}
// Minkowski with custom p via "minkowski:p" format
const rMink = new Loess({ fraction: 0.5, distance_metric: "minkowski:3" }).fit(x, y);
log(` minkowski(p=3): y[0]=${rMink.y[0].toFixed(3)}`);
log("");
}
// ── Example 14: Surface Modes and Standard Errors ─────────────────────
// Note: enp, trace_hat, delta1, delta2, residual_scale, leverage are not
// exposed by the WASM binding (LoessResult). Use the Node.js binding
// for full hat-matrix statistics.
function example_14_surface_modes_and_se() {
log("Example 14: Surface Modes and Standard Errors");
const { x, y } = makeLinear(30);
const rDirect = new Loess({
fraction: 0.5, surface_mode: "direct",
return_se: true, confidence_intervals: 0.95,
prediction_intervals: 0.95
}).fit(x, y);
log(" surface_mode=direct:");
log(` confidence_lower non-null: ${rDirect.confidence_lower != null}`);
log(` prediction_lower non-null: ${rDirect.prediction_lower != null}`);
if (rDirect.standard_errors) log(` standard_errors[0]: ${rDirect.standard_errors[0].toFixed(4)}`);
const rInterp = new Loess({
fraction: 0.5, surface_mode: "interpolation",
return_se: true
}).fit(x, y);
log(" surface_mode=interpolation:");
log(` y[0]: ${rInterp.y[0].toFixed(3)}`);
if (rInterp.standard_errors) log(` standard_errors[0]: ${rInterp.standard_errors[0].toFixed(4)}`);
log("");
}
// ── Example 15: Additional Weight Functions ───────────────────────────
function example_15_additional_kernels() {
log("Example 15: Additional Weight Functions (Uniform, Triangle, Cosine)");
const x = new Float64Array([1, 2, 3, 4, 5]);
const y = new Float64Array([2.0, 4.1, 5.9, 8.2, 9.8]);
for (const k of ["uniform", "triangle", "cosine"]) {
const r = new Loess({ fraction: 0.5, weight_function: k }).fit(x, y);
log(` ${k}: [${Array.from(r.y).map(v => v.toFixed(3)).join(', ')}]`);
}
log("");
}
// ── Example 16: LOOCV, K-Fold, and Auto-Converge ─────────────────────
function example_16_loocv_and_auto_converge() {
log("Example 16: LOOCV, K-Fold, and Auto-Converge");
const n = 20, x = new Float64Array(n), y = new Float64Array(n);
for (let i = 0; i < n; i++) { x[i] = i + 1; y[i] = 2 * x[i] + 1 + Math.sin(x[i] * 0.5); }
const rLoocv = new Loess({ cv_fractions: [0.3, 0.5, 0.7], cv_method: "loocv" }).fit(x, y);
log(` LOOCV selected fraction: ${rLoocv.fraction_used}`);
if (rLoocv.cv_scores) log(` LOOCV scores: [${Array.from(rLoocv.cv_scores).map(v => v.toFixed(4)).join(', ')}]`);
const rKfold = new Loess({ cv_fractions: [0.2, 0.4, 0.6], cv_method: "kfold", cv_k: 5 }).fit(x, y);
log(` KFold(k=5) selected fraction: ${rKfold.fraction_used}`);
if (rKfold.cv_scores) log(` KFold scores: [${Array.from(rKfold.cv_scores).map(v => v.toFixed(4)).join(', ')}]`);
const rAc = new Loess({ fraction: 0.5, auto_converge: 1e-4 }).fit(x, y);
log(` auto_converge=1e-4: iterations_used=${rAc.iterations_used}`);
log("");
}
// ── Example 17: Interpolation Tuning (surface_mode effects) ────────────
function example_17_interpolation_tuning() {
log("Example 17: Interpolation Tuning (surface_mode effects)");
const n = 50, { x, y } = makeLinear(n);
const rInterp = new Loess({ fraction: 0.5, surface_mode: "interpolation" }).fit(x, y);
log(` interpolation: y[0]=${rInterp.y[0].toFixed(3)}, y[-1]=${rInterp.y[n - 1].toFixed(3)}`);
const rDirect = new Loess({ fraction: 0.5, surface_mode: "direct" }).fit(x, y);
log(` direct: y[0]=${rDirect.y[0].toFixed(3)}, y[-1]=${rDirect.y[n - 1].toFixed(3)}`);
for (const frac of [0.2, 0.5, 0.8]) {
const r = new Loess({ fraction: frac, surface_mode: "direct" }).fit(x, y);
log(` direct fraction=${frac}: y[0]=${r.y[0].toFixed(3)}`);
}
// return_se works with interpolation mode too (fraction_used/iterations_used)
const rSe = new Loess({ fraction: 0.5, surface_mode: "interpolation", return_se: true }).fit(x, y);
log(` interpolation+SE: fraction_used=${rSe.fraction_used}, iterations_used=${rSe.iterations_used}`);
log("");
}
async function runDemo() {
try {
clearLog();
await init();
log("=".repeat(60));
log("fastloess WASM Batch Smoothing - Comprehensive Examples");
log("=".repeat(60));
log("");
example_1_basic_smoothing();
example_2_robust_with_outliers();
example_3_uncertainty_quantification();
example_4_cross_validation();
example_5_complete_diagnostics();
example_6_different_kernels();
example_7_robustness_methods();
example_8_benchmark();
example_9_scaling_methods();
example_10_boundary_policies();
example_11_zero_weight_fallback();
example_12_polynomial_degrees();
example_13_distance_metrics();
example_14_surface_modes_and_se();
example_15_additional_kernels();
example_16_loocv_and_auto_converge();
example_17_interpolation_tuning();
log("=== Batch Smoothing Examples Complete ===");
} catch (e) {
log(`Error: ${e}`);
console.error(e);
}
}
runDemo();
</script>
</body>
</html>
Streaming Smoothing¶
Process large datasets in memory-efficient chunks in the browser.
<!--
fastloess WASM Streaming Smoothing - Comprehensive Examples
9 examples covering the full StreamingLoessWasm API:
1. Basic chunked processing
2. Chunk size comparison
3. Overlap strategies
4. Large dataset processing
5. Outlier handling in streaming mode
6. File-based streaming simulation
7. Benchmark (sequential streaming)
8. Merge strategies
9. Advanced streaming options
-->
<!DOCTYPE html>
<html>
<head>
<title>fastloess WASM - Streaming Smoothing Examples</title>
<style>
body {
font-family: monospace;
max-width: 900px;
margin: 0 auto;
padding: 20px;
background: #1e1e1e;
color: #d4d4d4;
}
h1 {
color: #569cd6;
}
#output {
white-space: pre-wrap;
}
</style>
</head>
<body>
<h1>fastloess WASM - Streaming Smoothing Examples</h1>
<div id="output">Initializing...</div>
<script type="module">
import init, { StreamingLoessWasm } from "../../bindings/wasm/pkg-web/fastloess_wasm.js";
const out = document.getElementById('output');
function log(msg) { out.innerText += msg + "\n"; console.log(msg); }
function clearLog() { out.innerText = ""; }
function makeLinear(n) {
const x = new Float64Array(n), y = new Float64Array(n);
for (let i = 0; i < n; i++) { x[i] = i; y[i] = 2 * i + 1; }
return { x, y };
}
// ── Example 1: Basic Chunked Processing ───────────────────────────────
function example_1_basic_chunked_processing() {
log("Example 1: Basic Chunked Processing");
const n = 50, { x, y } = makeLinear(n);
const chunk_size = 15, overlap = 5;
const s = new StreamingLoessWasm(
{ fraction: 0.5, iterations: 2, return_residuals: true },
{ chunk_size, overlap }
);
log(` Dataset: ${n} pts, chunk=${chunk_size}, overlap=${overlap}`);
let total = 0, ci = 0;
for (let start = 0; start < n; start += chunk_size - overlap) {
const end = Math.min(start + chunk_size, n);
const res = s.processChunk(x.subarray(start, end), y.subarray(start, end));
if (res.x.length > 0) {
total += res.x.length;
log(` Chunk ${ci}: ${res.x.length} pts (x: ${res.x[0].toFixed(0)}..${res.x[res.x.length - 1].toFixed(0)})`);
}
ci++;
}
const fin = s.finalize();
if (fin.x.length > 0) { total += fin.x.length; log(` Finalize: ${fin.x.length} remaining`); }
log(` Total: ${total}/${n}`);
log("");
}
// ── Example 2: Chunk Size Comparison ──────────────────────────────────
function example_2_chunk_size_comparison() {
log("Example 2: Chunk Size Comparison");
const n = 100, { x, y } = makeLinear(n);
for (const [cs, ov, label] of [[20, 5, "Small"], [50, 10, "Medium"], [80, 15, "Large"]]) {
const s = new StreamingLoessWasm({ fraction: 0.5, iterations: 1 }, { chunk_size: cs, overlap: ov });
let chunks = 0, total = 0;
for (let start = 0; start < n; start += cs - ov) {
const end = Math.min(start + cs, n);
const res = s.processChunk(x.subarray(start, end), y.subarray(start, end));
if (res.x.length > 0) { chunks++; total += res.x.length; }
}
const fin = s.finalize();
if (fin.x.length > 0) { chunks++; total += fin.x.length; }
log(` ${label} (size=${cs}, overlap=${ov}): chunks=${chunks}, total=${total}`);
}
log("");
}
// ── Example 3: Overlap Strategies ─────────────────────────────────────
function example_3_overlap_strategies() {
log("Example 3: Overlap Strategies");
const n = 100, { x, y } = makeLinear(n);
for (const [overlap, label] of [[0, "No overlap"], [10, "10-pt overlap"], [20, "20-pt overlap"]]) {
const cs = 40;
const s = new StreamingLoessWasm({ fraction: 0.5 }, { chunk_size: cs, overlap });
let total = 0;
const step = cs - overlap;
// Feed only full-size chunks; finalize() handles remaining data
for (let start = 0; start + cs <= n; start += step) {
total += s.processChunk(x.subarray(start, start + cs), y.subarray(start, start + cs)).x.length;
}
total += s.finalize().x.length;
log(` ${label}: total output=${total}`);
}
log("");
}
// ── Example 4: Large Dataset Processing ───────────────────────────────
function example_4_large_dataset_processing() {
log("Example 4: Large Dataset Processing");
const n = 10000;
const x = new Float64Array(n), y = new Float64Array(n);
for (let i = 0; i < n; i++) { x[i] = i; y[i] = Math.sin(i * 0.01) + i * 0.001; }
const cs = 500, ov = 50;
const s = new StreamingLoessWasm({ fraction: 0.05, iterations: 2 }, { chunk_size: cs, overlap: ov });
let total = 0;
const step = cs - ov;
for (let start = 0; start < n; start += step) {
const end = Math.min(start + cs, n);
total += s.processChunk(x.subarray(start, end), y.subarray(start, end)).x.length;
if (total > 0 && total % 2000 < step) log(` Progress: ~${total} pts smoothed`);
}
total += s.finalize().x.length;
log(` Total: ${total}/${n}, memory: constant (chunk=${cs})`);
log("");
}
// ── Example 5: Outlier Handling ───────────────────────────────────────
function example_5_outlier_handling() {
log("Example 5: Outlier Handling in Streaming Mode");
const n = 100;
const x = new Float64Array(n), y = new Float64Array(n);
for (let i = 0; i < n; i++) {
x[i] = i; y[i] = 2 * i + 1 + Math.sin(i * 0.2) * 2;
if (i === 25 || i === 50 || i === 75) y[i] += 50;
}
for (const m of ["bisquare", "huber", "talwar"]) {
const s = new StreamingLoessWasm(
{ fraction: 0.5, iterations: 5, robustness_method: m, return_residuals: true },
{ chunk_size: 30, overlap: 10 }
);
let large = 0;
for (let start = 0; start < n; start += 20) {
const end = Math.min(start + 30, n);
const res = s.processChunk(x.subarray(start, end), y.subarray(start, end));
if (res.residuals) for (const r of res.residuals) if (Math.abs(r) > 10) large++;
}
const fin = s.finalize();
if (fin.residuals) for (const r of fin.residuals) if (Math.abs(r) > 10) large++;
log(` ${m}: pts with |residual|>10: ${large}`);
}
log("");
}
// ── Example 6: File-Based Streaming Simulation ────────────────────────
function example_6_file_simulation() {
log("Example 6: File-Based Streaming Simulation");
log(" Simulating: input.csv -> Smooth -> output.csv");
const total = 200, cs = 50, ov = 10;
const s = new StreamingLoessWasm(
{ fraction: 0.5, iterations: 2, return_residuals: true },
{ chunk_size: cs, overlap: ov }
);
let out_count = 0;
for (let ci = 0; ci < Math.ceil(total / (cs - ov)); ci++) {
const start = ci * (cs - ov), end = Math.min(start + cs, total);
const xc = new Float64Array(end - start), yc = new Float64Array(end - start);
for (let j = 0; j < end - start; j++) { xc[j] = start + j; yc[j] = 2 * xc[j] + 1 + Math.sin(xc[j] * 0.1) * 3; }
log(` Reading chunk ${ci} (lines ${start}..${end - 1})`);
const res = s.processChunk(xc, yc);
if (res.x.length > 0) { out_count += res.x.length; log(` -> Writing ${res.x.length} pts (total: ${out_count})`); }
}
const fin = s.finalize();
if (fin.x.length > 0) { out_count += fin.x.length; log(` Finalizing: ${fin.x.length} remaining`); }
log(` Input: ${total}, Output: ${out_count}`);
log("");
}
// ── Example 7: Benchmark ──────────────────────────────────────────────
function example_7_benchmark() {
log("Example 7: Benchmark (Sequential Streaming)");
const n = 1000, cs = 100, ov = 10;
const s = new StreamingLoessWasm({ fraction: 0.5, iterations: 3 }, { chunk_size: cs, overlap: ov });
const t0 = performance.now();
let total = 0;
for (let start = 0; start < n; start += cs - ov) {
const end = Math.min(start + cs, n);
const xc = new Float64Array(end - start), yc = new Float64Array(end - start);
for (let j = 0; j < end - start; j++) { xc[j] = start + j; yc[j] = Math.sin(xc[j] * 0.1) + Math.cos(xc[j] * 0.01); }
total += s.processChunk(xc, yc).x.length;
}
total += s.finalize().x.length;
log(` ${total} pts in ${(performance.now() - t0).toFixed(2)}ms (chunk=${cs}, overlap=${ov})`);
log("");
}
// ── Example 8: Merge Strategies ───────────────────────────────────────
function example_8_merge_strategies() {
log("Example 8: Merge Strategies");
const n = 50, { x, y } = makeLinear(n);
for (const ms of ["average", "weighted_average", "take_first", "take_last"]) {
const s = new StreamingLoessWasm(
{ fraction: 0.5, iterations: 2 },
{ chunk_size: 20, overlap: 5, merge_strategy: ms }
);
let total = 0;
for (let start = 0; start < n; start += 15) {
const end = Math.min(start + 20, n);
total += s.processChunk(x.subarray(start, end), y.subarray(start, end)).x.length;
}
total += s.finalize().x.length;
log(` ${ms}: total=${total}`);
}
log("");
}
// ── Example 9: Advanced Streaming Options ─────────────────────────────
function example_9_advanced_options() {
log("Example 9: Advanced Streaming Options");
const n = 50, { x, y } = makeLinear(n);
const s = new StreamingLoessWasm(
{
fraction: 0.5, iterations: 2, degree: "quadratic",
scaling_method: "mar", boundary_policy: "reflect",
zero_weight_fallback: "return_original", distance_metric: "manhattan",
surface_mode: "direct", return_se: true, return_diagnostics: true,
return_robustness_weights: true, auto_converge: 1e-3,
},
{ chunk_size: 20, overlap: 5 }
);
let total = 0;
for (let start = 0; start < n; start += 15) {
const end = Math.min(start + 20, n);
total += s.processChunk(x.subarray(start, end), y.subarray(start, end)).x.length;
}
const fin = s.finalize(); total += fin.x.length;
log(` total pts: ${total}`);
if (fin.standard_errors && fin.standard_errors.length > 0)
log(` standard_errors[0]: ${fin.standard_errors[0].toFixed(4)}`);
if (fin.diagnostics) {
log(` diagnostics.rmse: ${fin.diagnostics.rmse.toFixed(3)}`);
log(` diagnostics.r_squared: ${fin.diagnostics.r_squared.toFixed(3)}`);
if (fin.diagnostics.aic != null) log(` diagnostics.aic: ${fin.diagnostics.aic.toFixed(3)}`);
}
if (fin.robustness_weights && fin.robustness_weights.length > 0)
log(` robustness_weights[0]: ${fin.robustness_weights[0].toFixed(4)}`);
log("");
}
async function runDemo() {
try {
clearLog();
await init();
log("=".repeat(60));
log("fastloess WASM Streaming Smoothing - Comprehensive Examples");
log("=".repeat(60));
log("");
example_1_basic_chunked_processing();
example_2_chunk_size_comparison();
example_3_overlap_strategies();
example_4_large_dataset_processing();
example_5_outlier_handling();
example_6_file_simulation();
example_7_benchmark();
example_8_merge_strategies();
example_9_advanced_options();
log("=== Streaming Smoothing Examples Complete ===");
} catch (e) {
log(`Error: ${e}`);
console.error(e);
}
}
runDemo();
</script>
</body>
</html>
Download streaming_smoothing.html
Online Smoothing¶
Real-time smoothing with sliding window for browser applications.
<!--
fastloess WASM Online Smoothing - Comprehensive Examples
9 examples covering the full OnlineLoessWasm API:
1. Basic incremental processing
2. Real-time sensor data simulation
3. Outlier handling in online mode
4. Window size comparison
5. Memory-bounded processing (embedded systems)
6. Sliding window behavior
7. Benchmark (sequential online)
8. Update modes (Full vs Incremental) and min_points
9. Advanced online options
add_point(x, y) returns an OnlineOutput once the window has enough points,
or undefined while filling. The helper below returns the smoothed value or
falls back to the raw y value while the window is still filling.
-->
<!DOCTYPE html>
<html>
<head>
<title>fastloess WASM - Online Smoothing Examples</title>
<style>
body {
font-family: monospace;
max-width: 900px;
margin: 0 auto;
padding: 20px;
background: #1e1e1e;
color: #d4d4d4;
}
h1 {
color: #569cd6;
}
#output {
white-space: pre-wrap;
}
</style>
</head>
<body>
<h1>fastloess WASM - Online Smoothing Examples</h1>
<div id="output">Initializing...</div>
<script type="module">
import init, { OnlineLoessWasm } from "../../bindings/wasm/pkg-web/fastloess_wasm.js";
const out = document.getElementById('output');
function log(msg) { out.innerText += msg + "\n"; console.log(msg); }
function clearLog() { out.innerText = ""; }
// add_point() returns OnlineOutput (with .smoothed) or undefined while filling.
// This helper returns smoothed or falls back to raw y.
function addPoint(model, x, y) {
const result = model.add_point(x, y);
return result !== undefined ? result.smoothed : y;
}
// ── Example 1: Basic Incremental Processing ───────────────────────────
function example_1_basic_streaming() {
log("Example 1: Basic Incremental Processing");
const data = [[1, 3.1], [2, 5.0], [3, 7.2], [4, 8.9], [5, 11.1],
[6, 13.0], [7, 15.2], [8, 16.8], [9, 19.1], [10, 21.0]];
const model = new OnlineLoessWasm(
{ fraction: 0.5, iterations: 2 },
{ window_capacity: 5 }
);
log(` ${"X".padStart(8)} ${"Y_obs".padStart(12)} ${"Y_smooth".padStart(12)}`);
for (const [x, y] of data) {
const smoothed = addPoint(model, x, y);
log(` ${x.toFixed(2).padStart(8)} ${y.toFixed(2).padStart(12)} ${smoothed.toFixed(2).padStart(12)}`);
}
log("");
}
// ── Example 2: Real-Time Sensor Data Simulation ───────────────────────
function example_2_sensor_data_simulation() {
log("Example 2: Real-Time Sensor Data Simulation");
log(" Simulating temperature sensor with noise...");
const model = new OnlineLoessWasm(
{ fraction: 0.4, iterations: 3, robustness_method: "bisquare" },
{ window_capacity: 12 }
);
log(` ${"Hour".padStart(6)} ${"Raw".padStart(12)} ${"Smoothed".padStart(12)}`);
for (let hour = 0; hour < 24; hour++) {
const temp = 20 + 5 * Math.sin(hour * Math.PI / 12) + ((hour * 7) % 11) * 0.3 - 1.5;
const s = addPoint(model, hour, temp);
log(` ${hour.toString().padStart(6)} ${temp.toFixed(2).padStart(12)}°C ${s.toFixed(2).padStart(12)}°C`);
}
log("");
}
// ── Example 3: Outlier Handling ───────────────────────────────────────
function example_3_outlier_handling() {
log("Example 3: Outlier Handling in Online Mode");
const data = [[1, 2.0], [2, 4.1], [3, 5.9], [4, 25.0], [5, 10.1],
[6, 12.0], [7, 14.1], [8, 50.0], [9, 18.0], [10, 20.1]];
for (const method of ["bisquare", "talwar"]) {
const model = new OnlineLoessWasm(
{ fraction: 0.5, iterations: 5, robustness_method: method },
{ window_capacity: 6 }
);
const smoothed = [];
for (const [x, y] of data) {
smoothed.push(addPoint(model, x, y).toFixed(1));
}
log(` ${method}: [${smoothed.join(', ')}]`);
}
log("");
}
// ── Example 4: Window Size Comparison ────────────────────────────────
function example_4_window_size_comparison() {
log("Example 4: Window Size Comparison");
const data = Array.from({ length: 20 }, (_, i) => [i + 1, 2 * (i + 1) + Math.sin((i + 1) * 0.5) * 3]);
for (const w of [5, 10, 15]) {
const model = new OnlineLoessWasm({ fraction: 0.5, iterations: 2 }, { window_capacity: w });
const smoothed = [];
for (const [x, y] of data) { smoothed.push(addPoint(model, x, y)); }
const last5 = smoothed.slice(-5).map(v => v.toFixed(2));
log(` window_capacity=${w}: last 5 = [${last5.join(', ')}]`);
}
log("");
}
// ── Example 5: Memory-Bounded Processing ─────────────────────────────
function example_5_memory_bounded_processing() {
log("Example 5: Memory-Bounded Processing (Embedded Systems)");
const total = 1000;
const model = new OnlineLoessWasm({ fraction: 0.3, iterations: 1 }, { window_capacity: 20 });
let count = 0, last = 0;
for (let i = 0; i < total; i++) {
const s = addPoint(model, i, 2 * i + Math.sin(i * 0.1) * 5 + ((i % 7) - 3) * 0.5);
count++; last = s; if (count % 200 === 0) log(` Processed: ${count} pts | smoothed=${last.toFixed(2)}`);
}
log(` Total: ${count}, final smoothed: ${last.toFixed(2)}`);
log(` Memory: constant (window=20)`);
log("");
}
// ── Example 6: Sliding Window Behavior ───────────────────────────────
function example_6_sliding_window_behavior() {
log("Example 6: Sliding Window Behavior");
const data = [[1, 2], [2, 4], [3, 6], [4, 8], [5, 10], [6, 12], [7, 14], [8, 16]];
const model = new OnlineLoessWasm({ fraction: 0.6, iterations: 0 }, { window_capacity: 4 });
log(` ${"Pt".padStart(4)} ${"X".padStart(4)} ${"Y".padStart(6)} ${"Smoothed".padStart(10)} Status`);
data.forEach(([x, y], i) => {
const s = addPoint(model, x, y);
log(` ${(i + 1).toString().padStart(4)} ${x.toString().padStart(4)} ${y.toString().padStart(6)} ${s.toFixed(2).padStart(10)} Sliding window`);
});
log(" Output starts after window fills, then slides.");
log("");
}
// ── Example 7: Benchmark ──────────────────────────────────────────────
function example_7_benchmark() {
log("Example 7: Benchmark (Sequential Online)");
const n = 1000;
const model = new OnlineLoessWasm({ fraction: 0.5, iterations: 3 }, { window_capacity: 10 });
const t0 = performance.now();
let count = 0;
for (let i = 0; i < n; i++) {
addPoint(model, i, Math.sin(i * 0.1) + Math.cos(i * 0.01));
count++;
}
log(` ${count} pts in ${(performance.now() - t0).toFixed(2)}ms (window_capacity=10)`);
log("");
}
// ── Example 8: Update Modes (Full vs Incremental) and min_points ───────
function example_8_update_modes() {
log("Example 8: Update Modes (Full vs Incremental) and min_points");
const data = Array.from({ length: 30 }, (_, i) => [i, 2 * i + 1]);
for (const mode of ["full", "incremental"]) {
const model = new OnlineLoessWasm(
{ fraction: 0.5, iterations: 2 },
{ window_capacity: 15, min_points: 5, update_mode: mode }
);
let lastVal;
for (const [x, y] of data) { lastVal = addPoint(model, x, y); }
log(` ${mode}: last smoothed = ${lastVal.toFixed(3)} (${data.length} pts processed)`);
}
// Show effect of min_points: early points return raw y until min_points reached
for (const mp of [2, 5, 10]) {
const model = new OnlineLoessWasm({ fraction: 0.5 }, { window_capacity: 15, min_points: mp });
let lastVal;
for (const [x, y] of data) { lastVal = addPoint(model, x, y); }
log(` min_points=${mp}: last smoothed = ${lastVal.toFixed(3)}`);
}
log("");
}
// ── Example 9: Advanced Online Options ────────────────────────────────
function example_9_advanced_online_options() {
log("Example 9: Advanced Online Options");
const data = Array.from({ length: 30 }, (_, i) => [i, 2 * i + 1]);
const model = new OnlineLoessWasm(
{
fraction: 0.5, iterations: 2, degree: "quadratic",
scaling_method: "mar", boundary_policy: "reflect",
zero_weight_fallback: "return_original", distance_metric: "chebyshev",
auto_converge: 1e-3, return_residuals: true, return_robustness_weights: true,
},
{ window_capacity: 15, min_points: 5 }
);
let lastVal;
for (const [x, y] of data) { lastVal = addPoint(model, x, y); }
log(` emitted: ${data.length}, last smoothed: ${lastVal != null ? lastVal.toFixed(3) : "N/A"}`);
log("");
}
async function runDemo() {
try {
clearLog();
await init();
log("=".repeat(60));
log("fastloess WASM Online Smoothing - Comprehensive Examples");
log("=".repeat(60));
log("");
example_1_basic_streaming();
example_2_sensor_data_simulation();
example_3_outlier_handling();
example_4_window_size_comparison();
example_5_memory_bounded_processing();
example_6_sliding_window_behavior();
example_7_benchmark();
example_8_update_modes();
example_9_advanced_online_options();
log("=== Online Smoothing Examples Complete ===");
} catch (e) {
log(`Error: ${e}`);
console.error(e);
}
}
runDemo();
</script>
</body>
</html>
Download online_smoothing.html
Installation¶
NPM¶
CDN¶
<script type="module">
import init, { Loess } from 'https://unpkg.com/fastloess-wasm@latest';
await init();
// Ready to use
</script>
Quick Start¶
Browser (ES Modules)¶
import init, { Loess } from 'fastloess-wasm';
async function main() {
// Initialize WASM module
await init();
// Generate sample data
const x = Float64Array.from({ length: 100 }, (_, i) => i * 0.1);
const y = Float64Array.from(x, xi => Math.sin(xi) + Math.random() * 0.2);
// Basic smoothing
const model = new Loess({ fraction: 0.3 });
const result = model.fit(x, y);
console.log('Smoothed values:', result.y);
// With options
const resultWithOptions = new Loess({
fraction: 0.3,
iterations: 3,
confidence_intervals: 0.95,
return_diagnostics: true
}).fit(x, y);
console.log('R²:', resultWithOptions.diagnostics?.r_squared);
}
main();
Node.js¶
const { Loess } = require('fastloess-wasm');
// Same API as browser
const model = new Loess({ fraction: 0.3 });
const result = model.fit(x, y);
Features¶
The WebAssembly bindings provide:
- Zero dependencies - Pure WASM, no runtime requirements
- TypedArray support - Works with
Float64Arrayfor efficiency - Same API as Node.js - Consistent interface across platforms
- Small bundle size - Optimized with
wasm-opt