Skip to main content

lowess/
lib.rs

1//! # LOWESS — Locally Weighted Scatterplot Smoothing for Rust
2//!
3//! The fastest, most robust, and most feature-complete language-agnostic
4//! LOWESS (Locally Weighted Scatterplot Smoothing) implementation for **Rust**.
5//!
6//! ## What is LOWESS?
7//!
8//! LOWESS (Locally Weighted Scatterplot Smoothing) is a nonparametric regression
9//! method that fits smooth curves through scatter plots. At each point, it fits
10//! a weighted polynomial (typically linear) using nearby data points, with weights
11//! decreasing smoothly with distance. This creates flexible, data-adaptive curves
12//! without assuming a global functional form.
13//!
14//! ## Documentation
15//!
16//! > 📚 **Full Documentation**: [lowess.readthedocs.io](https://lowess.readthedocs.io/)
17//! >
18//! > Comprehensive guides, API references, and tutorials.
19//!
20//! ## Quick Start
21//!
22//! ### Typical Use
23//!
24//! ```rust
25//! use lowess::prelude::*;
26//!
27//! let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
28//! let y = vec![2.0, 4.1, 5.9, 8.2, 9.8];
29//!
30//! // Build the model
31//! let model = Lowess::new()
32//!     .fraction(0.5)      // Use 50% of data for each local fit
33//!     .iterations(3)      // 3 robustness iterations
34//!     .adapter(Batch)
35//!     .build()?;
36//!
37//! // Fit the model to the data
38//! let result = model.fit(&x, &y)?;
39//!
40//! println!("{}", result);
41//! # Result::<(), LowessError>::Ok(())
42//! ```
43//!
44//! ```text
45//! Summary:
46//!   Data points: 5
47//!   Fraction: 0.5
48//!
49//! Smoothed Data:
50//!        X     Y_smooth
51//!   --------------------
52//!     1.00     2.00000
53//!     2.00     4.10000
54//!     3.00     5.90000
55//!     4.00     8.20000
56//!     5.00     9.80000
57//! ```
58//!
59//! ### Full Features
60//!
61//! ```rust
62//! use lowess::prelude::*;
63//!
64//! let x = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
65//! let y = vec![2.1, 3.8, 6.2, 7.9, 10.3, 11.8, 14.1, 15.7];
66//!
67//! // Build model with all features enabled
68//! let model = Lowess::new()
69//!     .fraction(0.5)                                   // Use 50% of data for each local fit
70//!     .iterations(3)                                   // 3 robustness iterations
71//!     .weight_function(Tricube)                        // Kernel function
72//!     .robustness_method(Bisquare)                     // Outlier handling
73//!     .delta(0.01)                                     // Interpolation optimization
74//!     .zero_weight_fallback(UseLocalMean)              // Fallback policy
75//!     .boundary_policy(Extend)                         // Boundary handling policy
76//!     .scaling_method(MAD)                             // Robust scale estimation
77//!     .auto_converge(1e-6)                             // Auto-convergence threshold
78//!     .confidence_intervals(0.95)                      // 95% confidence intervals
79//!     .prediction_intervals(0.95)                      // 95% prediction intervals
80//!     .return_diagnostics()                            // Fit quality metrics
81//!     .return_residuals()                              // Include residuals
82//!     .return_robustness_weights()                     // Include robustness weights
83//!     .cross_validate(KFold(5, &[0.3, 0.7]).seed(123)) // K-fold CV with 5 folds and 2 fraction options
84//!     .adapter(Batch)                                  // Batch adapter
85//!     .build()?;
86//!
87//! let result = model.fit(&x, &y)?;
88//! println!("{}", result);
89//! # Result::<(), LowessError>::Ok(())
90//! ```
91//!
92//! ```text
93//! Summary:
94//!   Data points: 8
95//!   Fraction: 0.5
96//!   Robustness: Applied
97//!
98//! LOWESS Diagnostics:
99//!   RMSE:         0.191925
100//!   MAE:          0.181676
101//!   R^2:           0.998205
102//!   Residual SD:  0.297750
103//!   Effective DF: 8.00
104//!   AIC:          -10.41
105//!   AICc:         inf
106//!
107//! Smoothed Data:
108//!        X     Y_smooth      Std_Err   Conf_Lower   Conf_Upper   Pred_Lower   Pred_Upper     Residual Rob_Weight
109//!   ----------------------------------------------------------------------------------------------------------------
110//!     1.00     2.01963     0.389365     1.256476     2.782788     1.058911     2.980353     0.080368     1.0000
111//!     2.00     4.00251     0.345447     3.325438     4.679589     3.108641     4.896386    -0.202513     1.0000
112//!     3.00     5.99959     0.423339     5.169846     6.829335     4.985168     7.014013     0.200410     1.0000
113//!     4.00     8.09859     0.489473     7.139224     9.057960     6.975666     9.221518    -0.198592     1.0000
114//!     5.00    10.03881     0.551687     8.957506    11.120118     8.810073    11.267551     0.261188     1.0000
115//!     6.00    12.02872     0.539259    10.971775    13.085672    10.821364    13.236083    -0.228723     1.0000
116//!     7.00    13.89828     0.371149    13.170829    14.625733    12.965670    14.830892     0.201719     1.0000
117//!     8.00    15.77990     0.408300    14.979631    16.580167    14.789441    16.770356    -0.079899     1.0000
118//! ```
119//!
120//! ### Result and Error Handling
121//!
122//! The `fit` method returns a `Result<LowessResult<T>, LowessError>`.
123//!
124//! - **`Ok(LowessResult<T>)`**: Contains the smoothed data and diagnostics.
125//! - **`Err(LowessError)`**: Indicates a failure (e.g., mismatched input lengths, insufficient data).
126//!
127//! The `?` operator is idiomatic:
128//!
129//! ```rust
130//! use lowess::prelude::*;
131//! # let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
132//! # let y = vec![2.0, 4.1, 5.9, 8.2, 9.8];
133//!
134//! let model = Lowess::new().adapter(Batch).build()?;
135//!
136//! let result = model.fit(&x, &y)?;
137//! // or to be more explicit:
138//! // let result: LowessResult<f64> = model.fit(&x, &y)?;
139//! # Result::<(), LowessError>::Ok(())
140//! ```
141//!
142//! But you can also handle results explicitly:
143//!
144//! ```rust
145//! use lowess::prelude::*;
146//! # let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
147//! # let y = vec![2.0, 4.1, 5.9, 8.2, 9.8];
148//!
149//! let model = Lowess::new().adapter(Batch).build()?;
150//!
151//! match model.fit(&x, &y) {
152//!     Ok(result) => {
153//!         // result is LowessResult<f64>
154//!         println!("Smoothed: {:?}", result.y);
155//!     }
156//!     Err(e) => {
157//!         // e is LowessError
158//!         eprintln!("Fitting failed: {}", e);
159//!     }
160//! }
161//! # Result::<(), LowessError>::Ok(())
162//! ```
163//!
164//! ## Minimal Usage (no_std / Embedded)
165//!
166//! The crate supports `no_std` environments for embedded devices and resource-constrained systems.
167//! Disable default features to remove the standard library dependency:
168//!
169//! ```toml
170//! [dependencies]
171//! lowess = { version = "0.5", default-features = false }
172//! ```
173//!
174//! **Minimal example for embedded systems:**
175//!
176//! ```rust
177//! # #[cfg(feature = "std")] {
178//! use lowess::prelude::*;
179//!
180//! // In an embedded context (e.g., sensor data processing)
181//! fn smooth_sensor_data() -> Result<(), LowessError> {
182//!     // Small dataset from sensor readings
183//!     let x = vec![1.0_f32, 2.0, 3.0, 4.0, 5.0];
184//!     let y = vec![2.1, 3.9, 6.2, 7.8, 10.1];
185//!
186//!     // Build minimal model (no intervals, no diagnostics)
187//!     let model = Lowess::new()
188//!         .fraction(0.5)
189//!         .iterations(2)      // Fewer iterations for speed
190//!         .adapter(Batch)
191//!         .build()?;
192//!
193//!     // Fit the model
194//!     let result = model.fit(&x, &y)?;
195//!
196//!     // Use smoothed values (result.y)
197//!     // ...
198//!
199//!     Ok(())
200//! }
201//! # smooth_sensor_data().unwrap();
202//! # }
203//! ```
204//!
205//! **Tips for embedded/no_std usage:**
206//! - Use `f32` instead of `f64` to reduce memory footprint
207//! - Keep datasets small (< 1000 points)
208//! - Disable optional features (intervals, diagnostics) to reduce code size
209//! - Use fewer iterations (1-2) to reduce computation time
210//! - Allocate buffers statically when possible to avoid heap fragmentation
211//!
212//! ## References
213//!
214//! - Cleveland, W. S. (1979). "Robust Locally Weighted Regression and Smoothing Scatterplots"
215//! - Cleveland, W. S. (1981). "LOWESS: A Program for Smoothing Scatterplots by Robust Locally Weighted Regression"
216//!
217//! ## srrstats Compliance for rOpenSci Statistical Software Review
218//!
219//! @srrstats {G1.0} Statistical literature references documented above (Cleveland 1979, 1981).
220//! @srrstats {G1.1} This package provides LOWESS smoothing, a nonparametric regression method
221//!   for fitting smooth curves to scatterplot data using locally weighted linear regression.
222//! @srrstats {G1.4} All exported functions and types are documented with rustdoc comments.
223//! @srrstats {G1.6} Performance characteristics documented: SIMD-optimized solvers, O(n*k)
224//!   complexity where k is the window size, supports streaming and online modes.
225//!
226//! ## License
227//!
228//! See the repository for license information and contribution guidelines.
229
230#![cfg_attr(not(feature = "std"), no_std)]
231
232#[cfg(not(feature = "std"))]
233#[macro_use]
234extern crate alloc;
235
236// Layer 1: Primitives - data structures and basic utilities.
237mod primitives;
238
239// Layer 2: Math - pure mathematical functions.
240mod math;
241
242// Layer 3: Algorithms - core LOWESS algorithms.
243mod algorithms;
244
245// Layer 4: Evaluation - post-processing and diagnostics.
246mod evaluation;
247
248// Layer 5: Engine - orchestration and execution control.
249mod engine;
250
251// Layer 6: Adapters - execution mode adapters.
252mod adapters;
253
254// High-level fluent API for LOWESS smoothing.
255mod api;
256
257// Standard LOWESS prelude.
258pub mod prelude {
259    pub use crate::api::{
260        Adapter::{Batch, Online, Streaming},
261        BoundaryPolicy::Extend,
262        BoundaryPolicy::NoBoundary,
263        BoundaryPolicy::Reflect,
264        BoundaryPolicy::Zero,
265        KFold, LOOCV, LowessBuilder as Lowess, LowessError, LowessResult,
266        MergeStrategy::Average,
267        MergeStrategy::TakeFirst,
268        MergeStrategy::WeightedAverage,
269        RobustnessMethod::Bisquare,
270        RobustnessMethod::Huber,
271        RobustnessMethod::Talwar,
272        ScalingMethod::MAD,
273        ScalingMethod::MAR,
274        ScalingMethod::Mean,
275        UpdateMode::Full,
276        UpdateMode::Incremental,
277        WeightFunction::Biweight,
278        WeightFunction::Cosine,
279        WeightFunction::Epanechnikov,
280        WeightFunction::Gaussian,
281        WeightFunction::Triangle,
282        WeightFunction::Tricube,
283        WeightFunction::Uniform,
284        ZeroWeightFallback::ReturnNone,
285        ZeroWeightFallback::ReturnOriginal,
286        ZeroWeightFallback::UseLocalMean,
287    };
288}
289
290// Internal modules for development and testing.
291//
292// This module re-exports internal modules for development and testing purposes.
293// It is only available with the `dev` feature enabled.
294#[cfg(feature = "dev")]
295pub mod internals {
296    pub mod primitives {
297        pub use crate::primitives::*;
298    }
299    pub mod math {
300        pub use crate::math::*;
301    }
302    pub mod algorithms {
303        pub use crate::algorithms::*;
304    }
305    pub mod engine {
306        pub use crate::engine::*;
307    }
308    pub mod evaluation {
309        pub use crate::evaluation::*;
310    }
311    pub mod adapters {
312        pub use crate::adapters::*;
313    }
314    pub mod api {
315        pub use crate::api::*;
316    }
317}