Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add 'augurs' convenience crate, re-exporting other crates #117

Merged
merged 32 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7bcfc31
chore: use augurs-js as crate name for JS bindings
sd2k Sep 22, 2024
9711cc4
Mark pyaugurs with doc = false
sd2k Sep 22, 2024
31ef32f
feat: add 'augurs' convenience crate, re-exporting other crates
sd2k Sep 24, 2024
31c0108
Update some READMEs and use inline docs
sd2k Sep 24, 2024
596799c
Use just commands for tests/doctests, ignore some crates
sd2k Sep 24, 2024
da34c67
Fix potential divide-by-zero error in assert_within_pct
sd2k Sep 25, 2024
ee95020
Cast to f64 before calling abs, since the input could be any float type
sd2k Sep 25, 2024
e3c4462
Make changepoints integration test more explicit
sd2k Sep 25, 2024
997598d
Remove dbg call from changepoints integration test
sd2k Sep 25, 2024
58aca5d
Use assert_all_close for dtw integration test
sd2k Sep 25, 2024
f2831ce
Add trailing full-stops to headers in READMEs
sd2k Sep 25, 2024
e7671fe
Fix comment in README example
sd2k Sep 25, 2024
0d95848
Actually, remove trailing commas from README headings
sd2k Sep 25, 2024
cbf9575
Use assert_approx_eq in dtw integration test
sd2k Sep 25, 2024
8218f52
Improve example in clustering README
sd2k Sep 25, 2024
4717430
Fix badges in augurs README
sd2k Sep 25, 2024
0e0af75
Add comments to main augurs example
sd2k Sep 25, 2024
3b00bd7
Add comments to clustering README example
sd2k Sep 25, 2024
a7fbf67
Add examples for DBSCAN outlier detection and clustering
sd2k Sep 25, 2024
3541e5e
Improve comment on parallelize method of dbscan detector
sd2k Sep 25, 2024
3f4edcd
Export PreprocessedData associated types of outlier detectors
sd2k Sep 25, 2024
78734cd
Add 'parallel' feature flag to augurs
sd2k Sep 25, 2024
46c1c53
Add 'doc' Just target
sd2k Sep 25, 2024
9b27269
Consistently take self by value for 'parallelize' args
sd2k Sep 25, 2024
dded2b2
Update casing of DBSCANDetector in JS package too
sd2k Sep 25, 2024
0c29f1e
Use --all-targets for cargo check in CI
sd2k Sep 25, 2024
171d407
Add MAD outlier detection example
sd2k Sep 25, 2024
709fe56
Add forecaster examples
sd2k Sep 25, 2024
64feaa1
Remember to back-transform in-sample predictions in the forecaster
sd2k Sep 25, 2024
2ea4864
Remove unnecessary comment
sd2k Sep 25, 2024
2ddcfb3
Use references to 'augurs' crate in README imports
sd2k Sep 25, 2024
2dc6770
Fix accidental broken imports
sd2k Sep 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ jobs:
- name: Install Rust toolchain
uses: moonrepo/setup-rust@v1
with:
bins: cargo-nextest
bins: cargo-nextest,just

- name: Run cargo nextest
run: cargo nextest run
run: just test
- name: Run doc tests
run: cargo test --doc
run: just doctest

fmt:
name: Rustfmt
Expand Down
37 changes: 20 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# augurs - a time series framework for Rust
# augurs - a time series toolkit for Rust

