polars-df 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "polars"
3
- version = "0.18.0"
3
+ version = "0.20.0"
4
4
  license = "MIT"
5
5
  authors = ["Andrew Kane <andrew@ankane.org>"]
6
6
  edition = "2021"
@@ -12,22 +12,22 @@ crate-type = ["cdylib"]
12
12
 
13
13
  [dependencies]
14
14
  ahash = "0.8"
15
- arrow = { package = "polars-arrow", version = "=0.47.1" }
15
+ arrow = { package = "polars-arrow", version = "=0.49.1" }
16
16
  bytes = "1"
17
17
  chrono = "0.4"
18
18
  either = "1.8"
19
19
  magnus = "0.7"
20
20
  num-traits = "0.2"
21
- polars-core = "=0.47.1"
22
- polars-plan = "=0.47.1"
23
- polars-parquet = "=0.47.1"
24
- polars-utils = "=0.47.1"
21
+ polars-core = "=0.49.1"
22
+ polars-plan = "=0.49.1"
23
+ polars-parquet = "=0.49.1"
24
+ polars-utils = "=0.49.1"
25
25
  rayon = "1.9"
26
26
  regex = "1"
27
27
  serde_json = "1"
28
28
 
29
29
  [dependencies.polars]
30
- version = "=0.47.1"
30
+ version = "=0.49.1"
31
31
  features = [
32
32
  "abs",
33
33
  "approx_unique",
@@ -173,7 +173,7 @@ pub(crate) fn rb_object_to_any_value<'s>(ob: Value, strict: bool) -> RbResult<An
173
173
  }
174
174
 
175
175
  fn get_struct(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
176
- let dict = RHash::from_value(ob).unwrap();
176
+ let dict = RHash::try_convert(ob)?;
177
177
  let len = dict.len();
178
178
  let mut keys = Vec::with_capacity(len);
179
179
  let mut vals = Vec::with_capacity(len);
@@ -210,7 +210,10 @@ impl IntoValue for Wrap<DataType> {
210
210
  DataType::Datetime(tu, tz) => {
211
211
  let datetime_class = pl.const_get::<_, Value>("Datetime").unwrap();
212
212
  datetime_class
213
- .funcall::<_, _, Value>("new", (tu.to_ascii(), tz.as_deref()))
213
+ .funcall::<_, _, Value>(
214
+ "new",
215
+ (tu.to_ascii(), tz.as_deref().map(|x| x.as_str())),
216
+ )
214
217
  .unwrap()
215
218
  }
216
219
  DataType::Duration(tu) => {
@@ -375,7 +378,10 @@ impl TryConvert for Wrap<DataType> {
375
378
  let time_unit: Value = ob.funcall("time_unit", ()).unwrap();
376
379
  let time_unit = Wrap::<TimeUnit>::try_convert(time_unit)?.0;
377
380
  let time_zone: Option<String> = ob.funcall("time_zone", ())?;
378
- DataType::Datetime(time_unit, time_zone.as_deref().map(|x| x.into()))
381
+ DataType::Datetime(
382
+ time_unit,
383
+ TimeZone::opt_try_new(time_zone.as_deref()).map_err(RbPolarsErr::from)?,
384
+ )
379
385
  }
380
386
  "Polars::Duration" => {
381
387
  let time_unit: Value = ob.funcall("time_unit", ()).unwrap();
@@ -1229,3 +1235,13 @@ impl TryConvert for RbCompatLevel {
1229
1235
  }))
1230
1236
  }
1231
1237
  }
1238
+
1239
+ impl TryConvert for Wrap<Option<TimeZone>> {
1240
+ fn try_convert(ob: Value) -> RbResult<Self> {
1241
+ let tz = Option::<Wrap<PlSmallStr>>::try_convert(ob)?;
1242
+
1243
+ let tz = tz.map(|x| x.0);
1244
+
1245
+ Ok(Wrap(TimeZone::opt_try_new(tz).map_err(RbPolarsErr::from)?))
1246
+ }
1247
+ }
@@ -75,11 +75,11 @@ impl RbExpr {
75
75
  .into()
76
76
  }
77
77
 
