Skip to content

Commit

Permalink
chore(rust): move moment to ops (#11941)
Browse files Browse the repository at this point in the history
  • Loading branch information
reswqa authored Oct 23, 2023
1 parent 5f3312b commit 2a9a9ea
Show file tree
Hide file tree
Showing 8 changed files with 36 additions and 29 deletions.
2 changes: 0 additions & 2 deletions crates/polars-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ take_opt_iter = []
group_by_list = []
# rolling window functions
rolling_window = []
moment = []
diagonal_concat = []
horizontal_concat = []
abs = []
Expand Down Expand Up @@ -145,7 +144,6 @@ docs-selection = [
"dot_product",
"row_hash",
"rolling_window",
"moment",
"dtype-categorical",
"dtype-decimal",
"diagonal_concat",
Expand Down
2 changes: 0 additions & 2 deletions crates/polars-core/src/series/ops/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
mod downcast;
mod extend;
#[cfg(feature = "moment")]
pub mod moment;
mod null;
mod to_list;
mod unique;
Expand Down
2 changes: 1 addition & 1 deletion crates/polars-ops/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down
4 changes: 4 additions & 0 deletions crates/polars-ops/src/series/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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::*;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Option<f64>> {
// see: https://github.com/scipy/scipy/blob/47bb6febaa10658c72962b9615d5d5aa2513fa3a/scipy/stats/stats.py#L922
Expand Down Expand Up @@ -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
Expand All @@ -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<Option<f64>> {
let mean = match self.mean() {
fn skew(&self, bias: bool) -> PolarsResult<Option<f64>> {
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))
Expand All @@ -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<Option<f64>> {
let mean = match self.mean() {
fn kurtosis(&self, fisher: bool, bias: bool) -> PolarsResult<Option<f64>> {
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 {
Expand All @@ -101,27 +107,27 @@ impl Series {
}
}

impl MomentSeries for Series {}

#[cfg(test)]
mod test {
use super::*;

impl Series {
fn moment(&self, moment: usize) -> PolarsResult<Option<f64>> {
match self.mean() {
Some(mean) => moment_precomputed_mean(self, moment, mean),
None => Ok(None),
}
fn moment(s: &Series, moment: usize) -> PolarsResult<Option<f64>> {
match s.mean() {
Some(mean) => moment_precomputed_mean(s, moment, mean),
None => Ok(None),
}
}

#[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(())
}
Expand Down
1 change: 1 addition & 0 deletions crates/polars-ops/src/series/ops/rolling.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
2 changes: 1 addition & 1 deletion crates/polars-plan/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
2 changes: 1 addition & 1 deletion crates/polars/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down

0 comments on commit 2a9a9ea

Please sign in to comment.