Custom Weights
Per-observation weights that encode data quality directly into the LOWESS fit.
How Custom Weights Work
Section titled “How Custom Weights Work”Standard LOWESS assigns equal prior trust to all observations. Custom weights let you override this assumption point by point — before any distance or robustness weighting is applied.
The effective weight of observation $j$ in a local fit centred at $x_i$ is:
$$w_{ij} = \text{custom_weights}[j] \times K!\left(\frac{d_{ij}}{h_i}\right) \times r_j$$
where $K$ is the distance kernel, $h_i$ is the local bandwidth, and $r_j$ is the robustness weight from the current iteration.
Streaming and Online adapters.When to Use Custom Weights
Section titled “When to Use Custom Weights”| Situation | Recommended weight |
|---|---|
| Point known to be erroneous | 0.0 — fully excluded |
| Unreliable sensor / low precision | 0.1 – 0.5 |
| Standard observation | 1.0 (default) |
| Carefully calibrated measurement | > 1.0 |
| Measurement uncertainty $\sigma_i$ | $1 / \sigma_i^2$ |
Custom Weights vs. Robustness Iterations
Section titled “Custom Weights vs. Robustness Iterations”Both mechanisms handle unreliable data, but they serve different purposes:
| Custom Weights | Robustness Iterations | |
|---|---|---|
| When known | Before fitting | Computed from residuals |
| Knowledge required | Prior knowledge of quality | None — data-driven |
| Effect | Fixed throughout fit | Adapts each iteration |
| Use case | Known bad sensors, calibration | Unknown outlier contamination |
They compose: you can use both simultaneously. Custom weights suppress a priori bad points; robustness iterations then handle any residual outliers that remain.
Basic Usage
Section titled “Basic Usage”Suppress a Known Outlier
Section titled “Suppress a Known Outlier”Set the weight to 0 at the bad point — it is excluded from every local fit
that would otherwise include it.
const { Lowess } = require('fastlowess-wasm');
const x = Float64Array.from({length: 10}, (_, i) => i);const y = Float64Array.from(x, v => v * 2);y[5] = 100.0; // spike
const weights = new Float64Array(10).fill(1.0);weights[5] = 0.0; // exclude the spike
const model = new Lowess({fraction: 0.5, iterations: 0});const result = model.fit(x, y, weights);console.log("y[0]:", result.y[0].toFixed(4));y[0]: 0.5726Emphasize Important Points
Section titled “Emphasize Important Points”Assign high weights to measurements you trust most — calibration standards, reference instruments, or low-noise observations.
const { Lowess } = require('fastlowess-wasm');
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 calibrationIndices = [5, 20, 40, 60, 80];const weights = new Float64Array(x.length).fill(1.0);for (const i of calibrationIndices) weights[i] = 10.0;
const model = new Lowess({fraction: 0.5});const result = model.fit(x, y, weights);console.log("y[0]:", result.y[0].toFixed(4));y[0]: 0.0865Propagate Measurement Uncertainty
Section titled “Propagate Measurement Uncertainty”If each observation has a known standard deviation $\sigma_i$, set $w_i = 1 / \sigma_i^2$ to give the fit information-theoretically optimal weighting.
const { Lowess } = require('fastlowess-wasm');
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 sigma = Float64Array.from({ length: n }, (_, i) => 0.1 + (i % 4) * 0.1);const weights = Float64Array.from(sigma, s => 1.0 / (s * s));const model = new Lowess({fraction: 0.5});const result = model.fit(x, y, weights);console.log("y[0]:", result.y[0].toFixed(4));y[0]: 0.0090Combined with Robustness Iterations
Section titled “Combined with Robustness Iterations”Custom weights and robustness iterations compose naturally: use custom weights for known bad points and robustness for unknown contamination.
const { Lowess } = require('fastlowess-wasm');
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 weights = new Float64Array(x.length).fill(1.0);weights[3] = 0.0;
const model = new Lowess({fraction: 0.4, iterations: 3});const result = model.fit(x, y, weights);console.log("y[0]:", result.y[0].toFixed(4));y[0]: 0.0809Validation Rules
Section titled “Validation Rules”| Rule | Effect |
|---|---|
Length must equal n |
Error at fit time if mismatched |
| All values must be ≥ 0 | Negative weights are rejected |
| All-zero weight vector | Error: no points remain for any local fit |
Uniform weights (1.0 everywhere) |
Identical result to omitting weights |
that centre point falls back to the behaviour specified by`zero_weight_fallback` (default: `"use_local_mean"`).See Also
Section titled “See Also”- Robustness — adaptive outlier downweighting via IRLS
- Parameters — full parameter reference