Skip to main content

LOWESS Project

lowess fastLowess PyPI R-universe npm Julia WASM C++
fastlowess (Python) libfastlowess (C++) rfastlowess (R)
CI

One LOWESS to Rule Them All
One LOWESS to Rule Them All

The fastest, most robust, and most feature-complete language-agnostic LOWESS (Locally Weighted Scatterplot Smoothing) implementation for Rust, Python, R, Julia, JavaScript, C++, and WebAssembly.

[!IMPORTANT]

The lowess-project contains a complete ecosystem for LOWESS smoothing:


Installation

[!NOTE]

Currently available for R, Python, Rust, Julia, Node.js, WebAssembly, and C++. See the Installation Guide for detailed installation instructions.

Documentation

[!NOTE]

📚 View the full documentation


LOESS vs. LOWESS

Feature LOESS (This Crate) LOWESS
Polynomial Degree Linear, Quadratic, Cubic, Quartic Linear (Degree 1)
Dimensions Multivariate (n-D support) Univariate (1-D only)
Flexibility High (Distance metrics) Standard
Complexity Higher (Matrix inversion) Lower (Weighted average/slope)

[!TIP] Note: For a LOESS implementation, use loess-project.


Why this package?

Speed

The lowess project beats the competition in terms of speed, whether in single-threaded or multi-threaded parallel execution. It is on average 200-327x faster than Python's statsmodels.lowess and 2-3x faster than R's lowess.

For more details on the performance comparison, see the Benchmarks page.

Robustness

This implementation is more robust than R's lowess and Python's statsmodels due to two key design choices:

MAD-Based Scale Estimation:

For robustness weight calculations, this crate uses Median Absolute Deviation (MAD) for scale estimation:

s = median(|r_i - median(r)|)

In contrast, statsmodels and R's lowess uses the median of absolute residuals (MAR):

s = median(|r_i|)
  • MAD is a breakdown-point-optimal estimator—it remains valid even when up to 50% of data are outliers.
  • The median-centering step removes asymmetric bias from residual distributions.
  • MAD provides consistent outlier detection regardless of whether residuals are centered around zero.

Boundary Padding:

This crate applies a range of different boundary policies at dataset edges:

  • Extend: Repeats edge values to maintain local neighborhood size.
  • Reflect: Mirrors data symmetrically around boundaries.
  • Zero: Pads with zeros (useful for signal processing).
  • NoBoundary: Original Cleveland behavior

statsmodels and R's lowess do not apply boundary padding, which can lead to:

  • Biased estimates near boundaries due to asymmetric local neighborhoods.
  • Increased variance at the edges of the smoothed curve.

Features

A variety of features, supporting a range of use cases:

Feature This package statsmodels R (stats)
Kernel 7 options only Tricube only Tricube
Robustness Weighting 3 options only Huber only Huber
Scale Estimation 2 options only MAR only MAR
Boundary Padding 4 options no padding no padding
Zero Weight Fallback 3 options no no
Auto Convergence yes no no
Online Mode yes no no
Streaming Mode yes no no
Confidence Intervals yes no no
Prediction Intervals yes no no
Cross-Validation 2 options no no
Parallel Execution yes no no
GPU Acceleration yes* no no
no-std Support yes no no

* GPU acceleration is currently in beta and may not be available on all platforms.

Validation

All implementations are numerical twins of R's lowess:

Aspect Status Details
Accuracy ✅ EXACT MATCH Max diff < 1e-12 across all scenarios
Consistency ✅ PERFECT Multiple scenarios pass with strict tolerance
Robustness ✅ VERIFIED Robust smoothing matches R exactly

API Reference

R:

library(rfastlowess)

model <- Lowess(
    fraction = 0.5,
    iterations = 3L,
    delta = 0.01,
    weight_function = "tricube",
    robustness_method = "bisquare",
    scaling_method = "mad",
    zero_weight_fallback = "use_local_mean",
    boundary_policy = "extend",
    confidence_intervals = 0.95,
    prediction_intervals = 0.95,
    return_diagnostics = TRUE,
    return_residuals = TRUE,
    return_robustness_weights = TRUE,
    return_se = TRUE,
    cv_fractions = c(0.3, 0.5, 0.7),
    cv_method = "kfold",
    cv_k = 5L,
    cv_seed = 123L,
    auto_converge = 1e-4,
    parallel = TRUE
)
custom_weights <- rep(1, length(x))
result <- model$fit(x, y, custom_weights = custom_weights)

