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.bool()?;
18            PolarsResult::Ok(if ignore_nulls {
19                Some(s.any())
20            } else {
21                s.any_kleene()
22            })
23        })
24    }
25
26    fn all(&self, py: Python<'_>, ignore_nulls: bool) -> PyResult<Option<bool>> {
27        py.enter_polars(|| {
28            let s = self.series.bool()?;
29            PolarsResult::Ok(if ignore_nulls {
30                Some(s.all())
31            } else {
32                s.all_kleene()
33            })
34        })
35    }
36
37    fn arg_max(&self, py: Python) -> PyResult<Option<usize>> {
38        py.enter_polars_ok(|| self.series.arg_max())
39    }
40
41    fn arg_min(&self, py: Python) -> PyResult<Option<usize>> {
42        py.enter_polars_ok(|| self.series.arg_min())
43    }
44
45    fn min<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
46        scalar_to_py(py.enter_polars(|| self.series.min_reduce()), py)
47    }
48
49    fn max<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
50        scalar_to_py(py.enter_polars(|| self.series.max_reduce()), py)
51    }
52
53    fn mean<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
54        match self.series.dtype() {
55            Boolean => scalar_to_py(
56                py.enter_polars_ok(|| self.series.cast(&DataType::UInt8).unwrap().mean_reduce()),
57                py,
58            ),
59            // For non-numeric output types we require mean_reduce.
60            dt if dt.is_temporal() => {
61                scalar_to_py(py.enter_polars_ok(|| self.series.mean_reduce()), py)
62            },
63            _ => Ok(self.series.mean().into_pyobject(py)?),
64        }
65    }
66
67    fn median<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
68        match self.series.dtype() {
69            Boolean => scalar_to_py(
70                py.enter_polars(|| self.series.cast(&DataType::UInt8).unwrap().median_reduce()),
71                py,
72            ),
73            // For non-numeric output types we require median_reduce.
74            dt if dt.is_temporal() => {
75                scalar_to_py(py.enter_polars(|| self.series.median_reduce()), py)
76            },
77            _ => Ok(self.series.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.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(|| self.series.quantile_reduce(quantile, interpolation.0)),
93            py,
94        )
95    }
96
97    fn std<'py>(&self, py: Python<'py>, ddof: u8) -> PyResult<Bound<'py, PyAny>> {
98        scalar_to_py(py.enter_polars(|| self.series.std_reduce(ddof)), py)
99    }
100
101    fn var<'py>(&self, py: Python<'py>, ddof: u8) -> PyResult<Bound<'py, PyAny>> {
102        scalar_to_py(py.enter_polars(|| self.series.var_reduce(ddof)), py)
103    }
104
105    fn sum<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
106        scalar_to_py(py.enter_polars(|| self.series.sum_reduce()), py)
107    }
108
109    fn first<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
110        scalar_to_py(py.enter_polars_ok(|| self.series.first()), py)
111    }
112
113    fn last<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
114        scalar_to_py(py.enter_polars_ok(|| self.series.last()), py)
115    }
116
117    #[cfg(feature = "approx_unique")]
118    fn approx_n_unique(&self, py: Python) -> PyResult<IdxSize> {
119        py.enter_polars(|| self.series.approx_n_unique())
120    }
121
122    #[cfg(feature = "bitwise")]
123    fn bitwise_and<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
124        scalar_to_py(py.enter_polars(|| self.series.and_reduce()), py)
125    }
126
127    #[cfg(feature = "bitwise")]
128    fn bitwise_or<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
129        scalar_to_py(py.enter_polars(|| self.series.or_reduce()), py)
130    }
131
132    #[cfg(feature = "bitwise")]
133    fn bitwise_xor<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
134        scalar_to_py(py.enter_polars(|| self.series.xor_reduce()), py)
135    }
136}