polars_python/series/
aggregation.rs1use 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(|| s.cast(&DataType::UInt8).unwrap().mean_reduce()),
60 py,
61 ),
62 dt if dt.is_temporal() => scalar_to_py(py.enter_polars(|| 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 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: Bound<'py, PyAny>,
89 interpolation: Wrap<QuantileMethod>,
90 ) -> PyResult<Bound<'py, PyAny>> {
91 if let Ok(q_float) = quantile.extract::<f64>() {
93 scalar_to_py(
95 py.enter_polars(|| self.series.read().quantile_reduce(q_float, interpolation.0)),
96 py,
97 )
98 } else if let Ok(q_list) = quantile.extract::<Vec<f64>>() {
99 scalar_to_py(
101 py.enter_polars(|| {
102 self.series
103 .read()
104 .quantiles_reduce(&q_list, interpolation.0)
105 }),
106 py,
107 )
108 } else {
109 Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
110 "quantile must be a float or a list of floats",
111 ))
112 }
113 }
114
115 fn std<'py>(&self, py: Python<'py>, ddof: u8) -> PyResult<Bound<'py, PyAny>> {
116 scalar_to_py(py.enter_polars(|| self.series.read().std_reduce(ddof)), py)
117 }
118
119 fn var<'py>(&self, py: Python<'py>, ddof: u8) -> PyResult<Bound<'py, PyAny>> {
120 scalar_to_py(py.enter_polars(|| self.series.read().var_reduce(ddof)), py)
121 }
122
123 fn sum<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
124 scalar_to_py(py.enter_polars(|| self.series.read().sum_reduce()), py)
125 }
126
127 fn first<'py>(&self, py: Python<'py>, ignore_nulls: bool) -> PyResult<Bound<'py, PyAny>> {
128 let result = if ignore_nulls {
129 py.enter_polars_ok(|| self.series.read().first_non_null())
130 } else {
131 py.enter_polars_ok(|| self.series.read().first())
132 };
133 scalar_to_py(result, py)
134 }
135
136 fn last<'py>(&self, py: Python<'py>, ignore_nulls: bool) -> PyResult<Bound<'py, PyAny>> {
137 let result = if ignore_nulls {
138 py.enter_polars_ok(|| self.series.read().last_non_null())
139 } else {
140 py.enter_polars_ok(|| self.series.read().last())
141 };
142 scalar_to_py(result, py)
143 }
144
145 #[cfg(feature = "approx_unique")]
146 fn approx_n_unique(&self, py: Python) -> PyResult<IdxSize> {
147 py.enter_polars(|| self.series.read().approx_n_unique())
148 }
149
150 #[cfg(feature = "bitwise")]
151 fn bitwise_and<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
152 scalar_to_py(py.enter_polars(|| self.series.read().and_reduce()), py)
153 }
154
155 #[cfg(feature = "bitwise")]
156 fn bitwise_or<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
157 scalar_to_py(py.enter_polars(|| self.series.read().or_reduce()), py)
158 }
159
160 #[cfg(feature = "bitwise")]
161 fn bitwise_xor<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
162 scalar_to_py(py.enter_polars(|| self.series.read().xor_reduce()), py)
163 }
164}