Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-python/src/series/aggregation.rs
8362 views
1
use DataType::*;
2
use polars::prelude::*;
3
use pyo3::prelude::*;
4
5
use super::PySeries;
6
use crate::conversion::Wrap;
7
use crate::utils::EnterPolarsExt;
8
9
fn scalar_to_py(scalar: PyResult<Scalar>, py: Python<'_>) -> PyResult<Bound<'_, PyAny>> {
10
Wrap(scalar?.as_any_value()).into_pyobject(py)
11
}
12
13
#[pymethods]
14
impl 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
// For non-numeric output types we require mean_reduce.
63
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
// 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: Bound<'py, PyAny>,
89
interpolation: Wrap<QuantileMethod>,
90
) -> PyResult<Bound<'py, PyAny>> {
91
// Accept either a single float or a list of floats
92
if let Ok(q_float) = quantile.extract::<f64>() {
93
// Single quantile: use quantile_reduce
94
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
// Multiple quantiles: use quantiles_reduce
100
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
}
165
166