Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-python/src/series/map.rs
7889 views
1
use pyo3::Python;
2
use pyo3::prelude::*;
3
use pyo3::types::{PyNone, PyTuple};
4
5
use super::PySeries;
6
use crate::error::PyPolarsErr;
7
use crate::map::series::ApplyLambdaGeneric;
8
use crate::prelude::*;
9
#[cfg(feature = "object")]
10
use crate::series::construction::series_from_objects;
11
use crate::{apply_all_polars_dtypes, raise_err};
12
13
#[pymethods]
14
impl PySeries {
15
#[pyo3(signature = (function, return_dtype, skip_nulls))]
16
fn map_elements(
17
&self,
18
function: &Bound<PyAny>,
19
return_dtype: Option<Wrap<DataType>>,
20
skip_nulls: bool,
21
) -> PyResult<PySeries> {
22
let series = self.series.read().clone(); // Clone so we don't deadlock on re-entrance.
23
let series = series.to_storage();
24
25
if skip_nulls && (series.null_count() == series.len()) {
26
if let Some(return_dtype) = return_dtype {
27
return Ok(
28
Series::full_null(series.name().clone(), series.len(), &return_dtype.0).into(),
29
);
30
}
31
let msg = "The output type of the 'map_elements' function cannot be determined.\n\
32
The function was never called because 'skip_nulls=True' and all values are null.\n\
33
Consider setting 'skip_nulls=False' or setting the 'return_dtype'.";
34
raise_err!(msg, ComputeError)
35
}
36
37
let return_dtype = return_dtype.map(|dt| dt.0);
38
39
Python::attach(|py| {
40
let s = match &return_dtype {
41
#[cfg(feature = "object")]
42
Some(DataType::Object(_)) => {
43
// If the return dtype is Object we should not go through AnyValue.
44
call_and_collect_objects(
45
py,
46
series.name().clone(),
47
function,
48
series.len(),
49
series.iter().map(|av| av.null_to_none().map(Wrap)),
50
skip_nulls,
51
)
52
},
53
Some(return_dtype) => {
54
apply_all_polars_dtypes!(
55
series,
56
apply_generic_with_dtype,
57
py,
58
function,
59
return_dtype,
60
skip_nulls
61
)
62
},
63
None => apply_all_polars_dtypes!(series, apply_generic, py, function, skip_nulls),
64
};
65
s.map(PySeries::from)
66
})
67
}
68
}
69
70
#[cfg(feature = "object")]
71
fn call_and_collect_objects<'py, T, I>(
72
py: Python<'py>,
73
name: PlSmallStr,
74
lambda: &Bound<'py, PyAny>,
75
len: usize,
76
iter: I,
77
skip_nulls: bool,
78
) -> PyResult<Series>
79
where
80
T: IntoPyObject<'py>,
81
I: Iterator<Item = Option<T>>,
82
{
83
let mut objects = Vec::with_capacity(len);
84
for opt_val in iter {
85
let arg = match opt_val {
86
None if skip_nulls => {
87
objects.push(ObjectValue {
88
inner: PyNone::get(py).to_owned().unbind().into_any(),
89
});
90
continue;
91
},
92
None => PyTuple::new(py, [PyNone::get(py)])?,
93
Some(val) => PyTuple::new(py, [val])?,
94
};
95
let out = lambda.call1(arg)?;
96
objects.push(ObjectValue {
97
inner: out.unbind(),
98
});
99
}
100
Ok(series_from_objects(py, name, objects))
101
}
102
103