# Result structure:
result$x,
result$y,
result$standard_errors,
result$confidence_lower,
result$confidence_upper,
result$prediction_lower,
result$prediction_upper,
result$residuals,
result$robustness_weights,
result$diagnostics,
result$iterations_used,
result$fraction_used,
result$cv_scores

Python:

from fastlowess import Lowess

model = Lowess(
    fraction=0.5,
    iterations=3,
    delta=0.01,
    weight_function="tricube",
    robustness_method="bisquare",
    scaling_method="mad",
    zero_weight_fallback="use_local_mean",
    boundary_policy="extend",
    confidence_intervals=0.95,
    prediction_intervals=0.95,
    return_diagnostics=True,
    return_residuals=True,
    return_robustness_weights=True,
    return_se=True,
    cv_fractions=[0.3, 0.5, 0.7],
    cv_method="kfold",
    cv_k=5,
    cv_seed=123,
    auto_converge=1e-4,
    parallel=True
)
custom_weights = [1.0] * len(x)
result = model.fit(x, y, custom_weights=custom_weights)

# Result structure:
result.x,
result.y,
result.standard_errors,
result.confidence_lower,
result.confidence_upper,
result.prediction_lower,
result.prediction_upper,
result.residuals,
result.robustness_weights,
result.diagnostics,
result.iterations_used,
result.fraction_used,
result.cv_scores

Rust:

use lowess::prelude::*;

let model = Lowess::new()
    .fraction(0.5)
    .iterations(3)
    .delta(0.01)
    .weight_function("tricube")
    .robustness_method("bisquare")
    .scaling_method("mad")
    .zero_weight_fallback("use_local_mean")
    .boundary_policy("extend")
    .return_se()
    .confidence_intervals(0.95)
    .prediction_intervals(0.95)
    .return_diagnostics()
    .return_residuals()
    .return_robustness_weights()
    .cv_method("kfold")
    .cv_k(5)
    .cv_fractions(vec![0.3, 0.5, 0.7])
    .cv_seed(123)
    .auto_converge(1e-4)
    .custom_weights(vec![1.0; x.len()])
    .build()?;

let result = model.fit(&x, &y)?;

// Result structure:
pub struct LowessResult<T> {
    pub x: Vec<T>,                           // Sorted x values
    pub y: Vec<T>,                           // Smoothed y values
    pub standard_errors: Option<Vec<T>>,
    pub confidence_lower: Option<Vec<T>>,
    pub confidence_upper: Option<Vec<T>>,
    pub prediction_lower: Option<Vec<T>>,
    pub prediction_upper: Option<Vec<T>>,
    pub residuals: Option<Vec<T>>,
    pub robustness_weights: Option<Vec<T>>,
    pub diagnostics: Option<Diagnostics<T>>,
    pub iterations_used: Option<usize>,
    pub fraction_used: T,
    pub cv_scores: Option<Vec<T>>,
}

Julia:

using FastLOWESS

model = Lowess(;
    fraction=0.5,
    iterations=3,
    delta=NaN,  # NaN for auto
    weight_function="tricube",
    robustness_method="bisquare",
    scaling_method="mad",
    zero_weight_fallback="use_local_mean",
    boundary_policy="extend",
    confidence_intervals=NaN,
    prediction_intervals=NaN,
    return_diagnostics=true,
    return_residuals=true,
    return_robustness_weights=true,
    return_se=true,
    cv_fractions=Float64[], # e.g. [0.3, 0.5]
    cv_method="kfold",
    cv_k=5,
    cv_seed=123,
    auto_converge=NaN,
    parallel=true
)
custom_weights = ones(length(x))
result = fit(model, x, y; custom_weights=custom_weights)

