diff --git a/crates/augurs-js/src/seasons.rs b/crates/augurs-js/src/seasons.rs index 67bb3ff0..efcfc081 100644 --- a/crates/augurs-js/src/seasons.rs +++ b/crates/augurs-js/src/seasons.rs @@ -6,6 +6,6 @@ use augurs_seasons::{Detector, PeriodogramDetector}; /// Detect the seasonal periods in a time series. #[wasm_bindgen] -pub fn seasonalities(y: &[f64]) -> Vec { - PeriodogramDetector::builder().build(y).detect().collect() +pub fn seasonalities(y: &[f64]) -> Vec { + PeriodogramDetector::builder().build(y).detect() } diff --git a/crates/augurs-seasons/README.md b/crates/augurs-seasons/README.md index 5fe2e0f2..91d2833b 100644 --- a/crates/augurs-seasons/README.md +++ b/crates/augurs-seasons/README.md @@ -20,7 +20,7 @@ let y = &[ 0.09, 0.29, 0.81, 0.49, 0.11, 0.28, 0.78, 0.53, ]; -let periods: Vec<_> = PeriodogramDetector::builder().build(y).detect().collect(); +let periods = PeriodogramDetector::builder().build(y).detect(); assert_eq!(periods[0], 4); # } ``` diff --git a/crates/augurs-seasons/benches/periodogram.rs b/crates/augurs-seasons/benches/periodogram.rs index 5a7a6f14..bcf74b75 100644 --- a/crates/augurs-seasons/benches/periodogram.rs +++ b/crates/augurs-seasons/benches/periodogram.rs @@ -7,12 +7,7 @@ use augurs_testing::data::SEASON_EIGHT; fn season_eight(c: &mut Criterion) { let y = SEASON_EIGHT; c.bench_function("season_eight", |b| { - b.iter(|| { - PeriodogramDetector::builder() - .build(y) - .detect() - .collect::>() - }); + b.iter(|| PeriodogramDetector::builder().build(y).detect()); }); } diff --git a/crates/augurs-seasons/src/lib.rs b/crates/augurs-seasons/src/lib.rs index 3b4cdbc0..00e97874 100644 --- a/crates/augurs-seasons/src/lib.rs +++ b/crates/augurs-seasons/src/lib.rs @@ -17,5 +17,5 @@ pub use periodogram::{ /// A detector of periodic signals in a time series. pub trait Detector { /// Detects the periods of the time series. - fn detect(&self) -> impl Iterator; + fn detect(&self) -> Vec; } diff --git a/crates/augurs-seasons/src/periodogram.rs b/crates/augurs-seasons/src/periodogram.rs index 43f66e3a..64041144 100644 --- a/crates/augurs-seasons/src/periodogram.rs +++ b/crates/augurs-seasons/src/periodogram.rs @@ -12,8 +12,8 @@ const DEFAULT_MAX_FFT_PERIOD: f64 = 512.0; /// A builder for a periodogram detector. #[derive(Debug, Clone)] pub struct Builder { - min_period: usize, - max_period: Option, + min_period: u32, + max_period: Option, threshold: f64, } @@ -32,7 +32,7 @@ impl Builder { /// /// The default is 4. #[must_use] - pub fn min_period(mut self, min_period: usize) -> Self { + pub fn min_period(mut self, min_period: u32) -> Self { self.min_period = min_period; self } @@ -41,7 +41,7 @@ impl Builder { /// /// 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: usize) -> Self { + pub fn max_period(mut self, max_period: u32) -> Self { self.max_period = Some(max_period); self } @@ -71,15 +71,15 @@ impl Builder { } } -fn default_max_period(data: &[f64]) -> usize { - (data.len() as f64 / DEFAULT_MIN_FFT_CYCLES).min(DEFAULT_MAX_FFT_PERIOD) as usize +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, + pub periods: Vec, /// The powers of the periodogram. pub powers: Vec, } @@ -125,11 +125,11 @@ pub struct Period { /// The power of the peak. pub power: f64, /// The period of the peak. - pub period: usize, + pub period: u32, /// The previous period in the periodogram. - pub prev_period: usize, + pub prev_period: u32, /// The next period in the periodogram. - pub next_period: usize, + pub next_period: u32, } /// A season detector which uses a periodogram to identify seasonal periods. @@ -140,8 +140,8 @@ pub struct Period { #[derive(Debug)] pub struct Detector<'a> { data: &'a [f64], - min_period: usize, - max_period: usize, + min_period: u32, + max_period: u32, threshold: f64, } @@ -163,7 +163,7 @@ impl<'a> Detector<'a> { pub fn periodogram(&self) -> Periodogram { let frequency = 1.0; let data_len = self.data.len(); - let n_per_segment = (self.max_period * 2).min(data_len / 2); + let n_per_segment = (self.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; @@ -177,7 +177,7 @@ impl<'a> Detector<'a> { // 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 usize); + let periods = freqs.iter().skip(1).map(|x| x.recip().round() as u32); let power = sd.iter().skip(1).copied(); let (periods, powers) = periods @@ -202,8 +202,11 @@ impl<'a> Detector<'a> { } impl<'a> crate::Detector for Detector<'a> { - fn detect(&self) -> impl Iterator { - self.periodogram().peaks(self.threshold).map(|x| x.period) + fn detect(&self) -> Vec { + self.periodogram() + .peaks(self.threshold) + .map(|x| x.period) + .collect() } } @@ -225,7 +228,7 @@ mod test { 0.09, 0.29, 0.81, 0.49, 0.11, 0.28, 0.78, 0.53, ]; - let periods = Detector::builder().build(y).detect().collect::>(); + let periods = Detector::builder().build(y).detect(); assert_eq!(periods[0], 4); } diff --git a/crates/augurs-seasons/src/test_data.rs b/crates/augurs-seasons/src/test_data.rs index a1f198a1..f591f889 100644 --- a/crates/augurs-seasons/src/test_data.rs +++ b/crates/augurs-seasons/src/test_data.rs @@ -1,7 +1,7 @@ use augurs_testing::data::{SEASON_EIGHT, SEASON_SEVEN}; pub(crate) struct TestCase { - pub(crate) season_lengths: &'static [usize], + pub(crate) season_lengths: &'static [u32], pub(crate) data: &'static [f64], } diff --git a/crates/pyaugurs/src/seasons.rs b/crates/pyaugurs/src/seasons.rs index b23d0b3a..1d2699f1 100644 --- a/crates/pyaugurs/src/seasons.rs +++ b/crates/pyaugurs/src/seasons.rs @@ -7,14 +7,10 @@ use augurs_seasons::{Detector, PeriodogramDetector}; /// Detect the seasonal periods in a time series. #[pyfunction] -pub fn seasonalities( - py: Python<'_>, - y: PyReadonlyArray1<'_, f64>, -) -> PyResult>> { +pub fn seasonalities(py: Python<'_>, y: PyReadonlyArray1<'_, f64>) -> PyResult>> { Ok(PeriodogramDetector::builder() .build(y.as_slice()?) .detect() - .collect::>() .to_pyarray(py) .into()) }