[![Python](https:/grafana/augurs/actions/workflows/python.yml/badge.svg)](https:/grafana/augurs/actions/workflows/python.yml)
[![Rust](https:/grafana/augurs/actions/workflows/rust.yml/badge.svg)](https:/grafana/augurs/actions/workflows/rust.yml)
[![docs.rs](https://docs.rs/augurs-core/badge.svg)](https://docs.rs/augurs-core)
[![crates.io](https://img.shields.io/crates/v/augurs-core.svg)](https://crates.io/crates/augurs-core)

This repository contains `augurs`, a time series framework built in Rust.
This repository contains `augurs`, a time series toolkit built in Rust.
It aims to provide some useful primitives for working with time series,
as well as the main functionality: heavily optimized forecasting models
based on existing R or Python implementations.
as well as the main functionality: heavily optimized models for forecasting,
outlier detection, clustering, seasonality detection, changepoint detection
and more. Most algorithms are based on existing R or Python implementations.

As well as the core Rust library, augurs will provide bindings to other
languages such as Python and Javascript (via WASM).
Expand All @@ -18,19 +19,20 @@ APIs are subject to change, and functionality may not be fully implemented.

## Crate descriptions

| Name | Purpose | Status |
| ------------------------ | -------------------------------------------------------------------- | -------------------------------------------------------------------- |
| [`augurs-changepoint`][] | Changepoint detection for time series | alpha - API is flexible right now |
| [`augurs-clustering`][] | Time series clustering algorithms | alpha - API is flexible right now |
| [`augurs-core`][] | Common structs and traits | alpha - API is flexible right now |
| [`augurs-dtw`][] | Dynamic Time Warping (DTW) | alpha - API is flexible right now |
| [`augurs-ets`][] | Automatic exponential smoothing models | alpha - non-seasonal models working and tested against statsforecast |
| [`augurs-mstl`][] | Multiple Seasonal Trend Decomposition using LOESS (MSTL) | beta - working and tested against R |
| [`augurs-outlier`][] | Outlier detection for time series | alpha - API is flexible right now |
| [`augurs-seasons`][] | Seasonality detection using periodograms | alpha - working and tested against Python in limited scenarios |
| [`augurs-testing`][] | Testing data and, eventually, evaluation harness for implementations | alpha - just data right now |
| [`augurs-js`][] | WASM bindings to augurs | alpha - untested, should work though |
| [`pyaugurs`][] | Python bindings to augurs | alpha - untested, should work though |
| Name | Purpose | Status |
| ------------------------ | ---------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| [`augurs`] | Wrapper crate exposing functionality of all main crates behind feature flags | alpha |
| [`augurs-changepoint`][] | Changepoint detection for time series | alpha |
| [`augurs-clustering`][] | Time series clustering algorithms | beta |
| [`augurs-core`][] | Common structs and traits | alpha - API is flexible right now |
| [`augurs-dtw`][] | Dynamic Time Warping (DTW) | beta |
| [`augurs-ets`][] | Automatic exponential smoothing models | alpha - non-seasonal models working and tested against statsforecast |
| [`augurs-mstl`][] | Multiple Seasonal Trend Decomposition using LOESS (MSTL) | beta - working and tested against R |
| [`augurs-outlier`][] | Outlier detection for time series | alpha |
| [`augurs-seasons`][] | Seasonality detection using periodograms | alpha - working and tested against Python in limited scenarios |
| [`augurs-testing`][] | Testing data and, eventually, evaluation harness for implementations | alpha - just data right now |
| [`augurs-js`][] | WASM bindings to augurs | alpha |
| [`pyaugurs`][] | Python bindings to augurs | alpha |

## Releasing

Expand Down Expand Up @@ -62,6 +64,7 @@ just publish-npm
Dual-licensed to be compatible with the Rust project.
Licensed under the Apache License, Version 2.0 `<http://www.apache.org/licenses/LICENSE-2.0>` or the MIT license `<http://opensource.org/licenses/MIT>`, at your option.

[`augurs`]: https://crates.io/crates/augurs
[`augurs-changepoint`]: https://crates.io/crates/augurs-changepoint
[`augurs-clustering`]: https://crates.io/crates/augurs-clustering
[`augurs-core`]: https://crates.io/crates/augurs-core
Expand Down
4 changes: 1 addition & 3 deletions crates/augurs-changepoint/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
# Changepoint detection models.

This crate provides algorithms for performing changepoint detection.
Changepoint detection of time series.

For now it is mostly just a wrapper around the [`changepoint`] crate, with
a common `Detector` trait to allow for more implementations in future.

This crate is alpha quality - APIs will almost certainly change!

## Example

```rust
Expand Down
6 changes: 3 additions & 3 deletions crates/augurs-clustering/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Time series clustering algorithms

This crate contains algorithms for clustering time series.
Time series clustering algorithms.

So far, only DBSCAN is implemented, and the distance matrix must be passed directly.
A crate such as [`augurs-dtw`] must be used to calculate the distance matrix for now.

## Usage

```rust
use augurs_clustering::{Dbscan, DistanceMatrix};
use augurs_clustering::{DbscanClusterer, DistanceMatrix};

# fn main() -> Result<(), Box<dyn std::error::Error>> {
let distance_matrix = DistanceMatrix::try_from_square(
Expand All @@ -19,7 +19,7 @@ let distance_matrix = DistanceMatrix::try_from_square(
vec![3.0, 3.0, 4.0, 0.0],
],
)?;
let clusters = Dbscan::new(0.5, 2).fit(&distance_matrix);
let clusters = DbscanClusterer::new(0.5, 2).fit(&distance_matrix);
assert_eq!(clusters, vec![-1, -1, -1, -1]);
# Ok(())
# }
Expand Down
4 changes: 2 additions & 2 deletions crates/augurs-clustering/benches/dbscan.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use criterion::{criterion_group, criterion_main, Criterion};

use augurs_clustering::Dbscan;
use augurs_clustering::DbscanClusterer;
sd2k marked this conversation as resolved.
Show resolved Hide resolved
use augurs_core::DistanceMatrix;

fn dbscan(c: &mut Criterion) {
Expand All @@ -15,7 +15,7 @@ fn dbscan(c: &mut Criterion) {
let distance_matrix = DistanceMatrix::try_from_square(distance_matrix).unwrap();
c.bench_function("dbscan", |b| {
b.iter(|| {
Dbscan::new(10.0, 3).fit(&distance_matrix);
DbscanClusterer::new(10.0, 3).fit(&distance_matrix);
});
});
}
Expand Down
18 changes: 9 additions & 9 deletions crates/augurs-clustering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ pub use augurs_core::DistanceMatrix;

/// DBSCAN clustering algorithm.
#[derive(Debug)]
pub struct Dbscan {
pub struct DbscanClusterer {
epsilon: f64,
min_cluster_size: usize,
}

impl Dbscan {
impl DbscanClusterer {
/// Create a new DBSCAN instance clustering instance.
///
/// # Arguments
Expand Down Expand Up @@ -125,22 +125,22 @@ mod test {
];
let distance_matrix = DistanceMatrix::try_from_square(distance_matrix).unwrap();

let clusters = Dbscan::new(0.5, 2).fit(&distance_matrix);
let clusters = DbscanClusterer::new(0.5, 2).fit(&distance_matrix);
assert_eq!(clusters, vec![-1, -1, -1, -1]);

let clusters = Dbscan::new(1.0, 2).fit(&distance_matrix);
let clusters = DbscanClusterer::new(1.0, 2).fit(&distance_matrix);
assert_eq!(clusters, vec![0, 0, -1, -1]);

let clusters = Dbscan::new(1.0, 3).fit(&distance_matrix);
let clusters = DbscanClusterer::new(1.0, 3).fit(&distance_matrix);
assert_eq!(clusters, vec![-1, -1, -1, -1]);

let clusters = Dbscan::new(2.0, 2).fit(&distance_matrix);
let clusters = DbscanClusterer::new(2.0, 2).fit(&distance_matrix);
assert_eq!(clusters, vec![0, 0, 0, -1]);

let clusters = Dbscan::new(2.0, 3).fit(&distance_matrix);
let clusters = DbscanClusterer::new(2.0, 3).fit(&distance_matrix);
assert_eq!(clusters, vec![0, 0, 0, -1]);

let clusters = Dbscan::new(3.0, 3).fit(&distance_matrix);
let clusters = DbscanClusterer::new(3.0, 3).fit(&distance_matrix);
assert_eq!(clusters, vec![0, 0, 0, 0]);
}

Expand All @@ -155,7 +155,7 @@ mod test {
})
.collect::<Vec<Vec<f64>>>();
let distance_matrix = DistanceMatrix::try_from_square(distance_matrix).unwrap();
let clusters = Dbscan::new(10.0, 3).fit(&distance_matrix);
let clusters = DbscanClusterer::new(10.0, 3).fit(&distance_matrix);
let expected = vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Expand Down
1 change: 1 addition & 0 deletions crates/augurs-dtw/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use augurs_core::DistanceMatrix;

#[cfg(feature = "parallel")]
use rayon::prelude::*;
#[cfg(feature = "parallel")]
use tracing::debug;

/// A trait for defining a distance function.
Expand Down
1 change: 0 additions & 1 deletion crates/augurs-ets/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ mstl = ["dep:augurs-mstl"]
serde = ["dep:serde"]

[dev-dependencies]
assert_approx_eq.workspace = true
augurs-testing.workspace = true
criterion.workspace = true
iai.workspace = true
Expand Down
9 changes: 4 additions & 5 deletions crates/augurs-ets/src/auto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,11 +581,10 @@ impl Predict for FittedAutoETS {
#[cfg(test)]
mod test {
use augurs_core::Fit;
use augurs_testing::{assert_within_pct, data::AIR_PASSENGERS};

use super::{AutoETS, AutoSpec};
use crate::{
assert_closeish,
data::AIR_PASSENGERS,
model::{ErrorComponent, SeasonalComponent, TrendComponent},
Error,
};
Expand Down Expand Up @@ -628,11 +627,11 @@ mod test {
#[test]
fn air_passengers_fit() {
let auto = AutoETS::new(1, "ZZN").unwrap();
let fit = auto.fit(&AIR_PASSENGERS).expect("fit failed");
let fit = auto.fit(AIR_PASSENGERS).expect("fit failed");
assert_eq!(fit.model.model_type().error, ErrorComponent::Multiplicative);
assert_eq!(fit.model.model_type().trend, TrendComponent::Additive);
assert_eq!(fit.model.model_type().season, SeasonalComponent::None);
assert_closeish!(fit.model.log_likelihood(), -831.4883541595792, 0.01);
assert_closeish!(fit.model.aic(), 1672.9767083191584, 0.01);
assert_within_pct!(fit.model.log_likelihood(), -831.4883541595792, 0.01);
assert_within_pct!(fit.model.aic(), 1672.9767083191584, 0.01);
}
}
32 changes: 0 additions & 32 deletions crates/augurs-ets/src/data.rs

This file was deleted.

20 changes: 9 additions & 11 deletions crates/augurs-ets/src/ets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -527,13 +527,11 @@ impl Ets {

#[cfg(test)]
mod tests {
use augurs_testing::{assert_approx_eq, data::AIR_PASSENGERS as AP};

use crate::{
data::AIR_PASSENGERS as AP,
model::{
ErrorComponent, ModelType, OptimizationCriteria, OptimizeParams, Params,
SeasonalComponent, TrendComponent,
},
use crate::model::{
ErrorComponent, ModelType, OptimizationCriteria, OptimizeParams, Params, SeasonalComponent,
TrendComponent,
};

use super::Ets;
Expand Down Expand Up @@ -568,7 +566,7 @@ mod tests {
let mut amse = vec![0.0; 3];
let mut denom = vec![0.0; 3];
let fit = ets.etscalc_in(
&AP,
AP,
&mut init_states,
params,
&mut residuals,
Expand All @@ -577,9 +575,9 @@ mod tests {
&mut denom,
true,
);
assert_approx_eq::assert_approx_eq!(fit.lik, 2070.2270304137766);
assert_approx_eq::assert_approx_eq!(amse[0], 12170.41518101);
assert_approx_eq::assert_approx_eq!(amse[1], 12649.04373164);
assert_approx_eq::assert_approx_eq!(amse[2], 13109.83417796);
assert_approx_eq!(fit.lik, 2070.2270304137766);
assert_approx_eq!(amse[0], 12170.41518101);
assert_approx_eq!(amse[1], 12649.04373164);
assert_approx_eq!(amse[2], 13109.83417796);
}
}
16 changes: 0 additions & 16 deletions crates/augurs-ets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#![warn(missing_docs)]

mod auto;
pub mod data;
mod ets;
pub mod model;
mod stat;
Expand All @@ -35,21 +34,6 @@ pub mod trend;
use augurs_core::ModelError;
pub use auto::{AutoETS, AutoSpec, FittedAutoETS};

#[cfg(test)]
// Assert that a is within (tol * 100)% of b.
#[macro_export]
macro_rules! assert_closeish {
($a:expr, $b:expr, $tol:expr) => {
assert!(
(($a - $b) / $a).abs() < $tol,
"{} is not within {}% of {}",
$a,
$tol * 100.0,
$b
);
};
}

/// Errors returned by this crate.
#[derive(Debug, thiserror::Error)]
pub enum Error {
Expand Down
Loading
Loading