Skip to main content

polars_python/map/
series.rs

1use polars::prelude::*;
2use pyo3::prelude::*;
3use pyo3::types::{PyNone, PyTuple};
4
5use super::*;
6use crate::error::PyPolarsErr;
7use crate::prelude::ObjectValue;
8use crate::{PySeries, Wrap};
9
10pub trait ApplyLambdaGeneric<'py> {
11    fn apply_generic(
12        &self,
13        py: Python<'py>,
14        lambda: &Bound<'py, PyAny>,
15        skip_nulls: bool,
16    ) -> PyResult<Series>;
17
18    fn apply_generic_with_dtype(
19        &self,
20        py: Python<'py>,
21        lambda: &Bound<'py, PyAny>,
22        datatype: &DataType,
23        skip_nulls: bool,
24    ) -> PyResult<Series>;
25}
26
27fn call_and_collect_anyvalues<'py, T, I>(
28    py: Python<'py>,
29    lambda: &Bound<'py, PyAny>,
30    len: usize,
31    iter: I,
32    skip_nulls: bool,
33) -> PyResult<Vec<AnyValue<'static>>>
34where
35    T: IntoPyObject<'py>,
36    I: Iterator<Item = Option<T>>,
37{
38    let mut avs = Vec::with_capacity(len);
39    for opt_val in iter {
40        let arg = match opt_val {
41            None if skip_nulls => {
42                avs.push(AnyValue::Null);
43                continue;
44            },
45            None => PyTuple::new(py, [PyNone::get(py)])?,
46            Some(val) => PyTuple::new(py, [val])?,
47        };
48        let out = lambda.call1(arg)?;
49        let av: Option<Wrap<AnyValue>> = if out.is_none() {
50            Ok(None)
51        } else {
52            out.extract().map(Some)
53        }?;
54        avs.push(av.map(|w| w.0).unwrap_or(AnyValue::Null));
55    }
56    Ok(avs)
57}
58
59impl<'py> ApplyLambdaGeneric<'py> for BooleanChunked {
60    fn apply_generic(
61        &self,
62        py: Python<'py>,
63        lambda: &Bound<'py, PyAny>,
64        skip_nulls: bool,
65    ) -> PyResult<Series> {
66        let avs = call_and_collect_anyvalues(py, lambda, self.len(), self.into_iter(), skip_nulls)?;
67        Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(PyPolarsErr::from)?)
68    }
69
70    fn apply_generic_with_dtype(
71        &self,
72        py: Python<'py>,
73        lambda: &Bound<'py, PyAny>,
74        datatype: &DataType,
75        skip_nulls: bool,
76    ) -> PyResult<Series> {
77        let avs = call_and_collect_anyvalues(py, lambda, self.len(), self.into_iter(), skip_nulls)?;
78        Ok(
79            Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
80                .map_err(PyPolarsErr::from)?,
81        )
82    }
83}
84
85impl<'py, T> ApplyLambdaGeneric<'py> for ChunkedArray<T>
86where
87    T: PyPolarsNumericType,
88    T::Native: IntoPyObject<'py> + for<'a> FromPyObject<'a, 'py>,
89{
90    fn apply_generic(
91        &self,
92        py: Python<'py>,
93        lambda: &Bound<'py, PyAny>,
94        skip_nulls: bool,
95    ) -> PyResult<Series> {
96        let avs = call_and_collect_anyvalues(py, lambda, self.len(), self.into_iter(), skip_nulls)?;
97        Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(PyPolarsErr::from)?)
98    }
99
100    fn apply_generic_with_dtype(
101        &self,
102        py: Python<'py>,
103        lambda: &Bound<'py, PyAny>,
104        datatype: &DataType,
105        skip_nulls: bool,
106    ) -> PyResult<Series> {
107        let avs = call_and_collect_anyvalues(py, lambda, self.len(), self.into_iter(), skip_nulls)?;
108        Ok(
109            Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
110                .map_err(PyPolarsErr::from)?,
111        )
112    }
113}
114
115impl<'py> ApplyLambdaGeneric<'py> for StringChunked {
116    fn apply_generic(
117        &self,
118        py: Python<'py>,
119        lambda: &Bound<'py, PyAny>,
120        skip_nulls: bool,
121    ) -> PyResult<Series> {
122        let avs = call_and_collect_anyvalues(py, lambda, self.len(), self.into_iter(), skip_nulls)?;
123        Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(PyPolarsErr::from)?)
124    }
125
126    fn apply_generic_with_dtype(
127        &self,
128        py: Python<'py>,
129        lambda: &Bound<'py, PyAny>,
130        datatype: &DataType,
131        skip_nulls: bool,
132    ) -> PyResult<Series> {
133        let avs = call_and_collect_anyvalues(py, lambda, self.len(), self.into_iter(), skip_nulls)?;
134        Ok(
135            Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
136                .map_err(PyPolarsErr::from)?,
137        )
138    }
139}
140
141impl<'py> ApplyLambdaGeneric<'py> for ListChunked {
142    fn apply_generic(
143        &self,
144        py: Python<'py>,
145        lambda: &Bound<'py, PyAny>,
146        skip_nulls: bool,
147    ) -> PyResult<Series> {
148        let it = self.into_iter().map(|opt_s| opt_s.map(Wrap));
149        let avs = call_and_collect_anyvalues(py, lambda, self.len(), it, skip_nulls)?;
150        Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(PyPolarsErr::from)?)
151    }
152
153    fn apply_generic_with_dtype(
154        &self,
155        py: Python<'py>,
156        lambda: &Bound<'py, PyAny>,
157        datatype: &DataType,
158        skip_nulls: bool,
159    ) -> PyResult<Series> {
160        let it = self.into_iter().map(|opt_s| opt_s.map(Wrap));
161        let avs = call_and_collect_anyvalues(py, lambda, self.len(), it, skip_nulls)?;
162        Ok(
163            Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
164                .map_err(PyPolarsErr::from)?,
165        )
166    }
167}
168
169#[cfg(feature = "dtype-array")]
170impl<'py> ApplyLambdaGeneric<'py> for ArrayChunked {
171    fn apply_generic(
172        &self,
173        py: Python<'py>,
174        lambda: &Bound<'py, PyAny>,
175        skip_nulls: bool,
176    ) -> PyResult<Series> {
177        let it = self.into_iter().map(|opt_s| Some(PySeries::new(opt_s?)));
178        let avs = call_and_collect_anyvalues(py, lambda, self.len(), it, skip_nulls)?;
179        Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(PyPolarsErr::from)?)
180    }
181
182    fn apply_generic_with_dtype(
183        &self,
184        py: Python<'py>,
185        lambda: &Bound<'py, PyAny>,
186        datatype: &DataType,
187        skip_nulls: bool,
188    ) -> PyResult<Series> {
189        let it = self.into_iter().map(|opt_s| Some(PySeries::new(opt_s?)));
190        let avs = call_and_collect_anyvalues(py, lambda, self.len(), it, skip_nulls)?;
191        Ok(
192            Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
193                .map_err(PyPolarsErr::from)?,
194        )
195    }
196}
197
198#[cfg(feature = "object")]
199impl<'py> ApplyLambdaGeneric<'py> for ObjectChunked<ObjectValue> {
200    fn apply_generic(
201        &self,
202        py: Python<'py>,
203        lambda: &Bound<'py, PyAny>,
204        skip_nulls: bool,
205    ) -> PyResult<Series> {
206        let avs = call_and_collect_anyvalues(py, lambda, self.len(), self.into_iter(), skip_nulls)?;
207        Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(PyPolarsErr::from)?)
208    }
209
210    fn apply_generic_with_dtype(
211        &self,
212        py: Python<'py>,
213        lambda: &Bound<'py, PyAny>,
214        datatype: &DataType,
215        skip_nulls: bool,
216    ) -> PyResult<Series> {
217        let avs = call_and_collect_anyvalues(py, lambda, self.len(), self.into_iter(), skip_nulls)?;
218        Ok(
219            Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
220                .map_err(PyPolarsErr::from)?,
221        )
222    }
223}
224
225impl<'py> ApplyLambdaGeneric<'py> for StructChunked {
226    fn apply_generic(
227        &self,
228        py: Python<'py>,
229        lambda: &Bound<'py, PyAny>,
230        skip_nulls: bool,
231    ) -> PyResult<Series> {
232        let it = (0..self.len())
233            .map(|i| unsafe { self.get_any_value_unchecked(i).null_to_none().map(Wrap) });
234        let avs = call_and_collect_anyvalues(py, lambda, self.len(), it, skip_nulls)?;
235        Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(PyPolarsErr::from)?)
236    }
237
238    fn apply_generic_with_dtype(
239        &self,
240        py: Python<'py>,
241        lambda: &Bound<'py, PyAny>,
242        datatype: &DataType,
243        skip_nulls: bool,
244    ) -> PyResult<Series> {
245        let it = (0..self.len())
246            .map(|i| unsafe { self.get_any_value_unchecked(i).null_to_none().map(Wrap) });
247        let avs = call_and_collect_anyvalues(py, lambda, self.len(), it, skip_nulls)?;
248        Ok(
249            Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
250                .map_err(PyPolarsErr::from)?,
251        )
252    }
253}
254
255impl<'py> ApplyLambdaGeneric<'py> for BinaryChunked {
256    fn apply_generic(
257        &self,
258        py: Python<'py>,
259        lambda: &Bound<'py, PyAny>,
260        skip_nulls: bool,
261    ) -> PyResult<Series> {
262        let avs = call_and_collect_anyvalues(py, lambda, self.len(), self.into_iter(), skip_nulls)?;
263        Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(PyPolarsErr::from)?)
264    }
265
266    fn apply_generic_with_dtype(
267        &self,
268        py: Python<'py>,
269        lambda: &Bound<'py, PyAny>,
270        datatype: &DataType,
271        skip_nulls: bool,
272    ) -> PyResult<Series> {
273        let avs = call_and_collect_anyvalues(py, lambda, self.len(), self.into_iter(), skip_nulls)?;
274        Ok(
275            Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
276                .map_err(PyPolarsErr::from)?,
277        )
278    }
279}
280
281impl<'py, L, P> ApplyLambdaGeneric<'py> for Logical<L, P>
282where
283    L: PolarsDataType,
284    P: PolarsDataType,
285    Logical<L, P>: LogicalType,
286{
287    fn apply_generic(
288        &self,
289        py: Python<'py>,
290        lambda: &Bound<'py, PyAny>,
291        skip_nulls: bool,
292    ) -> PyResult<Series> {
293        let it = (0..self.len())
294            .map(|i| unsafe { self.get_any_value_unchecked(i).null_to_none().map(Wrap) });
295        let avs = call_and_collect_anyvalues(py, lambda, self.len(), it, skip_nulls)?;
296        Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(PyPolarsErr::from)?)
297    }
298
299    fn apply_generic_with_dtype(
300        &self,
301        py: Python<'py>,
302        lambda: &Bound<'py, PyAny>,
303        datatype: &DataType,
304        skip_nulls: bool,
305    ) -> PyResult<Series> {
306        let it = (0..self.len())
307            .map(|i| unsafe { self.get_any_value_unchecked(i).null_to_none().map(Wrap) });
308        let avs = call_and_collect_anyvalues(py, lambda, self.len(), it, skip_nulls)?;
309        Ok(
310            Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
311                .map_err(PyPolarsErr::from)?,
312        )
313    }
314}
315
316impl<'py> ApplyLambdaGeneric<'py> for NullChunked {
317    fn apply_generic(
318        &self,
319        py: Python<'py>,
320        lambda: &Bound<'py, PyAny>,
321        skip_nulls: bool,
322    ) -> PyResult<Series> {
323        let it = (0..self.len()).map(|_| None::<Wrap<AnyValue<'static>>>);
324        let avs = call_and_collect_anyvalues(py, lambda, self.len(), it, skip_nulls)?;
325        Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(PyPolarsErr::from)?)
326    }
327
328    fn apply_generic_with_dtype(
329        &self,
330        py: Python<'py>,
331        lambda: &Bound<'py, PyAny>,
332        datatype: &DataType,
333        skip_nulls: bool,
334    ) -> PyResult<Series> {
335        let it = (0..self.len()).map(|_| None::<Wrap<AnyValue<'static>>>);
336        let avs = call_and_collect_anyvalues(py, lambda, self.len(), it, skip_nulls)?;
337        Ok(
338            Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
339                .map_err(PyPolarsErr::from)?,
340        )
341    }
342}
343
344impl<'py> ApplyLambdaGeneric<'py> for ExtensionChunked {
345    fn apply_generic(
346        &self,
347        _py: Python<'py>,
348        _lambda: &Bound<'py, PyAny>,
349        _skip_nulls: bool,
350    ) -> PyResult<Series> {
351        unreachable!()
352    }
353
354    fn apply_generic_with_dtype(
355        &self,
356        _py: Python<'py>,
357        _lambda: &Bound<'py, PyAny>,
358        _datatype: &DataType,
359        _skip_nulls: bool,
360    ) -> PyResult<Series> {
361        unreachable!()
362    }
363}