From 14ffaa6ac56a7d96fe5a2c13d516767c59b9e536 Mon Sep 17 00:00:00 2001 From: Ben Sully Date: Thu, 15 Feb 2024 15:57:11 +0000 Subject: [PATCH] Add seasonality detection using periodograms, and Python/JS bindings (#61) * Add seasonality detection using periodograms, and Python/JS bindings * Use u32 instead of usize; return Vec instead of impl Iterator for lower MSRV * Change Detector trait to pass data to detect This means detectors don't need to store the data and can avoid a lifetime parameter. * Allow seasonality detector params to be customised in JS bindings * Allow seasonality detector params to be customised in Python bindings --- Cargo.toml | 1 + README.md | 2 + crates/augurs-js/Cargo.toml | 2 + crates/augurs-js/src/lib.rs | 1 + crates/augurs-js/src/seasons.rs | 54 + crates/augurs-seasons/Cargo.toml | 26 + crates/augurs-seasons/README.md | 49 + crates/augurs-seasons/benches/periodogram.rs | 20 + crates/augurs-seasons/src/lib.rs | 21 + crates/augurs-seasons/src/periodogram.rs | 260 ++++ crates/augurs-seasons/src/test_data.rs | 17 + crates/augurs-testing/src/data.rs | 1450 ++++++++++++++++++ crates/pyaugurs/Cargo.toml | 1 + crates/pyaugurs/augurs.pyi | 21 +- crates/pyaugurs/src/lib.rs | 2 + crates/pyaugurs/src/seasons.rs | 30 + 16 files changed, 1956 insertions(+), 1 deletion(-) create mode 100644 crates/augurs-js/src/seasons.rs create mode 100644 crates/augurs-seasons/Cargo.toml create mode 100644 crates/augurs-seasons/README.md create mode 100644 crates/augurs-seasons/benches/periodogram.rs create mode 100644 crates/augurs-seasons/src/lib.rs create mode 100644 crates/augurs-seasons/src/periodogram.rs create mode 100644 crates/augurs-seasons/src/test_data.rs create mode 100644 crates/pyaugurs/src/seasons.rs diff --git a/Cargo.toml b/Cargo.toml index 136fddf5..695892b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ keywords = [ augurs-core = { version = "0.1.0-alpha.0", path = "crates/augurs-core" } augurs-ets = { version = "0.1.0-alpha.0", path = "crates/augurs-ets" } augurs-mstl = { version = "0.1.0-alpha.0", path = "crates/augurs-mstl" } +augurs-seasons = { version = "0.1.0-alpha.0", path = "crates/augurs-seasons" } augurs-testing = { version = "0.1.0-alpha.0", path = "crates/augurs-testing" } distrs = "0.2.1" diff --git a/README.md b/README.md index 6f6fdec7..be231ba3 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ APIs are subject to change, and functionality may not be fully implemented. | [`augurs-core`][] | Common structs and traits | 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-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 | @@ -40,5 +41,6 @@ Licensed under the Apache License, Version 2.0 `, + + /// The maximum period to consider when detecting seasonal periods. + /// + /// The default is the length of the data divided by 3, or 512, whichever is smaller. + pub max_period: Option, + + /// The threshold for detecting peaks in the periodogram. + /// + /// The value will be clamped to the range 0.01 to 0.99. + /// + /// The default is 0.9. + pub threshold: Option, +} + +impl From for PeriodogramDetector { + fn from(options: SeasonalityOptions) -> Self { + let mut builder = PeriodogramDetector::builder(); + if let Some(min_period) = options.min_period { + builder = builder.min_period(min_period); + } + if let Some(max_period) = options.max_period { + builder = builder.max_period(max_period); + } + if let Some(threshold) = options.threshold { + builder = builder.threshold(threshold); + } + builder.build() + } +} + +/// Detect the seasonal periods in a time series. +#[wasm_bindgen] +pub fn seasonalities(y: &[f64], options: JsValue) -> Vec { + let options: SeasonalityOptions = + serde_wasm_bindgen::from_value::>(options) + .ok() + .flatten() + .unwrap_or_default(); + PeriodogramDetector::from(options).detect(y) +} diff --git a/crates/augurs-seasons/Cargo.toml b/crates/augurs-seasons/Cargo.toml new file mode 100644 index 00000000..ca6eed1d --- /dev/null +++ b/crates/augurs-seasons/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "augurs-seasons" +version.workspace = true +authors.workspace = true +documentation.workspace = true +repository.workspace = true +license.workspace = true +edition.workspace = true +keywords.workspace = true +description = "Seasonality detection using periodograms" + +[dependencies] +itertools.workspace = true +num-traits = "0.2.18" +thiserror.workspace = true +tracing.workspace = true +welch-sde = "0.1.0" + +[dev-dependencies] +augurs-testing.workspace = true +criterion.workspace = true +pprof.workspace = true + +[[bench]] +name = "periodogram" +harness = false diff --git a/crates/augurs-seasons/README.md b/crates/augurs-seasons/README.md new file mode 100644 index 00000000..bb3c3502 --- /dev/null +++ b/crates/augurs-seasons/README.md @@ -0,0 +1,49 @@ +# Seasonality detection for time series + +`augurs-seasons` contains methods for detecting seasonality or periodicity in time series. + +It currently contains implementations to do so using periodograms, similar to the [`seasonal`] Python package. + +## Usage + +```rust +use augurs_seasons::{Detector, PeriodogramDetector}; + +# fn main() { +let y = &[ + 0.1, 0.3, 0.8, 0.5, + 0.1, 0.31, 0.79, 0.48, + 0.09, 0.29, 0.81, 0.49, + 0.11, 0.28, 0.78, 0.53, + 0.1, 0.3, 0.8, 0.5, + 0.1, 0.31, 0.79, 0.48, + 0.09, 0.29, 0.81, 0.49, + 0.11, 0.28, 0.78, 0.53, +]; +// Use the detector with default parameters. +let periods = PeriodogramDetector::default().detect(y); +assert_eq!(periods[0], 4); + +// Customise the detector using the builder. +let periods = PeriodogramDetector::builder() + .min_period(4) + .max_period(8) + .threshold(0.8) + .build() + .detect(y); +assert_eq!(periods[0], 4); +# } +``` + +## Credits + +This implementation is based heavily on the [`seasonal`] Python package. +It also makes heavy use of the [`welch-sde`] crate. + +[`seasonal`]: https://github.com/welch/seasonal +[`welch-sde`]: https://crates.io/crates/welch-sde + +## License + +Dual-licensed to be compatible with the Rust project. +Licensed under the Apache License, Version 2.0 `` or the MIT license ``, at your option. diff --git a/crates/augurs-seasons/benches/periodogram.rs b/crates/augurs-seasons/benches/periodogram.rs new file mode 100644 index 00000000..71ca675b --- /dev/null +++ b/crates/augurs-seasons/benches/periodogram.rs @@ -0,0 +1,20 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use pprof::criterion::{Output, PProfProfiler}; + +use augurs_seasons::{Detector, PeriodogramDetector}; +use augurs_testing::data::SEASON_EIGHT; + +fn season_eight(c: &mut Criterion) { + let y = SEASON_EIGHT; + let detector = PeriodogramDetector::builder().build(); + c.bench_function("season_eight", |b| { + b.iter(|| detector.detect(y)); + }); +} + +criterion_group! { + name = benches; + config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Protobuf)); + targets = season_eight +} +criterion_main!(benches); diff --git a/crates/augurs-seasons/src/lib.rs b/crates/augurs-seasons/src/lib.rs new file mode 100644 index 00000000..0ad51c46 --- /dev/null +++ b/crates/augurs-seasons/src/lib.rs @@ -0,0 +1,21 @@ +#![doc = include_str!("../README.md")] +#![warn( + missing_docs, + missing_debug_implementations, + rust_2018_idioms, + unreachable_pub +)] + +mod periodogram; +#[cfg(test)] +mod test_data; + +pub use periodogram::{ + Builder as PeriodogramDetectorBuilder, Detector as PeriodogramDetector, Periodogram, +}; + +/// A detector of periodic signals in a time series. +pub trait Detector { + /// Detects the periods of a time series. + fn detect(&self, data: &[f64]) -> Vec; +} diff --git a/crates/augurs-seasons/src/periodogram.rs b/crates/augurs-seasons/src/periodogram.rs new file mode 100644 index 00000000..a5ab63b7 --- /dev/null +++ b/crates/augurs-seasons/src/periodogram.rs @@ -0,0 +1,260 @@ +use std::cmp::Ordering; + +use itertools::Itertools; +use welch_sde::{Build, SpectralDensity}; + +// Default number of cycles of data assumed when establishing FFT window sizes. +const DEFAULT_MIN_FFT_CYCLES: f64 = 3.0; + +/// Default maximum period assumed when establishing FFT window sizes. +const DEFAULT_MAX_FFT_PERIOD: f64 = 512.0; + +/// A builder for a periodogram detector. +#[derive(Debug, Clone)] +pub struct Builder { + min_period: u32, + max_period: Option, + threshold: f64, +} + +impl Default for Builder { + fn default() -> Self { + Self { + min_period: 4, + max_period: None, + threshold: 0.9, + } + } +} + +impl Builder { + /// Set the minimum period to consider when detecting seasonal periods. + /// + /// The default is 4. + #[must_use] + pub fn min_period(mut self, min_period: u32) -> Self { + self.min_period = min_period; + self + } + + /// Set the maximum period to consider when detecting seasonal periods. + /// + /// The default is the length of the data divided by 3, or 512, whichever is smaller. + #[must_use] + pub fn max_period(mut self, max_period: u32) -> Self { + self.max_period = Some(max_period); + self + } + + /// Set the threshold for detecting peaks in the periodogram. + /// + /// The value will be clamped to the range 0.01 to 0.99. + /// + /// The default is 0.9. + #[must_use] + pub fn threshold(mut self, threshold: f64) -> Self { + self.threshold = threshold.clamp(0.01, 0.99); + self + } + + /// Build the periodogram detector. + /// + /// The data is the time series to detect seasonal periods in. + #[must_use] + pub fn build(self) -> Detector { + Detector { + min_period: self.min_period, + max_period: self.max_period, + threshold: self.threshold, + } + } +} + +fn default_max_period(data: &[f64]) -> u32 { + (data.len() as f64 / DEFAULT_MIN_FFT_CYCLES).min(DEFAULT_MAX_FFT_PERIOD) as u32 +} + +/// A periodogram of a time series. +#[derive(Debug, Clone, PartialEq)] +pub struct Periodogram { + /// The periods of the periodogram. + pub periods: Vec, + /// The powers of the periodogram. + pub powers: Vec, +} + +impl Periodogram { + /// Find the peaks in the periodogram. + /// + /// The peaks are defined as the periods which have a power greater than `threshold` times the + /// maximum power in the periodogram. + pub fn peaks(&self, threshold: f64) -> impl Iterator { + // Scale the threshold so that it's relative to the maximum power. + let keep = self + .powers + .iter() + .copied() + .max_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal)) + .unwrap_or(1.0) + * threshold; + + // We're going to window by 3 and zip this up with the powers, but we want the + // middle element to represent the periodogram value we're looking at, so + // we need to prepend and append a 0 to the periods. + std::iter::once(0) + .chain(self.periods.iter().copied()) + .chain(std::iter::once(0)) + .tuple_windows() + .zip(self.powers.iter().copied()) + .filter_map(|((prev_period, period, next_period), power)| { + (power >= keep).then_some(Period { + power, + period, + prev_period, + next_period, + }) + }) + .sorted_by(|a, b| a.power.partial_cmp(&b.power).unwrap_or(Ordering::Equal)) + } +} + +/// A peak in the periodogram. +#[derive(Debug, Clone, PartialEq)] +pub struct Period { + /// The power of the peak. + pub power: f64, + /// The period of the peak. + pub period: u32, + /// The previous period in the periodogram. + pub prev_period: u32, + /// The next period in the periodogram. + pub next_period: u32, +} + +/// A season detector which uses a periodogram to identify seasonal periods. +/// +/// The detector works by calculating a robust periodogram of the data using +/// Welch's method. The peaks in the periodogram represent likely seasonal periods +/// in the data. +#[derive(Debug)] +pub struct Detector { + min_period: u32, + max_period: Option, + threshold: f64, +} + +impl Detector { + /// Create a new detector builder. + #[must_use] + pub fn builder() -> Builder { + Builder::default() + } + + /// Calculate the periodogram of the data. + /// + /// The periodogram is a frequency domain representation of the data, and is calculated using the + /// Welch method. + /// + /// The periodogram can then be used to identify peaks, which are returned as periods which + /// correspond to likely seasonal periods in the data. + #[must_use] + pub fn periodogram(&self, data: &[f64]) -> Periodogram { + let max_period = self.max_period.unwrap_or_else(|| default_max_period(data)); + let frequency = 1.0; + let data_len = data.len(); + let n_per_segment = (max_period * 2).min(data_len as u32 / 2); + let max_fft_size = (n_per_segment as f64).log2().floor() as usize; + let n_segments = (data_len as f64 / n_per_segment as f64).ceil() as usize; + + let welch: SpectralDensity<'_, f64> = SpectralDensity::builder(data, frequency) + .n_segment(n_segments) + .dft_log2_max_size(max_fft_size) + .build(); + let sd = welch.periodogram(); + + let freqs = sd.frequency(); + // Periods are the reciprocal of the frequency, since we've used a frequency of 1. + // Make sure we skip the first one, which is 0, and the first power, which corresponds to + // that. + let periods = freqs.iter().skip(1).map(|x| x.recip().round() as u32); + let power = sd.iter().skip(1).copied(); + + let (periods, powers) = periods + .zip(power) + .filter(|(per, _)| { + // Filter out periods that are too short or too long, and the period corresponding to the + // segment length. + *per >= self.min_period && *per < max_period && *per != n_per_segment + }) + // Group by period, and keep the maximum power for each period. + .group_by(|(per, _)| *per) + .into_iter() + .map(|(per, group)| { + let max_power = group + .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Equal)) + .unwrap_or((0, 0.0)); + (per, max_power.1) + }) + .unzip(); + Periodogram { periods, powers } + } +} + +impl Default for Detector { + fn default() -> Self { + Self::builder().build() + } +} + +impl crate::Detector for Detector { + fn detect(&self, data: &[f64]) -> Vec { + self.periodogram(data) + .peaks(self.threshold) + .map(|x| x.period) + .collect() + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{test_data::*, Detector as _}; + + #[test] + fn smoke() { + #[rustfmt::skip] + let y = &[ + 0.1, 0.3, 0.8, 0.5, + 0.1, 0.31, 0.79, 0.48, + 0.09, 0.29, 0.81, 0.49, + 0.11, 0.28, 0.78, 0.53, + 0.1, 0.3, 0.8, 0.5, + 0.1, 0.31, 0.79, 0.48, + 0.09, 0.29, 0.81, 0.49, + 0.11, 0.28, 0.78, 0.53, + ]; + let periods = Detector::default().detect(y); + assert_eq!(periods[0], 4); + } + + #[test] + fn test_detect() { + for (i, test_case) in CASES.iter().enumerate() { + let TestCase { + data, + season_lengths: expected, + } = test_case; + let detector = Detector::default(); + assert_eq!( + detector + .periodogram(data) + .peaks(0.5) + .map(|x| x.period) + .collect_vec(), + *expected, + "Test case {}", + i + ); + } + } +} diff --git a/crates/augurs-seasons/src/test_data.rs b/crates/augurs-seasons/src/test_data.rs new file mode 100644 index 00000000..f591f889 --- /dev/null +++ b/crates/augurs-seasons/src/test_data.rs @@ -0,0 +1,17 @@ +use augurs_testing::data::{SEASON_EIGHT, SEASON_SEVEN}; + +pub(crate) struct TestCase { + pub(crate) season_lengths: &'static [u32], + pub(crate) data: &'static [f64], +} + +pub(crate) static CASES: &[TestCase] = &[ + TestCase { + season_lengths: &[8], + data: SEASON_EIGHT, + }, + TestCase { + season_lengths: &[7], + data: SEASON_SEVEN, + }, +]; diff --git a/crates/augurs-testing/src/data.rs b/crates/augurs-testing/src/data.rs index c7b4fa47..95c123e9 100644 --- a/crates/augurs-testing/src/data.rs +++ b/crates/augurs-testing/src/data.rs @@ -43,3 +43,1453 @@ pub static AUSTRES: &[f64] = &[ 16697.0, 16777.2, 16833.1, 16891.6, 16956.8, 17026.3, 17085.4, 17106.9, 17169.4, 17239.4, 17292.0, 17354.2, 17414.2, 17447.3, 17482.6, 17526.0, 17568.7, 17627.1, 17661.5, ]; + +/// A trendless, seasonal time series with season length 7. +pub static SEASON_SEVEN: &[f64] = &[ + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526266544243022, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526463530349485, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526340412607388, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.01052624192136851, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526192678988796, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526229611227154, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526340412607388, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.0035087965529582666, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526266543897444, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526266543897444, + 0.010526365035741093, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.01052641428269969, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526291166685565, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.01052624192136851, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.00701739612499386, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526365035741093, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.01052641428269969, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526291166685565, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526389659133991, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526389659133991, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017617729309431, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526389659133991, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017469991543949, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526266543897444, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.0035087965529582666, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526389659133991, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.007017543859649122, + 0.0, + 0.007017543859649122, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.010526315789473682, + 0.003508771929824561, + 0.0, + 0.003508771929824561, + 0.010526315789473682, +]; + +/// A trendless, seasonal time series with season length 8. +pub static SEASON_EIGHT: &[f64] = &[ + 0.4070175438596491, + 0.39999999999999997, + 0.39999999999999997, + 0.4245614035087719, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.40350877192982454, + 0.39999999999999997, + 0.39999999999999997, + 0.4175438596491228, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4140350877192982, + 0.39999999999999997, + 0.39999999999999997, + 0.4175438596491228, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4175438596491228, + 0.39999999999999997, + 0.39999999999999997, + 0.41052631578947363, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4245614035087719, + 0.39999999999999997, + 0.39999999999999997, + 0.40701754385964906, + 0.4385964912280701, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.431578947368421, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.4350877192982456, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4385964912280701, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.431578947368421, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4350877192982456, + 0.4070175438596491, + 0.39999999999999997, + 0.39999999999999997, + 0.4350877192982456, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4350877192982456, + 0.40350662975477447, + 0.39999999999999997, + 0.39999999999999997, + 0.4245614035087719, + 0.44210526315789467, + 0.44210526315789467, + 0.44212465544068963, + 0.44210526315789467, + 0.40701754385964906, + 0.39999999999999997, + 0.39999999999999997, + 0.4175409049352831, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.41052631578947363, + 0.39999999999999997, + 0.39999999999999997, + 0.4140350877192982, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.42105263157894735, + 0.39999999999999997, + 0.39999999999999997, + 0.40350877192982454, + 0.4385964912280701, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4245614035087719, + 0.39999999999999997, + 0.39999999999999997, + 0.40350877192982454, + 0.4350877192982455, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.43157894736842106, + 0.40350877192982454, + 0.39999999999999997, + 0.39999999999999997, + 0.431578947368421, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4350877192982455, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.431578947368421, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.43859649122807015, + 0.40350877192982454, + 0.3999964912896265, + 0.39999999999999997, + 0.4245614035087719, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4070175438596491, + 0.39999999999999997, + 0.39999999999999997, + 0.42105263157894735, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4175438596491228, + 0.39999999999999997, + 0.39999999999999997, + 0.40701754385964906, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.42105263157894735, + 0.39999999999999997, + 0.39999999999999997, + 0.41052631578947363, + 0.4385964912280701, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4245614035087719, + 0.39999999999999997, + 0.39999999999999997, + 0.40350877192982454, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.43158123732789067, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.431578947368421, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4350877192982455, + 0.40351087721514334, + 0.4000014035186212, + 0.39999999999999997, + 0.4175438596491228, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.41052631578947363, + 0.39999999999999997, + 0.39999999999999997, + 0.4175438596491228, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4175438596491228, + 0.39999999999999997, + 0.39999999999999997, + 0.4140350877192982, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.42105263157894735, + 0.39999999999999997, + 0.39999999999999997, + 0.41052631578947363, + 0.43859649122807015, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4280670237438342, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.4175438596491228, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.42105263157894735, + 0.39999999999999997, + 0.39999999999999997, + 0.41052631578947363, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4175438596491228, + 0.39999999999999997, + 0.39999999999999997, + 0.40701754385964906, + 0.44210526315789467, + 0.44210526315789467, + 0.4421100647363582, + 0.44210526315789467, + 0.4245614035087719, + 0.39999999999999997, + 0.39999999999999997, + 0.40350877192982454, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.42807017543859643, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.431578947368421, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4350877192982456, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.4280701754385965, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.40701754385964906, + 0.39999999999999997, + 0.40000631598892594, + 0.4280701754385965, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.40350877192982454, + 0.39999999999999997, + 0.39999999999999997, + 0.4175438596491228, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4385964912280701, + 0.40701754385964906, + 0.39999999999999997, + 0.39999999999999997, + 0.42105263157894735, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4140350877192982, + 0.39999999999999997, + 0.39999999999999997, + 0.4140379932694654, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4140350877192982, + 0.39999999999999997, + 0.39999999999999997, + 0.4140350877192982, + 0.44210526315789467, + 0.44210526315789467, + 0.44210376116781946, + 0.44210526315789467, + 0.42105263157894735, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.44210526315789467, + 0.4421029363125817, + 0.44210526315789467, + 0.44210526315789467, + 0.4280701754385965, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.4350846168487139, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.43157894736842106, + 0.40350877192982454, + 0.39999999999999997, + 0.39999999999999997, + 0.431578947368421, + 0.4421015082222319, + 0.44210526315789467, + 0.44210526315789467, + 0.43859649122807015, + 0.40350877192982454, + 0.39999999999999997, + 0.39999999999999997, + 0.42105263157894735, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4070175438596491, + 0.39999999999999997, + 0.39999999999999997, + 0.42105263157894735, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.41052631578947363, + 0.39999999999999997, + 0.39999999999999997, + 0.4175438596491228, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4421091289979215, + 0.42105263157894735, + 0.39999999999999997, + 0.39999999999999997, + 0.41052631578947363, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4245614035087719, + 0.39999999999999997, + 0.39999999999999997, + 0.4140350877192982, + 0.44210526315789467, + 0.44210368729945504, + 0.44210526315789467, + 0.44210526315789467, + 0.42105263157894735, + 0.39999999999999997, + 0.39999999999999997, + 0.40701754385964906, + 0.4385964912280701, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.42807017543859643, + 0.39999999999999997, + 0.39999999999999997, + 0.40350877192982454, + 0.4350877192982456, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.42807017543859643, + 0.40350877192982454, + 0.39999999999999997, + 0.39999999999999997, + 0.4280701754385965, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4350877192982455, + 0.4070175438596491, + 0.39999999999999997, + 0.39999999999999997, + 0.42105263157894735, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4140350877192982, + 0.39999999999999997, + 0.39999999999999997, + 0.4140350877192982, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4140350877192982, + 0.39999999999999997, + 0.39999999999999997, + 0.41052631578947363, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4245614035087719, + 0.39999999999999997, + 0.39999999999999997, + 0.40701754385964906, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.42807017543859643, + 0.39999999999999997, + 0.39999999999999997, + 0.41052631578947363, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.431578947368421, + 0.39999999999999997, + 0.39999999999999997, + 0.40350877192982454, + 0.4385964912280701, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.431578947368421, + 0.40350877192982454, + 0.39999999999999997, + 0.39999999999999997, + 0.4350877192982455, + 0.45010666666666665, + 0.4508029090909091, + 0.4522948571428572, + 0.44898340740740744, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.431578947368421, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.43859649122807015, + 0.40350877192982454, + 0.39999999999999997, + 0.39999999999999997, + 0.4175438596491228, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4070175438596491, + 0.39999999999999997, + 0.39999999999999997, + 0.4140350877192982, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4175438596491228, + 0.41811133333333333, + 0.41811133333333333, + 0.4266148888888889, + 0.45617110000000005, + 0.441192787037037, + 0.43245774786324787, + 0.4318670906862745, + 0.42105263157894735, + 0.39999999999999997, + 0.39999999999999997, + 0.40701754385964906, + 0.4385964912280701, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4280701754385965, + 0.39999999999999997, + 0.3999971930218523, + 0.39999999999999997, + 0.4385964912280701, + 0.44210526315789467, + 0.44210526315789467, + 0.44211006473635817, + 0.4280701754385965, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.431578947368421, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4385964912280701, + 0.40350877192982454, + 0.39999999999999997, + 0.39999999999999997, + 0.4315774207555576, + 0.44210526315789467, + 0.44210526315789467, + 0.4421082672012879, + 0.4385964912280701, + 0.40350877192982454, + 0.39999999999999997, + 0.39999999999999997, + 0.4175438596491228, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4105249122905509, + 0.39999999999999997, + 0.39999999999999997, + 0.4140350877192982, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4210489997559292, + 0.39999999999999997, + 0.39999999999999997, + 0.40701754385964906, + 0.4385964912280701, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4280701754385965, + 0.39999999999999997, + 0.39999999999999997, + 0.4000014035186211, + 0.4385964912280701, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4280701754385965, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.4350877192982456, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4385964912280701, + 0.40350877192982454, + 0.39999999999999997, + 0.39999999999999997, + 0.42105263157894735, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.41052631578947363, + 0.39999999999999997, + 0.39999999999999997, + 0.41403656510732045, + 0.44210526315789467, + 0.44211235479458344, + 0.44210526315789467, + 0.44210526315789467, + 0.4245614035087719, + 0.39999999999999997, + 0.39999508783994775, + 0.4140350877192982, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4421068390384518, + 0.4245614035087719, + 0.39999999999999997, + 0.4068306052757674, + 0.40683058481247536, + 0.4447592183207115, + 0.4418504846043413, + 0.4249532996632996, + 0.43239620432457737, + 0.4280671222342955, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.431578947368421, + 0.4421052631578947, + 0.4421083164479009, + 0.4421052631578947, + 0.43508993538805485, + 0.39999999999999997, + 0.3999978947590025, + 0.39999999999999997, + 0.431578947368421, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4350877192982456, + 0.40350877192982454, + 0.39999999999999997, + 0.39999999999999997, + 0.4175438596491228, + 0.44210304711473947, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.41052631578947363, + 0.39999999999999997, + 0.39999999999999997, + 0.4140350877192982, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.41403508771929826, + 0.39999999999999997, + 0.39999999999999997, + 0.41403508771929826, + 0.4421052631578947, + 0.4421052631578947, + 0.4421052631578947, + 0.4421052631578947, + 0.42456140350877186, + 0.39999999999999997, + 0.39999999999999997, + 0.4105263157894737, + 0.43859649122807015, + 0.4421052631578947, + 0.44210762698702466, + 0.4421052631578947, + 0.4245614035087719, + 0.39999999999999997, + 0.39999999999999997, + 0.4070175438596491, + 0.4385964912280701, + 0.4421000924631029, + 0.44210526315789467, + 0.44210526315789467, + 0.4280701754385965, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.4385948907468424, + 0.44210526315789467, + 0.44210526315789467, + 0.44210378579060755, + 0.4280701754385965, + 0.39999999999999997, + 0.4000014035186212, + 0.39999999999999997, + 0.43508624193095846, + 0.44210526315789467, + 0.4421028624444765, + 0.44210526315789467, + 0.4350877192982456, + 0.39999087760909147, + 0.39999999999999997, + 0.39999999999999997, + 0.43157894736842106, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.44210526315789467, + 0.4070175438596491, + 0.39999999999999997, + 0.39999578956232495, + 0.42456140350877186, + 0.4421052631578947, + 0.4421052631578947, + 0.4421083164479009, + 0.4421052631578947, + 0.40350877192982454, + 0.39999999999999997, + 0.39999999999999997, + 0.4175438596491228, + 0.44210526315789467, + 0.4421109512015715, + 0.44210526315789467, + 0.44210526315789467, + 0.4140350877192982, + 0.39999999999999997, + 0.40000280705694113, + 0.4105263157894737, + 0.4421052631578947, + 0.44210755311736427, + 0.4421022099535938, + 0.4421052631578947, + 0.42456140350877186, + 0.39999999999999997, + 0.39999999999999997, + 0.40350877192982454, + 0.43859649122807015, + 0.4421052631578947, + 0.4421052631578947, + 0.4421052631578947, + 0.4245614035087719, + 0.39999999999999997, + 0.3999964912896265, + 0.40350877192982454, + 0.4350877192982456, + 0.4421052631578947, + 0.4421052631578947, + 0.4421052631578947, + 0.431578947368421, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, + 0.4350862173081703, + 0.4421052631578947, + 0.4421052631578947, + 0.4421052631578947, + 0.41726220532813507, + 0.39507753968253967, + 0.39160266666666665, + 0.39160266666666665, + 0.39160266666666665, + 0.4364861481481481, + 0.4376113821041213, + 0.43611022727272725, + 0.4353893101851851, + 0.4280701754385965, + 0.39999999999999997, + 0.39999999999999997, + 0.39999999999999997, +]; diff --git a/crates/pyaugurs/Cargo.toml b/crates/pyaugurs/Cargo.toml index fbddf0d6..9514125c 100644 --- a/crates/pyaugurs/Cargo.toml +++ b/crates/pyaugurs/Cargo.toml @@ -18,6 +18,7 @@ crate-type = ["cdylib"] augurs-core.workspace = true augurs-ets = { workspace = true, features = ["mstl"] } augurs-mstl.workspace = true +augurs-seasons.workspace = true numpy = "0.20.0" pyo3 = { version = "0.20.0", features = ["extension-module"] } pyo3-log = "0.9.0" diff --git a/crates/pyaugurs/augurs.pyi b/crates/pyaugurs/augurs.pyi index 4eb2baf4..0522a1bb 100644 --- a/crates/pyaugurs/augurs.pyi +++ b/crates/pyaugurs/augurs.pyi @@ -1,5 +1,5 @@ import abc -from typing import Sequence +from typing import Optional, Sequence import numpy as np import numpy.typing as npt @@ -68,3 +68,22 @@ class AutoETS: def fit(self, y: npt.NDArray[np.float64]) -> None: ... def predict(self, horizon: int, level: float | None) -> Forecast: ... def predict_in_sample(self, level: float | None) -> Forecast: ... + +def seasonalities( + y: npt.NDArray[np.float64], + min_period: Optional[int] = None, + max_period: Optional[int] = None, + threshold: Optional[float] = None, +) -> npt.NDArray[np.uint64]: ... +""" +Determine the seasonalities of a time series. + +:param y: the time series to analyze. +:param min_period: the minimum period to consider. The default is 4. +:param max_period: the maximum period to consider. The default is the length of the + data divided by 3, or 512, whichever is smaller. +:param threshold: the threshold for detecting peaks in the periodogram. + The value will be clamped to the range 0.01 to 0.99. + The default is 0.9. +:return: an array of season lengths. +""" diff --git a/crates/pyaugurs/src/lib.rs b/crates/pyaugurs/src/lib.rs index 16e1ab0b..aff37b03 100644 --- a/crates/pyaugurs/src/lib.rs +++ b/crates/pyaugurs/src/lib.rs @@ -16,6 +16,7 @@ use pyo3::prelude::*; pub mod ets; pub mod mstl; +pub mod seasons; pub mod trend; /// Forecasts produced by augurs models. @@ -110,5 +111,6 @@ fn augurs(_py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_function(wrap_pyfunction!(seasons::seasonalities, m)?)?; Ok(()) } diff --git a/crates/pyaugurs/src/seasons.rs b/crates/pyaugurs/src/seasons.rs new file mode 100644 index 00000000..78bce304 --- /dev/null +++ b/crates/pyaugurs/src/seasons.rs @@ -0,0 +1,30 @@ +//! Bindings for seasonality detection. + +use numpy::{PyArray1, PyReadonlyArray1, ToPyArray}; +use pyo3::prelude::*; + +use augurs_seasons::{Detector, PeriodogramDetector}; + +/// Detect the seasonal periods in a time series. +#[pyfunction] +pub fn seasonalities( + py: Python<'_>, + y: PyReadonlyArray1<'_, f64>, + min_period: Option, + max_period: Option, + threshold: Option, +) -> PyResult>> { + let mut builder = PeriodogramDetector::builder(); + + if let Some(min_period) = min_period { + builder = builder.min_period(min_period); + } + if let Some(max_period) = max_period { + builder = builder.max_period(max_period); + } + if let Some(threshold) = threshold { + builder = builder.threshold(threshold); + } + + Ok(builder.build().detect(y.as_slice()?).to_pyarray(py).into()) +}