Skip to content

Quick Start

Get up and running with LOWESS in minutes.

Smooth a noisy sine wave — the kind of signal where LOWESS shines. Each example recovers the underlying trend from 100 points of Gaussian noise.

const { Lowess } = require('fastlowess');
// 100-point noisy sine wave
const n = 100;
const x = Float64Array.from({ length: n }, (_, i) => i * 2 * Math.PI / (n - 1));
const y = Float64Array.from(x, (xi, i) => Math.sin(xi) + (((i * 7 + 3) % 17) / 17 - 0.5) * 0.6);
const model = new Lowess({ fraction: 0.3, iterations: 3 });
const result = model.fit(x, y);
console.log(`First smoothed: ${result.y[0].toFixed(4)} (true: ${Math.sin(x[0]).toFixed(4)})`);
First smoothed: 0.0278 (true: 0.0000)

const { Lowess } = require('fastlowess');
const n = 100;
const x = Float64Array.from({ length: n }, (_, i) => i * 2 * Math.PI / (n - 1));
const y = Float64Array.from(x, (xi, i) => Math.sin(xi) + (((i*7+3)%17)/17-0.5)*0.6);
const model = new Lowess({
fraction: 0.5,
iterations: 3,
confidence_intervals: 0.95,
prediction_intervals: 0.95,
return_diagnostics: true
});
const result = model.fit(x, y);
console.log("Smoothed (first 5):", [...result.y.slice(0, 5)].map(v => v.toFixed(4)));
console.log("CI lower (first 5):", [...result.confidence_lower.slice(0, 5)].map(v => v.toFixed(4)));
console.log("CI upper (first 5):", [...result.confidence_upper.slice(0, 5)].map(v => v.toFixed(4)));
console.log("R²:", result.diagnostics.r_squared.toFixed(4));
Smoothed (first 5): [ '0.1181', '0.1502', '0.1833', '0.2172', '0.2518' ]
CI lower (first 5): [ '0.0551', '0.0762', '0.1205', '0.1405', '0.1770' ]
CI upper (first 5): [ '0.1812', '0.2243', '0.2461', '0.2938', '0.3266' ]
R²: 0.9022

LOWESS can robustly handle outliers through iterative reweighting:

const { Lowess } = require('fastlowess');
const xOut = new Float64Array([1, 2, 3, 4, 5, 6]);
const yWithOutlier = new Float64Array([2.0, 4.0, 6.0, 50.0, 10.0, 12.0]);
const model = new Lowess({
fraction: 0.7,
iterations: 5,
robustness_method: "bisquare",
return_robustness_weights: true
});
const result = model.fit(xOut, yWithOutlier);
// Outliers will have low robustness weights
result.robustness_weights.forEach((w, i) => {
if (w < 0.5) {
console.log(`Point ${i} is likely an outlier (weight: ${w.toFixed(3)})`);
}
});
Point 3 is likely an outlier (weight: 0.000)

For datasets too large to fit in memory, stream them in fixed-size chunks with overlap.

const { StreamingLowess } = require('fastlowess');
const n = 5000;
const x = Float64Array.from({ length: n }, (_, i) => i * 10 * Math.PI / (n - 1));
const y = Float64Array.from(x, (xi, i) =>
Math.sin(xi / Math.PI) * Math.exp(-xi / 30) +
(((i * 7 + 3) % 17) / 17 - 0.5) * 0.3
);
const model = new StreamingLowess(
{ fraction: 0.2 },
{ chunk_size: 1000, overlap: 100, merge_strategy: 'weighted_average' }
);
const chunk_size = 1000;
for (let start = 0; start <= 4000; start += chunk_size) {
const end = Math.min(start + chunk_size, n);
model.process_chunk(x.slice(start, end), y.slice(start, end));
}
const result = model.finalize();
console.log(`Smoothed ${result.y.length} points in streaming mode`);
Smoothed 100 points in streaming mode

TopicLink
How LOWESS worksConcepts
All parameters explainedAPI Reference
Batch vs Streaming vs OnlineExecution Modes
Edge handlingBoundary
Outlier handling in depthRobustness
Full API per languageAPI Reference