# Result structure:
result.x,
result.y,
result.standard_errors,
result.confidence_lower,
result.confidence_upper,
result.prediction_lower,
result.prediction_upper,
result.residuals,
result.robustness_weights,
result.diagnostics,
result.iterations_used,
result.fraction_used,
result.cv_scores

Node.js:

import { Lowess } from "fastlowess"

const model = new Lowess({
    fraction: 0.5,
    iterations: 3,
    delta: 0.01,
    weight_function: "tricube",
    robustness_method: "bisquare",
    scaling_method: "mad",
    zero_weight_fallback: "use_local_mean",
    boundary_policy: "extend",
    return_se: true,
    confidence_intervals: 0.95,
    prediction_intervals: 0.95,
    return_diagnostics: true,
    return_residuals: true,
    return_robustness_weights: true,
    cv_fractions: [0.3, 0.5, 0.7],
    cv_method: "kfold",
    cv_k: 5,
    cv_seed: 123,
    auto_converge: 1e-4,
    parallel: true
})
const custom_weights = Array(x.length).fill(1.0)
const result = model.fit(x, y, custom_weights)

// Result structure:
result.x,
result.y,
result.standard_errors,
result.confidence_lower,
result.confidence_upper,
result.prediction_lower,
result.prediction_upper,
result.residuals,
result.robustness_weights,
result.diagnostics,
result.iterations_used,
result.fraction_used,
result.cv_scores

WebAssembly:

import { Lowess } from "fastlowess-wasm"

const model = new Lowess({
    fraction: 0.5,
    iterations: 3,
    delta: 0.01,
    weight_function: "tricube",
    robustness_method: "bisquare",
    scaling_method: "mad",
    zero_weight_fallback: "use_local_mean",
    boundary_policy: "extend",
    return_se: true,
    confidence_intervals: 0.95,
    prediction_intervals: 0.95,
    return_diagnostics: true,
    return_residuals: true,
    return_robustness_weights: true,
    cv_fractions: [0.3, 0.5, 0.7],
    cv_method: "kfold",
    cv_k: 5,
    cv_seed: 123,
    auto_converge: 1e-4,
    parallel: true
})
const custom_weights = new Float64Array(x.length).fill(1)
const result = model.fit(x, y, custom_weights)

// Result structure:
result.x,
result.y,
result.standard_errors,
result.confidence_lower,
result.confidence_upper,
result.prediction_lower,
result.prediction_upper,
result.residuals,
result.robustness_weights,
result.diagnostics,
result.iterations_used,
result.fraction_used,
result.cv_scores

C++:

#include "fastlowess.hpp"

fastlowess::LowessOptions options;
options.fraction = 0.5;
options.iterations = 3;
options.delta = 0.01;
options.weight_function = "tricube";
options.robustness_method = "bisquare";
options.scaling_method = "mad";
options.zero_weight_fallback = "use_local_mean";
options.boundary_policy = "extend";
options.confidence_intervals = 0.95;
options.prediction_intervals = 0.95;
options.return_diagnostics = true;
options.return_residuals = true;
options.return_robustness_weights = true;
options.return_se = true;
options.cv_fractions = {0.3, 0.5, 0.7};
options.cv_method = "kfold";
options.cv_k = 5;
options.cv_seed = 123;
options.auto_converge = 1e-4;
options.parallel = true;

fastlowess::Lowess model(options);
std::vector<double> custom_weights(x.size(), 1.0);
const auto result = model.fit(x, y, custom_weights).value();

// Result structure:
result.x_vector(),
result.y_vector(),
result.standard_errors(),
result.confidence_lower(),
result.confidence_upper(),
result.prediction_lower(),
result.prediction_upper(),
result.residuals(),
result.robustness_weights(),
result.diagnostics(),
result.iterations_used(),
result.fraction_used(),
result.cv_scores()

Contributing

Contributions are welcome! Please see the Contributing Guide for more information.

Changelog

See the Changelog for a history of changes.

License

Licensed under MIT or Apache-2.0.

Citation

If you use this software in your research, please cite it using the CITATION.cff file or the BibTeX entry below:

