From 2a9a9eaf465b28cd4f8ba933ceb5c9c8f908ad1b Mon Sep 17 00:00:00 2001 From: Weijie Guo Date: Mon, 23 Oct 2023 14:33:51 +0800 Subject: [PATCH] chore(rust): move moment to ops (#11941) --- crates/polars-core/Cargo.toml | 2 - crates/polars-core/src/series/ops/mod.rs | 2 - crates/polars-ops/Cargo.toml | 2 +- crates/polars-ops/src/series/ops/mod.rs | 4 ++ .../src/series/ops/moment.rs | 50 +++++++++++-------- crates/polars-ops/src/series/ops/rolling.rs | 1 + crates/polars-plan/Cargo.toml | 2 +- crates/polars/Cargo.toml | 2 +- 8 files changed, 36 insertions(+), 29 deletions(-) rename crates/{polars-core => polars-ops}/src/series/ops/moment.rs (79%) diff --git a/crates/polars-core/Cargo.toml b/crates/polars-core/Cargo.toml index 8ec0afe8a936..d34488684198 100644 --- a/crates/polars-core/Cargo.toml +++ b/crates/polars-core/Cargo.toml @@ -91,7 +91,6 @@ take_opt_iter = [] group_by_list = [] # rolling window functions rolling_window = [] -moment = [] diagonal_concat = [] horizontal_concat = [] abs = [] @@ -145,7 +144,6 @@ docs-selection = [ "dot_product", "row_hash", "rolling_window", - "moment", "dtype-categorical", "dtype-decimal", "diagonal_concat", diff --git a/crates/polars-core/src/series/ops/mod.rs b/crates/polars-core/src/series/ops/mod.rs index 650e5cbecaa4..159991052e1a 100644 --- a/crates/polars-core/src/series/ops/mod.rs +++ b/crates/polars-core/src/series/ops/mod.rs @@ -1,7 +1,5 @@ mod downcast; mod extend; -#[cfg(feature = "moment")] -pub mod moment; mod null; mod to_list; mod unique; diff --git a/crates/polars-ops/Cargo.toml b/crates/polars-ops/Cargo.toml index 1651ab8afb38..24f429134949 100644 --- a/crates/polars-ops/Cargo.toml +++ b/crates/polars-ops/Cargo.toml @@ -93,7 +93,7 @@ hash = [] zip_with = ["polars-core/zip_with"] group_by_list = ["polars-core/group_by_list"] rolling_window = ["polars-core/rolling_window"] -moment = ["polars-core/moment"] +moment = [] mode = [] search_sorted = [] merge_sorted = [] diff --git a/crates/polars-ops/src/series/ops/mod.rs b/crates/polars-ops/src/series/ops/mod.rs index ea7f4a9a455e..b422b4e28509 100644 --- a/crates/polars-ops/src/series/ops/mod.rs +++ b/crates/polars-ops/src/series/ops/mod.rs @@ -28,6 +28,8 @@ mod is_last_distinct; mod is_unique; #[cfg(feature = "log")] mod log; +#[cfg(feature = "moment")] +mod moment; #[cfg(feature = "pct_change")] mod pct_change; #[cfg(feature = "rank")] @@ -74,6 +76,8 @@ pub use is_last_distinct::*; pub use is_unique::*; #[cfg(feature = "log")] pub use log::*; +#[cfg(feature = "moment")] +pub use moment::*; #[cfg(feature = "pct_change")] pub use pct_change::*; use polars_core::prelude::*; diff --git a/crates/polars-core/src/series/ops/moment.rs b/crates/polars-ops/src/series/ops/moment.rs similarity index 79% rename from crates/polars-core/src/series/ops/moment.rs rename to crates/polars-ops/src/series/ops/moment.rs index d34df64619d7..a6e5a264e3df 100644 --- a/crates/polars-core/src/series/ops/moment.rs +++ b/crates/polars-ops/src/series/ops/moment.rs @@ -1,4 +1,6 @@ -use crate::prelude::*; +use polars_core::prelude::*; + +use crate::prelude::SeriesSealed; fn moment_precomputed_mean(s: &Series, moment: usize, mean: f64) -> PolarsResult> { // see: https://github.com/scipy/scipy/blob/47bb6febaa10658c72962b9615d5d5aa2513fa3a/scipy/stats/stats.py#L922 @@ -39,7 +41,7 @@ fn moment_precomputed_mean(s: &Series, moment: usize, mean: f64) -> PolarsResult Ok(out) } -impl Series { +pub trait MomentSeries: SeriesSealed { /// Compute the sample skewness of a data set. /// /// For normally distributed data, the skewness should be about zero. For @@ -49,19 +51,21 @@ impl Series { /// is close enough to zero, statistically speaking. /// /// see: [scipy](https://github.com/scipy/scipy/blob/47bb6febaa10658c72962b9615d5d5aa2513fa3a/scipy/stats/stats.py#L1024) - pub fn skew(&self, bias: bool) -> PolarsResult> { - let mean = match self.mean() { + fn skew(&self, bias: bool) -> PolarsResult> { + let s = self.as_series(); + + let mean = match s.mean() { Some(mean) => mean, None => return Ok(None), }; // we can unwrap because if it were None, we already return None above - let m2 = moment_precomputed_mean(self, 2, mean)?.unwrap(); - let m3 = moment_precomputed_mean(self, 3, mean)?.unwrap(); + let m2 = moment_precomputed_mean(s, 2, mean)?.unwrap(); + let m3 = moment_precomputed_mean(s, 3, mean)?.unwrap(); let out = m3 / m2.powf(1.5); if !bias { - let n = (self.len() - self.null_count()) as f64; + let n = (s.len() - s.null_count()) as f64; Ok(Some(((n - 1.0) * n).sqrt() / (n - 2.0) * out)) } else { Ok(Some(out)) @@ -77,17 +81,19 @@ impl Series { /// eliminate bias coming from biased moment estimators /// /// see: [scipy](https://github.com/scipy/scipy/blob/47bb6febaa10658c72962b9615d5d5aa2513fa3a/scipy/stats/stats.py#L1027) - pub fn kurtosis(&self, fisher: bool, bias: bool) -> PolarsResult> { - let mean = match self.mean() { + fn kurtosis(&self, fisher: bool, bias: bool) -> PolarsResult> { + let s = self.as_series(); + + let mean = match s.mean() { Some(mean) => mean, None => return Ok(None), }; // we can unwrap because if it were None, we already return None above - let m2 = moment_precomputed_mean(self, 2, mean)?.unwrap(); - let m4 = moment_precomputed_mean(self, 4, mean)?.unwrap(); + let m2 = moment_precomputed_mean(s, 2, mean)?.unwrap(); + let m4 = moment_precomputed_mean(s, 4, mean)?.unwrap(); let out = if !bias { - let n = (self.len() - self.null_count()) as f64; + let n = (s.len() - s.null_count()) as f64; 3.0 + 1.0 / (n - 2.0) / (n - 3.0) * ((n.powf(2.0) - 1.0) * m4 / m2.powf(2.0) - 3.0 * (n - 1.0).powf(2.0)) } else { @@ -101,16 +107,16 @@ impl Series { } } +impl MomentSeries for Series {} + #[cfg(test)] mod test { use super::*; - impl Series { - fn moment(&self, moment: usize) -> PolarsResult> { - match self.mean() { - Some(mean) => moment_precomputed_mean(self, moment, mean), - None => Ok(None), - } + fn moment(s: &Series, moment: usize) -> PolarsResult> { + match s.mean() { + Some(mean) => moment_precomputed_mean(s, moment, mean), + None => Ok(None), } } @@ -118,10 +124,10 @@ mod test { fn test_moment_compute() -> PolarsResult<()> { let s = Series::new("", &[1, 2, 3, 4, 5, 23]); - assert_eq!(s.moment(0)?, Some(1.0)); - assert_eq!(s.moment(1)?, Some(0.0)); - assert!((s.moment(2)?.unwrap() - 57.22222222222223).abs() < 0.00001); - assert!((s.moment(3)?.unwrap() - 724.0740740740742).abs() < 0.00001); + assert_eq!(moment(&s, 0)?, Some(1.0)); + assert_eq!(moment(&s, 1)?, Some(0.0)); + assert!((moment(&s, 2)?.unwrap() - 57.22222222222223).abs() < 0.00001); + assert!((moment(&s, 3)?.unwrap() - 724.0740740740742).abs() < 0.00001); Ok(()) } diff --git a/crates/polars-ops/src/series/ops/rolling.rs b/crates/polars-ops/src/series/ops/rolling.rs index f01e50324fa8..87724501146a 100644 --- a/crates/polars-ops/src/series/ops/rolling.rs +++ b/crates/polars-ops/src/series/ops/rolling.rs @@ -1,6 +1,7 @@ use polars_core::prelude::*; #[cfg(feature = "moment")] use { + crate::series::ops::moment::MomentSeries, polars_core::export::num::{self, Float, FromPrimitive}, polars_core::utils::with_unstable_series, std::ops::SubAssign, diff --git a/crates/polars-plan/Cargo.toml b/crates/polars-plan/Cargo.toml index d70a90914835..b8d55668befe 100644 --- a/crates/polars-plan/Cargo.toml +++ b/crates/polars-plan/Cargo.toml @@ -109,7 +109,7 @@ rolling_window = [ rank = ["polars-ops/rank"] diff = ["polars-ops/diff"] pct_change = ["polars-ops/pct_change"] -moment = ["polars-core/moment", "polars-ops/moment"] +moment = ["polars-ops/moment"] abs = ["polars-core/abs"] random = ["polars-core/random"] dynamic_group_by = ["polars-core/dynamic_group_by"] diff --git a/crates/polars/Cargo.toml b/crates/polars/Cargo.toml index 165fea5806f0..01f9e520eb51 100644 --- a/crates/polars/Cargo.toml +++ b/crates/polars/Cargo.toml @@ -145,7 +145,7 @@ interpolate = ["polars-ops/interpolate", "polars-lazy?/interpolate"] rank = ["polars-lazy?/rank", "polars-ops/rank"] diff = ["polars-ops/diff", "polars-lazy?/diff"] pct_change = ["polars-ops/pct_change", "polars-lazy?/pct_change"] -moment = ["polars-core/moment", "polars-lazy?/moment", "polars-ops/moment"] +moment = ["polars-ops/moment", "polars-lazy?/moment"] range = ["polars-lazy?/range"] true_div = ["polars-lazy?/true_div"] diagonal_concat = ["polars-core/diagonal_concat", "polars-lazy?/diagonal_concat", "polars-sql?/diagonal_concat"]