polars_python/series/
aggregation.rs

1use DataType::*;
2use polars::prelude::*;
3use pyo3::prelude::*;
4
5use super::PySeries;
6use crate::conversion::Wrap;
7use crate::utils::EnterPolarsExt;
8
9fn scalar_to_py(scalar: PyResult<Scalar>, py: Python<'_>) -> PyResult<Bound<'_, PyAny>> {
10    Wrap(scalar?.as_any_value()).into_pyobject(py)
11}
12
13#[pymethods]
14impl PySeries {
15    fn any(&self, py: Python<'_>, ignore_nulls: bool) -> PyResult<Option<bool>> {
16        py.enter_polars(|| {
17            let s = self.series.read();
18            let s = s.bool()?;
19            PolarsResult::Ok(if ignore_nulls {
20                Some(s.any())
21            } else {
22                s.any_kleene()
23            })
24        })
25    }
26
27    fn all(&self, py: Python<'_>, ignore_nulls: bool) -> PyResult<Option<bool>> {
28        py.enter_polars(|| {
29            let s = self.series.read();
30            let s = s.bool()?;
31            PolarsResult::Ok(if ignore_nulls {
32                Some(s.all())
33            } else {
34                s.all_kleene()
35            })
36        })
37    }
38
39    fn arg_max(&self, py: Python) -> PyResult<Option<usize>> {
40        py.enter_polars_ok(|| self.series.read().arg_max())
41    }
42
43    fn arg_min(&self, py: Python) -> PyResult<Option<usize>> {
44        py.enter_polars_ok(|| self.series.read().arg_min())
45    }
46
47    fn min<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
48        scalar_to_py(py.enter_polars(|| self.series.read().min_reduce()), py)
49    }
50
51    fn max<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
52        scalar_to_py(py.enter_polars(|| self.series.read().max_reduce()), py)
53    }
54
55    fn mean<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
56        let s = self.series.read();
57        match s.dtype() {
58            Boolean => scalar_to_py(
59                py.enter_polars_ok(|| s.cast(&DataType::UInt8).unwrap().mean_reduce()),
60                py,
61            ),
62            // For non-numeric output types we require mean_reduce.
63            dt if dt.is_temporal() => scalar_to_py(py.enter_polars_ok(|| s.mean_reduce()), py),
64            _ => Ok(s.mean().into_pyobject(py)?),
65        }
66    }
67
68    fn median<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
69        let s = self.series.read();
70        match s.dtype() {
71            Boolean => scalar_to_py(
72                py.enter_polars(|| s.cast(&DataType::UInt8).unwrap().median_reduce()),
73                py,
74            ),
75            // For non-numeric output types we require median_reduce.
76            dt if dt.is_temporal() => scalar_to_py(py.enter_polars(|| s.median_reduce()), py),
77            _ => Ok(s.median().into_pyobject(py)?),
78        }
79    }
80
81    fn product<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
82        scalar_to_py(py.enter_polars(|| self.series.read().product()), py)
83    }
84
85    fn quantile<'py>(
86        &self,
87        py: Python<'py>,
88        quantile: f64,
89        interpolation: Wrap<QuantileMethod>,
90    ) -> PyResult<Bound<'py, PyAny>> {
91        scalar_to_py(
92            py.enter_polars(|| {
93                self.series
94                    .read()
95                    .quantile_reduce(quantile, interpolation.0)
96            }),
97            py,
98        )
99    }
100
101    fn std<'py>(&self, py: Python<'py>, ddof: u8) -> PyResult<Bound<'py, PyAny>> {
102        scalar_to_py(py.enter_polars(|| self.series.read().std_reduce(ddof)), py)
103    }
104
105    fn var<'py>(&self, py: Python<'py>, ddof: u8) -> PyResult<Bound<'py, PyAny>> {
106        scalar_to_py(py.enter_polars(|| self.series.read().var_reduce(ddof)), py)
107    }
108
109    fn sum<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
110        scalar_to_py(py.enter_polars(|| self.series.read().sum_reduce()), py)
111    }
112
113    fn first<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
114        scalar_to_py(py.enter_polars_ok(|| self.series.read().first()), py)
115    }
116
117    fn last<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
118        scalar_to_py(py.enter_polars_ok(|| self.series.read().last()), py)
119    }
120
121    #[cfg(feature = "approx_unique")]
122    fn approx_n_unique(&self, py: Python) -> PyResult<IdxSize> {
123        py.enter_polars(|| self.series.read().approx_n_unique())
124    }
125
126    #[cfg(feature = "bitwise")]
127    fn bitwise_and<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
128        scalar_to_py(py.enter_polars(|| self.series.read().and_reduce()), py)
129    }
130
131    #[cfg(feature = "bitwise")]
132    fn bitwise_or<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
133        scalar_to_py(py.enter_polars(|| self.series.read().or_reduce()), py)
134    }
135
136    #[cfg(feature = "bitwise")]
137    fn bitwise_xor<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
138        scalar_to_py(py.enter_polars(|| self.series.read().xor_reduce()), py)
139    }
140}