78
- pub fn arr_contains(&self, other: &RbExpr) -> Self {
78
+ pub fn arr_contains(&self, other: &RbExpr, nulls_equal: bool) -> Self {
79
79
  self.inner
80
80
  .clone()
81
81
  .arr()
82
- .contains(other.inner.clone())
82
+ .contains(other.inner.clone(), nulls_equal)
83
83
  .into()
84
84
  }
85
85
 
@@ -1,7 +1,7 @@
1
1
  use polars::prelude::*;
2
2
 
3
3
  use crate::conversion::Wrap;
4
- use crate::RbExpr;
4
+ use crate::{RbExpr, RbPolarsErr, RbResult};
5
5
 
6
6
  impl RbExpr {
7
7
  pub fn dt_to_string(&self, format: String) -> Self {
@@ -30,12 +30,17 @@ impl RbExpr {
30
30
  self.inner.clone().dt().with_time_unit(tu.0).into()
31
31
  }
32
32
 
33
- pub fn dt_convert_time_zone(&self, time_zone: String) -> Self {
34
- self.inner
33
+ pub fn dt_convert_time_zone(&self, time_zone: String) -> RbResult<Self> {
34
+ Ok(self
35
+ .inner
35
36
  .clone()
36
37
  .dt()
37
- .convert_time_zone(time_zone.into())
38
- .into()
38
+ .convert_time_zone(
39
+ TimeZone::opt_try_new(Some(PlSmallStr::from(time_zone)))
40
+ .map_err(RbPolarsErr::from)?
41
+ .unwrap_or(TimeZone::UTC),
42
+ )
43
+ .into())
39
44
  }
40
45
 
41
46
  pub fn dt_cast_time_unit(&self, tu: Wrap<TimeUnit>) -> Self {
@@ -47,16 +52,18 @@ impl RbExpr {
47
52
  time_zone: Option<String>,
48
53
  ambiguous: &Self,
49
54
  non_existent: Wrap<NonExistent>,
50
- ) -> Self {
51
- self.inner
55
+ ) -> RbResult<Self> {
56
+ Ok(self
57
+ .inner
52
58
  .clone()
53
59
  .dt()
54
60
  .replace_time_zone(
55
- time_zone.map(|x| x.into()),
61
+ TimeZone::opt_try_new(time_zone.map(PlSmallStr::from_string))
62
+ .map_err(RbPolarsErr::from)?,
56
63
  ambiguous.inner.clone(),
57
64
  non_existent.0,
58
65
  )
59
- .into()
66
+ .into())
60
67
  }
61
68
 
62
69
  pub fn dt_truncate(&self, every: &Self) -> Self {
@@ -1,6 +1,6 @@
1
1
  use std::ops::Neg;
2
2
 
3
- use magnus::{prelude::*, value::Opaque, IntoValue, RArray, Ruby, Value};
3
+ use magnus::{RArray, Value};
4
4
  use polars::lazy::dsl;
5
5
  use polars::prelude::*;
6
6
  use polars::series::ops::NullBehavior;
@@ -331,10 +331,15 @@ impl RbExpr {
331
331
  self.inner.clone().arg_min().into()
332
332
  }
333
333
 
334
- pub fn search_sorted(&self, element: &Self, side: Wrap<SearchSortedSide>) -> Self {
334
+ pub fn search_sorted(
335
+ &self,
336
+ element: &Self,
337
+ side: Wrap<SearchSortedSide>,
338
+ descending: bool,
339
+ ) -> Self {
335
340
  self.inner
336
341
  .clone()
337
- .search_sorted(element.inner.clone(), side.0)
342
+ .search_sorted(element.inner.clone(), side.0, descending)
338
343
  .into()
339
344
  }
340
345
 
@@ -389,16 +394,8 @@ impl RbExpr {
389
394
  strategy: String,
390
395
  limit: FillNullLimit,
391
396
  ) -> RbResult<Self> {
392
- let strat = parse_fill_null_strategy(&strategy, limit)?;
393
- Ok(self
394
- .inner
395
- .clone()
396
- .apply(
397
- move |s| s.fill_null(strat).map(Some),
398
- GetOutput::same_type(),
399
- )
400
- .with_fmt("fill_null_with_strategy")
401
- .into())
397
+ let strategy = parse_fill_null_strategy(&strategy, limit)?;
398
+ Ok(self.inner.clone().fill_null_with_strategy(strategy).into())
402
399
  }
403
400
 
404
401
  pub fn fill_nan(&self, expr: &Self) -> Self {
@@ -678,10 +675,10 @@ impl RbExpr {
678
675
  self.inner.clone().upper_bound().into()
679
676
  }
680
677
 
681
- pub fn cumulative_eval(&self, expr: &Self, min_periods: usize, parallel: bool) -> Self {
678
+ pub fn cumulative_eval(&self, expr: &Self, min_samples: usize) -> Self {
682
679
  self.inner
683
680
  .clone()
684
- .cumulative_eval(expr.inner.clone(), min_periods, parallel)
681
+ .cumulative_eval(expr.inner.clone(), min_samples)
685
682
  .into()
686
683
  }
687
684
 
@@ -807,20 +804,10 @@ impl RbExpr {
807
804
  self.inner.clone().ewm_var(options).into()
808
805
  }
809
806
 
810
- pub fn extend_constant(&self, value: Wrap<AnyValue>, n: usize) -> Self {
811
- let value = value.into_value();
812
- let value = Opaque::from(value);
807
+ pub fn extend_constant(&self, value: &Self, n: &Self) -> Self {
813
808
  self.inner
814
809
  .clone()
815
- .apply(
816
- move |s| {
817
- let value = Ruby::get().unwrap().get_inner(value);
818
- let value = Wrap::<AnyValue>::try_convert(value).unwrap().0;
819
- s.extend_constant(value, n).map(Some)
820
- },
821
- GetOutput::same_type(),
822
- )
823
- .with_fmt("extend")
810
+ .extend_constant(value.inner.clone(), n.inner.clone())
824
811
  .into()
825
812
  }
826
813
 
@@ -23,11 +23,11 @@ impl RbExpr {
23
23
  self.inner.clone().list().arg_min().into()
24
24
  }
25
25
 
26
- pub fn list_contains(&self, other: &RbExpr) -> Self {
26
+ pub fn list_contains(&self, other: &RbExpr, nulls_equal: bool) -> Self {
27
27
  self.inner
28
28
  .clone()
29
29
  .list()
30
- .contains(other.inner.clone())
30
+ .contains(other.inner.clone(), nulls_equal)
31
31
  .into()
32
32
  }
33
33
 
@@ -43,12 +43,8 @@ impl RbExpr {
43
43
  Ok(self.inner.clone().list().diff(n, null_behavior.0).into())
44
44
  }
45
45
 
46
- pub fn list_eval(&self, expr: &RbExpr, parallel: bool) -> Self {
47
- self.inner
48
- .clone()
49
- .list()
50
- .eval(expr.inner.clone(), parallel)
51
- .into()
46
+ pub fn list_eval(&self, expr: &RbExpr) -> Self {
47
+ self.inner.clone().list().eval(expr.inner.clone()).into()
52
48
  }
53
49
 
54
50
  pub fn list_get(&self, index: &RbExpr, null_on_oob: bool) -> Self {
@@ -76,12 +72,7 @@ impl RbExpr {
76
72
  }
77
73
 
78
74
  pub fn list_mean(&self) -> Self {
79
- self.inner
80
- .clone()
81
- .list()
82
- .mean()
83
- .with_fmt("list.mean")
84
- .into()
75
+ self.inner.clone().list().mean().into()
85
76
  }
86
77
 
87
78
  pub fn list_min(&self) -> Self {
@@ -116,20 +107,20 @@ impl RbExpr {
116
107
  self.inner.clone().list().tail(n.inner.clone()).into()
117
108
  }
118
109
 
119
- pub fn list_sort(&self, reverse: bool) -> Self {
110
+ pub fn list_sort(&self, descending: bool, nulls_last: bool) -> Self {
120
111
  self.inner
121
112
  .clone()
122
113
  .list()
123
- .sort(SortOptions {
124
- descending: reverse,
125
- ..Default::default()
126
- })
127
- .with_fmt("list.sort")
114
+ .sort(
115
+ SortOptions::default()
116
+ .with_order_descending(descending)
117
+ .with_nulls_last(nulls_last),
118
+ )
128
119
  .into()
129
120
  }
130
121
 
131
122
  pub fn list_sum(&self) -> Self {
132
- self.inner.clone().list().sum().with_fmt("list.sum").into()
123
+ self.inner.clone().list().sum().into()
133
124
  }
134
125
 
135
126
  pub fn list_drop_nulls(&self) -> Self {
@@ -180,7 +171,7 @@ impl RbExpr {
180
171
  &self,
181
172
  width_strat: Wrap<ListToStructWidthStrategy>,
182
173
  name_gen: Option<Value>,
183
- upper_bound: usize,
174
+ upper_bound: Option<usize>,
184
175
  ) -> RbResult<Self> {
185
176
  let name_gen = name_gen.map(|lambda| {
186
177
  let lambda = Opaque::from(lambda);
@@ -1,14 +1,21 @@
1
1
  use magnus::RArray;
2
+ use polars::prelude::Schema;
2
3
 
3
- use crate::{RbExpr, RbPolarsErr, RbResult};
4
+ use crate::{RbExpr, RbPolarsErr, RbResult, Wrap};
4
5
 
5
6
  impl RbExpr {
6
7
  pub fn meta_eq(&self, other: &RbExpr) -> bool {
7
8
  self.inner == other.inner
8
9
  }
9
10
 
10
- pub fn meta_pop(&self) -> RbResult<RArray> {
11
- let exprs = self.inner.clone().meta().pop().map_err(RbPolarsErr::from)?;
11
+ pub fn meta_pop(&self, schema: Option<Wrap<Schema>>) -> RbResult<RArray> {
12
+ let schema = schema.as_ref().map(|s| &s.0);
13
+ let exprs = self
14
+ .inner
15
+ .clone()
16
+ .meta()
17
+ .pop(schema)
18
+ .map_err(RbPolarsErr::from)?;
12
19
  Ok(RArray::from_iter(
13
20
  exprs.iter().map(|e| RbExpr::from(e.clone())),
14
21
  ))
@@ -84,17 +91,21 @@ impl RbExpr {
84
91
  self.inner.clone().meta()._into_selector().into()
85
92
  }
86
93
 
87
- fn compute_tree_format(&self, display_as_dot: bool) -> RbResult<String> {
94
+ fn compute_tree_format(
95
+ &self,
96
+ display_as_dot: bool,
97
+ schema: Option<Wrap<Schema>>,
98
+ ) -> RbResult<String> {
88
99
  let e = self
89
100
  .inner
90
101
  .clone()
91
102
  .meta()
92
- .into_tree_formatter(display_as_dot)
103
+ .into_tree_formatter(display_as_dot, schema.as_ref().map(|s| &s.0))
93
104
  .map_err(RbPolarsErr::from)?;
94
105
  Ok(format!("{e}"))
95
106
  }
96
107
 
97
- pub fn meta_tree_format(&self) -> RbResult<String> {
98
- self.compute_tree_format(false)
108
+ pub fn meta_tree_format(&self, schema: Option<Wrap<Schema>>) -> RbResult<String> {
109
+ self.compute_tree_format(false, schema)
99
110
  }
100
111
  }
@@ -35,14 +35,14 @@ impl RbExpr {
35
35
  &self,
36
36
  format: Option<String>,
37
37
  time_unit: Option<Wrap<TimeUnit>>,
38
- time_zone: Option<Wrap<TimeZone>>,
38
+ time_zone: Wrap<Option<TimeZone>>,
39
39
  strict: bool,
40
40
  exact: bool,
41
41
  cache: bool,
42
42
  ambiguous: &Self,
43
43
  ) -> Self {
44
44
  let format = format.map(|x| x.into());
45
- let time_zone = time_zone.map(|x| x.0);
45
+ let time_zone = time_zone.0;
46
46
 
47
47
  let options = StrptimeOptions {
48
48
  format,
@@ -225,7 +225,6 @@ impl RbExpr {
225
225
  .clone()
226
226
  .str()
227
227
  .to_integer(base.inner.clone(), strict)
228
- .with_fmt("str.to_integer")
229
228
  .into()
230
229
  }
231
230
 
@@ -93,7 +93,7 @@ impl RbFileLikeObject {
93
93
 
94
94
  /// Extracts a string repr from, and returns an IO error to send back to rust.
95
95
  fn rberr_to_io_err(e: Error) -> io::Error {
96
- io::Error::new(io::ErrorKind::Other, e.to_string())
96
+ io::Error::other(e.to_string())
97
97
  }
98
98
 
99
99
  impl Read for RbFileLikeObject {
@@ -310,7 +310,7 @@ pub fn last() -> RbExpr {
310
310
  dsl::last().into()
311
311
  }
312
312
 
313
- pub fn lit(value: Value, allow_object: bool) -> RbResult<RbExpr> {
313
+ pub fn lit(value: Value, allow_object: bool, is_scalar: bool) -> RbResult<RbExpr> {
314
314
  if value.is_kind_of(class::true_class()) || value.is_kind_of(class::false_class()) {
315
315
  Ok(dsl::lit(bool::try_convert(value)?).into())
316
316
  } else if let Some(v) = Integer::from_value(value) {
@@ -336,7 +336,16 @@ pub fn lit(value: Value, allow_object: bool) -> RbResult<RbExpr> {
336
336
  Ok(dsl::lit(unsafe { v.as_slice() }).into())
337
337
  }
338
338
  } else if let Ok(series) = Obj::<RbSeries>::try_convert(value) {
339
- Ok(dsl::lit(series.series.borrow().clone()).into())
339
+ let s = series.series.borrow();
340
+ if is_scalar {
341
+ let av = s
342
+ .get(0)
343
+ .map_err(|_| RbValueError::new_err("expected at least 1 value"))?;
344
+ let av = av.into_static();
345
+ Ok(dsl::lit(Scalar::new(s.dtype().clone(), av)).into())
346
+ } else {
347
+ Ok(dsl::lit(s.clone()).into())
348
+ }
340
349
  } else if value.is_nil() {
341
350
  Ok(dsl::lit(Null {}).into())
342
351
  } else if allow_object {
@@ -3,7 +3,7 @@ use polars_core::datatypes::{TimeUnit, TimeZone};
3
3
 
4
4
  use crate::conversion::Wrap;
5
5
  use crate::prelude::*;
6
- use crate::RbExpr;
6
+ use crate::{RbExpr, RbResult};
7
7
 
8
8
  pub fn int_range(start: &RbExpr, end: &RbExpr, step: i64, dtype: Wrap<DataType>) -> RbExpr {
9
9
  let start = start.inner.clone();
@@ -12,16 +12,20 @@ pub fn int_range(start: &RbExpr, end: &RbExpr, step: i64, dtype: Wrap<DataType>)
12
12
  dsl::int_range(start, end, step, dtype).into()
13
13
  }
14
14
 
15
- pub fn int_ranges(start: &RbExpr, end: &RbExpr, step: &RbExpr, dtype: Wrap<DataType>) -> RbExpr {
15
+ pub fn int_ranges(
16
+ start: &RbExpr,
17
+ end: &RbExpr,
18
+ step: &RbExpr,
19
+ dtype: Wrap<DataType>,
20
+ ) -> RbResult<RbExpr> {
16
21
  let dtype = dtype.0;
17
-
18
- let mut result = dsl::int_ranges(start.inner.clone(), end.inner.clone(), step.inner.clone());
19
-
20
- if dtype != DataType::Int64 {
21
- result = result.cast(DataType::List(Box::new(dtype)))
22
- }
23
-
24
- result.into()
22
+ Ok(dsl::int_ranges(
23
+ start.inner.clone(),
24
+ end.inner.clone(),
25
+ step.inner.clone(),
26
+ dtype,
27
+ )
28
+ .into())
25
29
  }
26
30
 
27
31
  pub fn date_range(
@@ -56,14 +60,14 @@ pub fn datetime_range(
56
60
  every: String,
57
61
  closed: Wrap<ClosedWindow>,
58
62
  time_unit: Option<Wrap<TimeUnit>>,
59
- time_zone: Option<Wrap<TimeZone>>,
63
+ time_zone: Wrap<Option<TimeZone>>,
60
64
  ) -> RbExpr {
61
65
  let start = start.inner.clone();
62
66
  let end = end.inner.clone();
63
67
  let every = Duration::parse(&every);
64
68
  let closed = closed.0;
65
69
  let time_unit = time_unit.map(|x| x.0);
66
- let time_zone = time_zone.map(|x| x.0);
70
+ let time_zone = time_zone.0;
67
71
  dsl::datetime_range(start, end, every, closed, time_unit, time_zone).into()
68
72
  }
69
73
 
@@ -73,14 +77,14 @@ pub fn datetime_ranges(
73
77
  every: String,
74
78
  closed: Wrap<ClosedWindow>,
75
79
  time_unit: Option<Wrap<TimeUnit>>,
76
- time_zone: Option<Wrap<TimeZone>>,
80
+ time_zone: Wrap<Option<TimeZone>>,
77
81
  ) -> RbExpr {
78
82
  let start = start.inner.clone();
79
83
  let end = end.inner.clone();
80
84
  let every = Duration::parse(&every);
81
85
  let closed = closed.0;
82
86
  let time_unit = time_unit.map(|x| x.0);
83
- let time_zone = time_zone.map(|x| x.0);
87
+ let time_zone = time_zone.0;
84
88
  dsl::datetime_ranges(start, end, every, closed, time_unit, time_zone).into()
85
89
  }
86
90
 
@@ -305,7 +305,7 @@ impl RbLazyFrame {
305
305
  .with_predicate_pushdown(predicate_pushdown)
306
306
  .with_simplify_expr(simplify_expr)
307
307
  .with_slice_pushdown(slice_pushdown)
308
- .with_streaming(allow_streaming)
308
+ .with_new_streaming(allow_streaming)
309
309
  ._with_eager(_eager)
310
310
  .with_projection_pushdown(projection_pushdown);
311
311
 
@@ -392,6 +392,8 @@ impl RbLazyFrame {
392
392
  statistics: statistics.0,
393
393
  row_group_size,
394
394
  data_page_size,
395
+ key_value_metadata: None,
396
+ field_overwrites: Vec::new(),
395
397
  };
396
398
 
397
399
  let cloud_options = match target.base_path() {
@@ -217,7 +217,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
217
217
  class.define_method("peak_max", method!(RbExpr::peak_max, 0))?;
218
218
  class.define_method("arg_max", method!(RbExpr::arg_max, 0))?;
219
219
  class.define_method("arg_min", method!(RbExpr::arg_min, 0))?;
220
- class.define_method("search_sorted", method!(RbExpr::search_sorted, 2))?;
220
+ class.define_method("search_sorted", method!(RbExpr::search_sorted, 3))?;
221
221
  class.define_method("gather", method!(RbExpr::gather, 1))?;
222
222
  class.define_method("get", method!(RbExpr::get, 1))?;
223
223
  class.define_method("sort_by", method!(RbExpr::sort_by, 5))?;
@@ -320,7 +320,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
320
320
  class.define_method("arr_arg_max", method!(RbExpr::arr_arg_max, 0))?;
321
321
  class.define_method("arr_get", method!(RbExpr::arr_get, 2))?;
322
322
  class.define_method("arr_join", method!(RbExpr::arr_join, 2))?;
323
- class.define_method("arr_contains", method!(RbExpr::arr_contains, 1))?;
323
+ class.define_method("arr_contains", method!(RbExpr::arr_contains, 2))?;
324
324
  class.define_method("arr_count_matches", method!(RbExpr::arr_count_matches, 1))?;
325
325
  class.define_method("binary_contains", method!(RbExpr::bin_contains, 1))?;
326
326
  class.define_method("binary_ends_with", method!(RbExpr::bin_ends_with, 1))?;
@@ -365,7 +365,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
365
365
  class.define_method("str_contains_any", method!(RbExpr::str_contains_any, 2))?;
366
366
  class.define_method("str_replace_many", method!(RbExpr::str_replace_many, 3))?;
367
367
  class.define_method("list_len", method!(RbExpr::list_len, 0))?;
368
- class.define_method("list_contains", method!(RbExpr::list_contains, 1))?;
368
+ class.define_method("list_contains", method!(RbExpr::list_contains, 2))?;
369
369
  class.define_method("list_count_matches", method!(RbExpr::list_count_matches, 1))?;
370
370
  class.define_method("dt_year", method!(RbExpr::dt_year, 0))?;
371
371
  class.define_method("dt_is_leap_year", method!(RbExpr::dt_is_leap_year, 0))?;
@@ -464,7 +464,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
464
464
  class.define_method("list_to_array", method!(RbExpr::list_to_array, 1))?;
465
465
  class.define_method("list_mean", method!(RbExpr::list_mean, 0))?;
466
466
  class.define_method("list_tail", method!(RbExpr::list_tail, 1))?;
467
- class.define_method("list_sort", method!(RbExpr::list_sort, 1))?;
467
+ class.define_method("list_sort", method!(RbExpr::list_sort, 2))?;
468
468
  class.define_method("list_reverse", method!(RbExpr::list_reverse, 0))?;
469
469
  class.define_method("list_unique", method!(RbExpr::list_unique, 1))?;
470
470
  class.define_method("list_get", method!(RbExpr::list_get, 2))?;
@@ -476,8 +476,8 @@ fn init(ruby: &Ruby) -> RbResult<()> {
476
476
  class.define_method("list_diff", method!(RbExpr::list_diff, 2))?;
477
477
  class.define_method("list_shift", method!(RbExpr::list_shift, 1))?;
478
478
  class.define_method("list_slice", method!(RbExpr::list_slice, 2))?;
479
- class.define_method("list_eval", method!(RbExpr::list_eval, 2))?;
480
- class.define_method("cumulative_eval", method!(RbExpr::cumulative_eval, 3))?;
479
+ class.define_method("list_eval", method!(RbExpr::list_eval, 1))?;
480
+ class.define_method("cumulative_eval", method!(RbExpr::cumulative_eval, 2))?;
481
481
  class.define_method("list_to_struct", method!(RbExpr::list_to_struct, 3))?;
482
482
  class.define_method("rank", method!(RbExpr::rank, 3))?;
483
483
  class.define_method("diff", method!(RbExpr::diff, 2))?;
@@ -520,7 +520,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
520
520
  class.define_method("replace_strict", method!(RbExpr::replace_strict, 4))?;
521
521
 
522
522
  // meta
523
- class.define_method("meta_pop", method!(RbExpr::meta_pop, 0))?;
523
+ class.define_method("meta_pop", method!(RbExpr::meta_pop, 1))?;
524
524
  class.define_method("meta_eq", method!(RbExpr::meta_eq, 1))?;
525
525
  class.define_method("meta_roots", method!(RbExpr::meta_root_names, 0))?;
526
526
  class.define_method("meta_output_name", method!(RbExpr::meta_output_name, 0))?;
@@ -538,7 +538,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
538
538
  class.define_method("_meta_selector_sub", method!(RbExpr::_meta_selector_sub, 1))?;
539
539
  class.define_method("_meta_selector_and", method!(RbExpr::_meta_selector_and, 1))?;
540
540
  class.define_method("_meta_as_selector", method!(RbExpr::_meta_as_selector, 0))?;
541
- class.define_method("meta_tree_format", method!(RbExpr::meta_tree_format, 0))?;
541
+ class.define_method("meta_tree_format", method!(RbExpr::meta_tree_format, 1))?;
542
542
 
543
543
  // name
544
544
  class.define_method("name_keep", method!(RbExpr::name_keep, 0))?;
@@ -559,7 +559,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
559
559
  class.define_singleton_method("cols", function!(functions::lazy::cols, 1))?;
560
560
  class.define_singleton_method("fold", function!(functions::lazy::fold, 5))?;
561
561
  class.define_singleton_method("cum_fold", function!(functions::lazy::cum_fold, 4))?;
562
- class.define_singleton_method("lit", function!(functions::lazy::lit, 2))?;
562
+ class.define_singleton_method("lit", function!(functions::lazy::lit, 3))?;
563
563
  class.define_singleton_method("int_range", function!(functions::range::int_range, 4))?;
564
564
  class.define_singleton_method("int_ranges", function!(functions::range::int_ranges, 4))?;
565
565
  class.define_singleton_method("repeat", function!(functions::lazy::repeat, 3))?;
@@ -481,6 +481,8 @@ module Polars
481
481
  #
482
482
  # @param item [Object]
483
483
  # Item that will be checked for membership
484
+ # @param nulls_equal [Boolean]
485
+ # If true, treat null as a distinct value. Null values will not propagate.
484
486
  #
485
487
  # @return [Expr]
486
488
  #
@@ -501,9 +503,9 @@ module Polars
501
503
  # # │ ["x", "y"] ┆ false │
502
504
  # # │ ["a", "c"] ┆ true │
503
505
  # # └───────────────┴──────────┘
504
- def contains(item)
506
+ def contains(item, nulls_equal: true)
505
507
  item = Utils.parse_into_expression(item, str_as_lit: true)
506
- Utils.wrap_expr(_rbexpr.arr_contains(item))
508
+ Utils.wrap_expr(_rbexpr.arr_contains(item, nulls_equal))
507
509
  end
508
510
 
509
511
  # Count how often the value produced by `element` occurs.