polars_python/expr/
list.rs1use std::borrow::Cow;
2
3use polars::prelude::*;
4use polars::series::ops::NullBehavior;
5use polars_utils::pl_str::PlSmallStr;
6use pyo3::prelude::*;
7use pyo3::types::PySequence;
8
9use crate::PyExpr;
10use crate::conversion::Wrap;
11
12#[pymethods]
13impl PyExpr {
14 #[cfg(feature = "list_any_all")]
15 fn list_all(&self) -> Self {
16 self.inner.clone().list().all().into()
17 }
18
19 #[cfg(feature = "list_any_all")]
20 fn list_any(&self) -> Self {
21 self.inner.clone().list().any().into()
22 }
23
24 fn list_arg_max(&self) -> Self {
25 self.inner.clone().list().arg_max().into()
26 }
27
28 fn list_arg_min(&self) -> Self {
29 self.inner.clone().list().arg_min().into()
30 }
31
32 #[cfg(feature = "is_in")]
33 fn list_contains(&self, other: PyExpr, nulls_equal: bool) -> Self {
34 self.inner
35 .clone()
36 .list()
37 .contains(other.inner, nulls_equal)
38 .into()
39 }
40
41 #[cfg(feature = "list_count")]
42 fn list_count_matches(&self, expr: PyExpr) -> Self {
43 self.inner.clone().list().count_matches(expr.inner).into()
44 }
45
46 fn list_diff(&self, n: i64, null_behavior: Wrap<NullBehavior>) -> PyResult<Self> {
47 Ok(self.inner.clone().list().diff(n, null_behavior.0).into())
48 }
49
50 fn list_eval(&self, expr: PyExpr, _parallel: bool) -> Self {
51 self.inner.clone().list().eval(expr.inner).into()
52 }
53
54 #[cfg(feature = "list_filter")]
55 fn list_filter(&self, predicate: PyExpr) -> Self {
56 self.inner
57 .clone()
58 .list()
59 .eval(Expr::Column(PlSmallStr::EMPTY).filter(predicate.inner))
60 .into()
61 }
62
63 fn list_get(&self, index: PyExpr, null_on_oob: bool) -> Self {
64 self.inner
65 .clone()
66 .list()
67 .get(index.inner, null_on_oob)
68 .into()
69 }
70
71 fn list_join(&self, separator: PyExpr, ignore_nulls: bool) -> Self {
72 self.inner
73 .clone()
74 .list()
75 .join(separator.inner, ignore_nulls)
76 .into()
77 }
78
79 fn list_len(&self) -> Self {
80 self.inner.clone().list().len().into()
81 }
82
83 fn list_max(&self) -> Self {
84 self.inner.clone().list().max().into()
85 }
86
87 fn list_mean(&self) -> Self {
88 self.inner.clone().list().mean().into()
89 }
90
91 fn list_median(&self) -> Self {
92 self.inner.clone().list().median().into()
93 }
94
95 fn list_std(&self, ddof: u8) -> Self {
96 self.inner.clone().list().std(ddof).into()
97 }
98
99 fn list_var(&self, ddof: u8) -> Self {
100 self.inner.clone().list().var(ddof).into()
101 }
102
103 fn list_min(&self) -> Self {
104 self.inner.clone().list().min().into()
105 }
106
107 fn list_reverse(&self) -> Self {
108 self.inner.clone().list().reverse().into()
109 }
110
111 fn list_shift(&self, periods: PyExpr) -> Self {
112 self.inner.clone().list().shift(periods.inner).into()
113 }
114
115 #[pyo3(signature = (offset, length=None))]
116 fn list_slice(&self, offset: PyExpr, length: Option<PyExpr>) -> Self {
117 let length = match length {
118 Some(i) => i.inner,
119 None => lit(i64::MAX),
120 };
121 self.inner.clone().list().slice(offset.inner, length).into()
122 }
123
124 fn list_tail(&self, n: PyExpr) -> Self {
125 self.inner.clone().list().tail(n.inner).into()
126 }
127
128 fn list_sort(&self, descending: bool, nulls_last: bool) -> Self {
129 self.inner
130 .clone()
131 .list()
132 .sort(
133 SortOptions::default()
134 .with_order_descending(descending)
135 .with_nulls_last(nulls_last),
136 )
137 .into()
138 }
139
140 fn list_sum(&self) -> Self {
141 self.inner.clone().list().sum().into()
142 }
143
144 #[cfg(feature = "list_drop_nulls")]
145 fn list_drop_nulls(&self) -> Self {
146 self.inner.clone().list().drop_nulls().into()
147 }
148
149 #[cfg(feature = "list_sample")]
150 #[pyo3(signature = (n, with_replacement, shuffle, seed=None))]
151 fn list_sample_n(
152 &self,
153 n: PyExpr,
154 with_replacement: bool,
155 shuffle: bool,
156 seed: Option<u64>,
157 ) -> Self {
158 self.inner
159 .clone()
160 .list()
161 .sample_n(n.inner, with_replacement, shuffle, seed)
162 .into()
163 }
164
165 #[cfg(feature = "list_sample")]
166 #[pyo3(signature = (fraction, with_replacement, shuffle, seed=None))]
167 fn list_sample_fraction(
168 &self,
169 fraction: PyExpr,
170 with_replacement: bool,
171 shuffle: bool,
172 seed: Option<u64>,
173 ) -> Self {
174 self.inner
175 .clone()
176 .list()
177 .sample_fraction(fraction.inner, with_replacement, shuffle, seed)
178 .into()
179 }
180
181 #[cfg(feature = "list_gather")]
182 fn list_gather(&self, index: PyExpr, null_on_oob: bool) -> Self {
183 self.inner
184 .clone()
185 .list()
186 .gather(index.inner, null_on_oob)
187 .into()
188 }
189
190 #[cfg(feature = "list_gather")]
191 fn list_gather_every(&self, n: PyExpr, offset: PyExpr) -> Self {
192 self.inner
193 .clone()
194 .list()
195 .gather_every(n.inner, offset.inner)
196 .into()
197 }
198
199 fn list_to_array(&self, width: usize) -> Self {
200 self.inner.clone().list().to_array(width).into()
201 }
202
203 #[pyo3(signature = (width_strat, name_gen, upper_bound))]
204 fn list_to_struct(
205 &self,
206 width_strat: Wrap<ListToStructWidthStrategy>,
207 name_gen: Option<PyObject>,
208 upper_bound: Option<usize>,
209 ) -> PyResult<Self> {
210 let name_gen = name_gen.map(|lambda| {
211 NameGenerator::from_func(move |idx: usize| {
212 Python::with_gil(|py| {
213 let out = lambda.call1(py, (idx,)).unwrap();
214 let out: PlSmallStr = out.extract::<Cow<str>>(py).unwrap().as_ref().into();
215 out
216 })
217 })
218 });
219
220 Ok(self
221 .inner
222 .clone()
223 .list()
224 .to_struct(ListToStructArgs::InferWidth {
225 infer_field_strategy: width_strat.0,
226 get_index_name: name_gen,
227 max_fields: upper_bound,
228 })
229 .into())
230 }
231
232 #[pyo3(signature = (names))]
233 fn list_to_struct_fixed_width(&self, names: Bound<'_, PySequence>) -> PyResult<Self> {
234 Ok(self
235 .inner
236 .clone()
237 .list()
238 .to_struct(ListToStructArgs::FixedWidth(
239 names
240 .try_iter()?
241 .map(|x| Ok(x?.extract::<Wrap<PlSmallStr>>()?.0))
242 .collect::<PyResult<Arc<[_]>>>()?,
243 ))
244 .into())
245 }
246
247 fn list_n_unique(&self) -> Self {
248 self.inner.clone().list().n_unique().into()
249 }
250
251 fn list_unique(&self, maintain_order: bool) -> Self {
252 let e = self.inner.clone();
253
254 if maintain_order {
255 e.list().unique_stable().into()
256 } else {
257 e.list().unique().into()
258 }
259 }
260
261 #[cfg(feature = "list_sets")]
262 fn list_set_operation(&self, other: PyExpr, operation: Wrap<SetOperation>) -> Self {
263 let e = self.inner.clone().list();
264 match operation.0 {
265 SetOperation::Intersection => e.set_intersection(other.inner),
266 SetOperation::Difference => e.set_difference(other.inner),
267 SetOperation::Union => e.union(other.inner),
268 SetOperation::SymmetricDifference => e.set_symmetric_difference(other.inner),
269 }
270 .into()
271 }
272}