@software{lowess_project,
  author = {Valizadeh, Amir},
  title = {LOWESS Project: High-Performance Locally Weighted Scatterplot Smoothing},
  year = {2026},
  url = {https://github.com/thisisamirv/lowess-project},
  license = {MIT OR Apache-2.0}
}

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastlowess-2.0.0.tar.gz (36.4 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

fastlowess-2.0.0-cp38-abi3-win_amd64.whl (339.8 kB view details)

Uploaded CPython 3.8+Windows x86-64

fastlowess-2.0.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (462.3 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ x86-64

fastlowess-2.0.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (445.4 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARM64

fastlowess-2.0.0-cp38-abi3-macosx_11_0_arm64.whl (414.0 kB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

fastlowess-2.0.0-cp38-abi3-macosx_10_12_x86_64.whl (431.5 kB view details)

Uploaded CPython 3.8+macOS 10.12+ x86-64

File details

Details for the file fastlowess-2.0.0.tar.gz.

File metadata

  • Download URL: fastlowess-2.0.0.tar.gz
  • Upload date:
  • Size: 36.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fastlowess-2.0.0.tar.gz
Algorithm Hash digest
SHA256 44bd4cad76b45dad0b61b2328062f7db98495fc652e0f292307f5ce79525e525
MD5 34c26f24fd557250966c14e7a779a484
BLAKE2b-256 a64a536819b4814592bfe42bcbb7336fb54785daed896715d434d1a11bd5693c

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastlowess-2.0.0.tar.gz:

Publisher: release-pypi.yml on thisisamirv/lowess-project

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fastlowess-2.0.0-cp38-abi3-win_amd64.whl.

File metadata

  • Download URL: fastlowess-2.0.0-cp38-abi3-win_amd64.whl
  • Upload date:
  • Size: 339.8 kB
  • Tags: CPython 3.8+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fastlowess-2.0.0-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 31d6d133ddcf807239a60480d73932e35992ce355943fbe60450c7633bf23f0f
MD5 00dbc9f5fc1af2372a8625ec8128d53b
BLAKE2b-256 d88e72f2a4923f88d4019560e007457d86626be7fbc799a94c1e471741060f20

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastlowess-2.0.0-cp38-abi3-win_amd64.whl:

Publisher: release-pypi.yml on thisisamirv/lowess-project

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fastlowess-2.0.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for fastlowess-2.0.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 0c37e8f6824aad73545f9f4d37b5dc62c927e97ee6d768eaa04f7d0830dec450
MD5 439c5f6951bbc57ef6e5597f18b326f2
BLAKE2b-256 38ec39e3405037a6c6df4eedafd69c1eb4ada5583c3a71246cdd36bd58e70f89

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastlowess-2.0.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release-pypi.yml on thisisamirv/lowess-project

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fastlowess-2.0.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for fastlowess-2.0.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 c3086a97e298f85bfe2eff9449c23c1527cdbb063f30514241fc1a96ea9ec931
MD5 17c358e224af883655aba230c8799bea
BLAKE2b-256 fa5749ba3475e492d8540a3446b8ecd1c1d7247237ad016237d96e0e91f857f0

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastlowess-2.0.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release-pypi.yml on thisisamirv/lowess-project

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fastlowess-2.0.0-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for fastlowess-2.0.0-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 75a0ac0e1c297c001facd2cad9a8de09215679948b640fa0d9c5bde26aeeb255
MD5 43c06d163b49aecbb0bb752760a4c3cf
BLAKE2b-256 301f5745ae6fac324e4aaaaf98505341adf1819d08bce15e9ba581e031cb0e02

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastlowess-2.0.0-cp38-abi3-macosx_11_0_arm64.whl:

Publisher: release-pypi.yml on thisisamirv/lowess-project

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fastlowess-2.0.0-cp38-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for fastlowess-2.0.0-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 df20aaa7849bd8678084795aed79e1aae1ff668a3f7ec206038c1494c2e35bf0
MD5 fd8563872abc9298b24199c7539dcccb
BLAKE2b-256 fc17a2439a8dab41246cecd18cb8ddd18fbdb1c8adf9e412e16339505f18b8e2

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastlowess-2.0.0-cp38-abi3-macosx_10_12_x86_64.whl:

Publisher: release-pypi.yml on thisisamirv/lowess-project

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page