Skip to content

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>

Download batch_smoothing.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

npm install fastloess-wasm

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 Float64Array for efficiency
  • Same API as Node.js - Consistent interface across platforms
  • Small bundle size - Optimized with wasm-opt