polars-df 0.11.0 → 0.12.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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/Cargo.lock +360 -361
  4. data/ext/polars/Cargo.toml +10 -7
  5. data/ext/polars/src/batched_csv.rs +1 -1
  6. data/ext/polars/src/conversion/any_value.rs +261 -0
  7. data/ext/polars/src/conversion/chunked_array.rs +4 -4
  8. data/ext/polars/src/conversion/mod.rs +51 -10
  9. data/ext/polars/src/dataframe/construction.rs +6 -8
  10. data/ext/polars/src/dataframe/general.rs +19 -29
  11. data/ext/polars/src/dataframe/io.rs +43 -33
  12. data/ext/polars/src/error.rs +26 -4
  13. data/ext/polars/src/expr/categorical.rs +0 -10
  14. data/ext/polars/src/expr/datetime.rs +4 -12
  15. data/ext/polars/src/expr/general.rs +123 -110
  16. data/ext/polars/src/expr/mod.rs +2 -2
  17. data/ext/polars/src/expr/rolling.rs +17 -9
  18. data/ext/polars/src/expr/string.rs +2 -6
  19. data/ext/polars/src/functions/eager.rs +10 -10
  20. data/ext/polars/src/functions/lazy.rs +21 -21
  21. data/ext/polars/src/functions/range.rs +6 -12
  22. data/ext/polars/src/interop/numo/to_numo_series.rs +2 -1
  23. data/ext/polars/src/lazyframe/mod.rs +81 -98
  24. data/ext/polars/src/lib.rs +55 -45
  25. data/ext/polars/src/map/dataframe.rs +2 -2
  26. data/ext/polars/src/rb_modules.rs +25 -1
  27. data/ext/polars/src/series/aggregation.rs +4 -2
  28. data/ext/polars/src/series/arithmetic.rs +21 -11
  29. data/ext/polars/src/series/construction.rs +56 -38
  30. data/ext/polars/src/series/export.rs +1 -1
  31. data/ext/polars/src/series/mod.rs +31 -10
  32. data/ext/polars/src/sql.rs +3 -1
  33. data/lib/polars/array_expr.rb +4 -4
  34. data/lib/polars/batched_csv_reader.rb +2 -2
  35. data/lib/polars/cat_expr.rb +0 -36
  36. data/lib/polars/cat_name_space.rb +0 -37
  37. data/lib/polars/data_frame.rb +93 -101
  38. data/lib/polars/data_types.rb +1 -1
  39. data/lib/polars/date_time_expr.rb +525 -573
  40. data/lib/polars/date_time_name_space.rb +263 -464
  41. data/lib/polars/dynamic_group_by.rb +3 -3
  42. data/lib/polars/exceptions.rb +3 -0
  43. data/lib/polars/expr.rb +367 -330
  44. data/lib/polars/expr_dispatch.rb +1 -1
  45. data/lib/polars/functions/aggregation/horizontal.rb +8 -8
  46. data/lib/polars/functions/as_datatype.rb +63 -40
  47. data/lib/polars/functions/lazy.rb +63 -14
  48. data/lib/polars/functions/lit.rb +1 -1
  49. data/lib/polars/functions/range/date_range.rb +18 -77
  50. data/lib/polars/functions/range/datetime_range.rb +4 -4
  51. data/lib/polars/functions/range/int_range.rb +2 -2
  52. data/lib/polars/functions/range/time_range.rb +4 -4
  53. data/lib/polars/functions/repeat.rb +1 -1
  54. data/lib/polars/functions/whenthen.rb +1 -1
  55. data/lib/polars/io/csv.rb +8 -8
  56. data/lib/polars/io/ipc.rb +3 -3
  57. data/lib/polars/io/json.rb +13 -2
  58. data/lib/polars/io/ndjson.rb +15 -4
  59. data/lib/polars/io/parquet.rb +5 -4
  60. data/lib/polars/lazy_frame.rb +120 -106
  61. data/lib/polars/lazy_group_by.rb +1 -1
  62. data/lib/polars/list_expr.rb +11 -11
  63. data/lib/polars/list_name_space.rb +5 -1
  64. data/lib/polars/rolling_group_by.rb +5 -7
  65. data/lib/polars/series.rb +105 -189
  66. data/lib/polars/string_expr.rb +42 -67
  67. data/lib/polars/string_name_space.rb +5 -4
  68. data/lib/polars/testing.rb +2 -2
  69. data/lib/polars/utils/constants.rb +9 -0
  70. data/lib/polars/utils/convert.rb +97 -0
  71. data/lib/polars/utils/parse.rb +89 -0
  72. data/lib/polars/utils/various.rb +76 -0
  73. data/lib/polars/utils/wrap.rb +19 -0
  74. data/lib/polars/utils.rb +4 -330
  75. data/lib/polars/version.rb +1 -1
  76. data/lib/polars/whenthen.rb +6 -6
  77. data/lib/polars.rb +11 -0
  78. metadata +9 -4
  79. data/ext/polars/src/conversion/anyvalue.rs +0 -186
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "polars"
3
- version = "0.11.0"
3
+ version = "0.12.0"
4
4
  license = "MIT"
5
5
  authors = ["Andrew Kane <andrew@ankane.org>"]
6
6
  edition = "2021"
@@ -14,15 +14,15 @@ crate-type = ["cdylib"]
14
14
  ahash = "0.8"
15
15
  chrono = "0.4"
16
16
  either = "1.8"
17
- magnus = "0.6"
18
- polars-core = "=0.40.0"
19
- polars-parquet = "=0.40.0"
20
- polars-utils = "=0.40.0"
17
+ magnus = "0.7"
18
+ polars-core = "=0.41.3"
19
+ polars-parquet = "=0.41.3"
20
+ polars-utils = "=0.41.3"
21
21
  serde_json = "1"
22
22
  smartstring = "1"
23
23
 
24
24
  [dependencies.polars]
25
- version = "=0.40.0"
25
+ version = "=0.41.3"
26
26
  features = [
27
27
  "abs",
28
28
  "approx_unique",
@@ -41,7 +41,6 @@ features = [
41
41
  "cumulative_eval",
42
42
  "cutqcut",
43
43
  "dataframe_arithmetic",
44
- "date_offset",
45
44
  "diagonal_concat",
46
45
  "diff",
47
46
  "dot_product",
@@ -56,6 +55,7 @@ features = [
56
55
  "interpolate",
57
56
  "ipc",
58
57
  "ipc_streaming",
58
+ "is_between",
59
59
  "is_first_distinct",
60
60
  "is_in",
61
61
  "is_last_distinct",
@@ -74,6 +74,9 @@ features = [
74
74
  "meta",
75
75
  "mode",
76
76
  "moment",
77
+ "month_start",
78
+ "month_end",
79
+ "offset_by",
77
80
  "object",
78
81
  "parquet",
79
82
  "partition_by",
@@ -95,7 +95,7 @@ impl RbBatchedCsv {
95
95
  .with_projection(projection.map(Arc::new))
96
96
  .with_rechunk(rechunk)
97
97
  .with_chunk_size(chunk_size)
98
- .with_columns(columns.map(Arc::new))
98
+ .with_columns(columns.map(Arc::from))
99
99
  .with_n_threads(n_threads)
100
100
  .with_dtype_overwrite(overwrite_dtype_slice.map(Arc::new))
101
101
  .with_low_memory(low_memory)
@@ -0,0 +1,261 @@
1
+ use magnus::encoding::{EncodingCapable, Index};
2
+ use magnus::{
3
+ class, prelude::*, r_hash::ForEach, IntoValue, RArray, RHash, RString, Ruby, TryConvert, Value,
4
+ };
5
+ use polars::prelude::*;
6
+ use polars_core::utils::any_values_to_supertype_and_n_dtypes;
7
+
8
+ use super::{struct_dict, ObjectValue, Wrap};
9
+
10
+ use crate::error::RbOverflowError;
11
+ use crate::rb_modules::utils;
12
+ use crate::{RbPolarsErr, RbResult, RbSeries};
13
+
14
+ impl IntoValue for Wrap<AnyValue<'_>> {
15
+ fn into_value_with(self, ruby: &Ruby) -> Value {
16
+ any_value_into_rb_object(self.0, ruby)
17
+ }
18
+ }
19
+
20
+ impl<'s> TryConvert for Wrap<AnyValue<'s>> {
21
+ fn try_convert(ob: Value) -> RbResult<Self> {
22
+ rb_object_to_any_value(ob, true).map(Wrap)
23
+ }
24
+ }
25
+
26
+ pub(crate) fn any_value_into_rb_object(av: AnyValue, ruby: &Ruby) -> Value {
27
+ match av {
28
+ AnyValue::UInt8(v) => ruby.into_value(v),
29
+ AnyValue::UInt16(v) => ruby.into_value(v),
30
+ AnyValue::UInt32(v) => ruby.into_value(v),
31
+ AnyValue::UInt64(v) => ruby.into_value(v),
32
+ AnyValue::Int8(v) => ruby.into_value(v),
33
+ AnyValue::Int16(v) => ruby.into_value(v),
34
+ AnyValue::Int32(v) => ruby.into_value(v),
35
+ AnyValue::Int64(v) => ruby.into_value(v),
36
+ AnyValue::Float32(v) => ruby.into_value(v),
37
+ AnyValue::Float64(v) => ruby.into_value(v),
38
+ AnyValue::Null => ruby.qnil().as_value(),
39
+ AnyValue::Boolean(v) => ruby.into_value(v),
40
+ AnyValue::String(v) => ruby.into_value(v),
41
+ AnyValue::StringOwned(v) => ruby.into_value(v.as_str()),
42
+ AnyValue::Categorical(idx, rev, arr) | AnyValue::Enum(idx, rev, arr) => {
43
+ let s = if arr.is_null() {
44
+ rev.get(idx)
45
+ } else {
46
+ unsafe { arr.deref_unchecked().value(idx as usize) }
47
+ };
48
+ s.into_value()
49
+ }
50
+ AnyValue::Date(v) => utils().funcall("_to_ruby_date", (v,)).unwrap(),
51
+ AnyValue::Datetime(v, time_unit, time_zone) => {
52
+ let time_unit = time_unit.to_ascii();
53
+ utils()
54
+ .funcall("_to_ruby_datetime", (v, time_unit, time_zone.clone()))
55
+ .unwrap()
56
+ }
57
+ AnyValue::Duration(v, time_unit) => {
58
+ let time_unit = time_unit.to_ascii();
59
+ utils()
60
+ .funcall("_to_ruby_duration", (v, time_unit))
61
+ .unwrap()
62
+ }
63
+ AnyValue::Time(v) => utils().funcall("_to_ruby_time", (v,)).unwrap(),
64
+ AnyValue::Array(v, _) | AnyValue::List(v) => RbSeries::new(v).to_a().into_value(),
65
+ ref av @ AnyValue::Struct(_, _, flds) => struct_dict(av._iter_struct_av(), flds),
66
+ AnyValue::StructOwned(payload) => struct_dict(payload.0.into_iter(), &payload.1),
67
+ AnyValue::Object(v) => {
68
+ let object = v.as_any().downcast_ref::<ObjectValue>().unwrap();
69
+ object.to_object()
70
+ }
71
+ AnyValue::ObjectOwned(v) => {
72
+ let object = v.0.as_any().downcast_ref::<ObjectValue>().unwrap();
73
+ object.to_object()
74
+ }
75
+ AnyValue::Binary(v) => RString::from_slice(v).into_value(),
76
+ AnyValue::BinaryOwned(v) => RString::from_slice(&v).into_value(),
77
+ AnyValue::Decimal(v, scale) => utils()
78
+ .funcall("_to_ruby_decimal", (v.to_string(), -(scale as i32)))
79
+ .unwrap(),
80
+ }
81
+ }
82
+
83
+ pub(crate) fn rb_object_to_any_value<'s>(ob: Value, strict: bool) -> RbResult<AnyValue<'s>> {
84
+ // Conversion functions.
85
+ fn get_null(_ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
86
+ Ok(AnyValue::Null)
87
+ }
88
+
89
+ fn get_bool(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
90
+ let b = bool::try_convert(ob)?;
91
+ Ok(AnyValue::Boolean(b))
92
+ }
93
+
94
+ fn get_int(ob: Value, strict: bool) -> RbResult<AnyValue<'static>> {
95
+ if let Ok(v) = i64::try_convert(ob) {
96
+ Ok(AnyValue::Int64(v))
97
+ } else if let Ok(v) = u64::try_convert(ob) {
98
+ Ok(AnyValue::UInt64(v))
99
+ } else if !strict {
100
+ let f = f64::try_convert(ob)?;
101
+ Ok(AnyValue::Float64(f))
102
+ } else {
103
+ Err(RbOverflowError::new_err(format!(
104
+ "int value too large for Polars integer types: {ob}"
105
+ )))
106
+ }
107
+ }
108
+
109
+ fn get_float(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
110
+ Ok(AnyValue::Float64(f64::try_convert(ob)?))
111
+ }
112
+
113
+ fn get_str(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
114
+ let v = RString::from_value(ob).unwrap();
115
+ if v.enc_get() == Index::utf8() {
116
+ Ok(AnyValue::StringOwned(v.to_string()?.into()))
117
+ } else {
118
+ Ok(AnyValue::BinaryOwned(unsafe { v.as_slice() }.to_vec()))
119
+ }
120
+ }
121
+
122
+ fn get_list(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
123
+ let v = RArray::from_value(ob).unwrap();
124
+ if v.is_empty() {
125
+ Ok(AnyValue::List(Series::new_empty("", &DataType::Null)))
126
+ } else {
127
+ let list = v;
128
+
129
+ let mut avs = Vec::with_capacity(25);
130
+ let mut iter = list.into_iter();
131
+
132
+ for item in (&mut iter).take(25) {
133
+ avs.push(Wrap::<AnyValue>::try_convert(item)?.0)
134
+ }
135
+
136
+ let (dtype, _n_types) =
137
+ any_values_to_supertype_and_n_dtypes(&avs).map_err(RbPolarsErr::from)?;
138
+
139
+ // push the rest
140
+ avs.reserve(list.len());
141
+ for item in iter {
142
+ avs.push(Wrap::<AnyValue>::try_convert(item)?.0)
143
+ }
144
+
145
+ let s = Series::from_any_values_and_dtype("", &avs, &dtype, true)
146
+ .map_err(RbPolarsErr::from)?;
147
+ Ok(AnyValue::List(s))
148
+ }
149
+ }
150
+
151
+ fn get_list_from_series(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
152
+ let s = super::get_series(ob)?;
153
+ Ok(AnyValue::List(s))
154
+ }
155
+
156
+ fn get_struct(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
157
+ let dict = RHash::from_value(ob).unwrap();
158
+ let len = dict.len();
159
+ let mut keys = Vec::with_capacity(len);
160
+ let mut vals = Vec::with_capacity(len);
161
+ dict.foreach(|k: Value, v: Value| {
162
+ let key = String::try_convert(k)?;
163
+ let val = Wrap::<AnyValue>::try_convert(v)?.0;
164
+ let dtype = DataType::from(&val);
165
+ keys.push(Field::new(&key, dtype));
166
+ vals.push(val);
167
+ Ok(ForEach::Continue)
168
+ })?;
169
+ Ok(AnyValue::StructOwned(Box::new((vals, keys))))
170
+ }
171
+
172
+ fn get_date(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
173
+ // convert to DateTime for UTC
174
+ let v = ob
175
+ .funcall::<_, _, Value>("to_datetime", ())?
176
+ .funcall::<_, _, Value>("to_time", ())?
177
+ .funcall::<_, _, i64>("to_i", ())?;
178
+ Ok(AnyValue::Date((v / 86400) as i32))
179
+ }
180
+
181
+ fn get_time(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
182
+ let sec = ob.funcall::<_, _, i64>("to_i", ())?;
183
+ let nsec = ob.funcall::<_, _, i64>("nsec", ())?;
184
+ let v = sec * 1_000_000_000 + nsec;
185
+ // TODO support time zone when possible
186
+ // https://github.com/pola-rs/polars/issues/9103
187
+ Ok(AnyValue::Datetime(v, TimeUnit::Nanoseconds, &None))
188
+ }
189
+
190
+ fn get_datetime(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
191
+ let sec: i64 = ob.funcall("to_i", ())?;
192
+ let nsec: i64 = ob.funcall("nsec", ())?;
193
+ Ok(AnyValue::Datetime(
194
+ sec * 1_000_000_000 + nsec,
195
+ TimeUnit::Nanoseconds,
196
+ &None,
197
+ ))
198
+ }
199
+
200
+ fn get_decimal(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
201
+ fn abs_decimal_from_digits(digits: String, exp: i32) -> Option<(i128, usize)> {
202
+ let exp = exp - (digits.len() as i32);
203
+ match digits.parse::<i128>() {
204
+ Ok(mut v) => {
205
+ let scale = if exp > 0 {
206
+ v = 10_i128
207
+ .checked_pow(exp as u32)
208
+ .and_then(|factor| v.checked_mul(factor))?;
209
+ 0
210
+ } else {
211
+ (-exp) as usize
212
+ };
213
+ Some((v, scale))
214
+ }
215
+ Err(_) => None,
216
+ }
217
+ }
218
+
219
+ let (sign, digits, _, exp): (i8, String, i32, i32) = ob.funcall("split", ()).unwrap();
220
+ let (mut v, scale) = abs_decimal_from_digits(digits, exp).ok_or_else(|| {
221
+ RbPolarsErr::other("BigDecimal is too large to fit in Decimal128".into())
222
+ })?;
223
+ if sign < 0 {
224
+ // TODO better error
225
+ v = v.checked_neg().unwrap();
226
+ }
227
+ Ok(AnyValue::Decimal(v, scale))
228
+ }
229
+
230
+ if ob.is_nil() {
231
+ get_null(ob, strict)
232
+ } else if ob.is_kind_of(class::true_class()) || ob.is_kind_of(class::false_class()) {
233
+ get_bool(ob, strict)
234
+ } else if ob.is_kind_of(class::integer()) {
235
+ get_int(ob, strict)
236
+ } else if ob.is_kind_of(class::float()) {
237
+ get_float(ob, strict)
238
+ } else if ob.is_kind_of(class::string()) {
239
+ get_str(ob, strict)
240
+ } else if ob.is_kind_of(class::array()) {
241
+ get_list(ob, strict)
242
+ } else if ob.is_kind_of(class::hash()) {
243
+ get_struct(ob, strict)
244
+ } else if ob.respond_to("_s", true)? {
245
+ get_list_from_series(ob, strict)
246
+ // call is_a? for ActiveSupport::TimeWithZone
247
+ } else if ob.funcall::<_, _, bool>("is_a?", (class::time(),))? {
248
+ get_time(ob, strict)
249
+ } else if ob.is_kind_of(crate::rb_modules::datetime()) {
250
+ get_datetime(ob, strict)
251
+ } else if ob.is_kind_of(crate::rb_modules::date()) {
252
+ get_date(ob, strict)
253
+ } else if ob.is_kind_of(crate::rb_modules::bigdecimal()) {
254
+ get_decimal(ob, strict)
255
+ } else {
256
+ Err(RbPolarsErr::other(format!(
257
+ "object type not supported {:?}",
258
+ ob
259
+ )))
260
+ }
261
+ }
@@ -11,8 +11,8 @@ impl TryConvert for Wrap<StringChunked> {
11
11
  let (seq, len) = get_rbseq(obj)?;
12
12
  let mut builder = StringChunkedBuilder::new("", len);
13
13
 
14
- for res in seq.each() {
15
- let item = res?;
14
+ for res in seq.into_iter() {
15
+ let item = res;
16
16
  match String::try_convert(item) {
17
17
  Ok(val) => builder.append_value(&val),
18
18
  Err(_) => builder.append_null(),
@@ -27,8 +27,8 @@ impl TryConvert for Wrap<BinaryChunked> {
27
27
  let (seq, len) = get_rbseq(obj)?;
28
28
  let mut builder = BinaryChunkedBuilder::new("", len);
29
29
 
30
- for res in seq.each() {
31
- let item = res?;
30
+ for res in seq.into_iter() {
31
+ let item = res;
32
32
  match RString::try_convert(item) {
33
33
  Ok(val) => builder.append_value(unsafe { val.as_slice() }),
34
34
  Err(_) => builder.append_null(),
@@ -1,4 +1,4 @@
1
- pub(crate) mod anyvalue;
1
+ pub(crate) mod any_value;
2
2
  mod chunked_array;
3
3
 
4
4
  use std::fmt::{Debug, Display, Formatter};
@@ -7,7 +7,7 @@ use std::num::NonZeroUsize;
7
7
 
8
8
  use magnus::{
9
9
  class, exception, prelude::*, r_hash::ForEach, value::Opaque, IntoValue, Module, RArray, RHash,
10
- Ruby, TryConvert, Value,
10
+ Ruby, Symbol, TryConvert, Value,
11
11
  };
12
12
  use polars::chunked_array::object::PolarsObjectSafe;
13
13
  use polars::chunked_array::ops::{FillNullLimit, FillNullStrategy};
@@ -75,7 +75,7 @@ pub(crate) fn get_df(obj: Value) -> RbResult<DataFrame> {
75
75
 
76
76
  pub(crate) fn get_lf(obj: Value) -> RbResult<LazyFrame> {
77
77
  let rbdf = obj.funcall::<_, _, &RbLazyFrame>("_ldf", ())?;
78
- Ok(rbdf.ldf.clone())
78
+ Ok(rbdf.ldf.borrow().clone())
79
79
  }
80
80
 
81
81
  pub(crate) fn get_series(obj: Value) -> RbResult<Series> {
@@ -379,8 +379,8 @@ impl TryConvert for Wrap<DataType> {
379
379
  "Polars::Struct" => {
380
380
  let arr: RArray = ob.funcall("fields", ())?;
381
381
  let mut fields = Vec::with_capacity(arr.len());
382
- for v in arr.each() {
383
- fields.push(Wrap::<Field>::try_convert(v?)?.0);
382
+ for v in arr.into_iter() {
383
+ fields.push(Wrap::<Field>::try_convert(v)?.0);
384
384
  }
385
385
  DataType::Struct(fields)
386
386
  }
@@ -427,11 +427,36 @@ impl TryConvert for Wrap<DataType> {
427
427
  }
428
428
  }
429
429
 
430
+ impl TryConvert for Wrap<StatisticsOptions> {
431
+ fn try_convert(ob: Value) -> RbResult<Self> {
432
+ let mut statistics = StatisticsOptions::empty();
433
+
434
+ let dict = RHash::try_convert(ob)?;
435
+ dict.foreach(|key: Symbol, val: bool| {
436
+ match key.name()?.as_ref() {
437
+ "min" => statistics.min_value = val,
438
+ "max" => statistics.max_value = val,
439
+ "distinct_count" => statistics.distinct_count = val,
440
+ "null_count" => statistics.null_count = val,
441
+ _ => {
442
+ return Err(RbTypeError::new_err(format!(
443
+ "'{key}' is not a valid statistic option",
444
+ )))
445
+ }
446
+ }
447
+ Ok(ForEach::Continue)
448
+ })
449
+ .unwrap();
450
+
451
+ Ok(Wrap(statistics))
452
+ }
453
+ }
454
+
430
455
  impl<'s> TryConvert for Wrap<Row<'s>> {
431
456
  fn try_convert(ob: Value) -> RbResult<Self> {
432
457
  let mut vals: Vec<Wrap<AnyValue<'s>>> = Vec::new();
433
- for item in RArray::try_convert(ob)?.each() {
434
- vals.push(Wrap::<AnyValue<'s>>::try_convert(item?)?);
458
+ for item in RArray::try_convert(ob)?.into_iter() {
459
+ vals.push(Wrap::<AnyValue<'s>>::try_convert(item)?);
435
460
  }
436
461
  let vals: Vec<AnyValue> = unsafe { std::mem::transmute(vals) };
437
462
  Ok(Wrap(Row(vals)))
@@ -686,14 +711,13 @@ impl TryConvert for Wrap<JoinType> {
686
711
  let parsed = match String::try_convert(ob)?.as_str() {
687
712
  "inner" => JoinType::Inner,
688
713
  "left" => JoinType::Left,
689
- "outer" => JoinType::Outer,
690
- "outer_coalesce" => JoinType::Outer,
714
+ "full" => JoinType::Full,
691
715
  "semi" => JoinType::Semi,
692
716
  "anti" => JoinType::Anti,
693
717
  "cross" => JoinType::Cross,
694
718
  v => {
695
719
  return Err(RbValueError::new_err(format!(
696
- "how must be one of {{'inner', 'left', 'outer', 'semi', 'anti', 'cross'}}, got {}",
720
+ "how must be one of {{'inner', 'left', 'full', 'semi', 'anti', 'cross'}}, got {}",
697
721
  v
698
722
  )))
699
723
  }
@@ -903,6 +927,23 @@ impl TryConvert for Wrap<SearchSortedSide> {
903
927
  }
904
928
  }
905
929
 
930
+ impl TryConvert for Wrap<ClosedInterval> {
931
+ fn try_convert(ob: Value) -> RbResult<Self> {
932
+ let parsed = match String::try_convert(ob)?.as_str() {
933
+ "both" => ClosedInterval::Both,
934
+ "left" => ClosedInterval::Left,
935
+ "right" => ClosedInterval::Right,
936
+ "none" => ClosedInterval::None,
937
+ v => {
938
+ return Err(RbValueError::new_err(format!(
939
+ "`closed` must be one of {{'both', 'left', 'right', 'none'}}, got {v}",
940
+ )))
941
+ }
942
+ };
943
+ Ok(Wrap(parsed))
944
+ }
945
+ }
946
+
906
947
  impl TryConvert for Wrap<WindowMapping> {
907
948
  fn try_convert(ob: Value) -> RbResult<Self> {
908
949
  let parsed = match String::try_convert(ob)?.as_str() {
@@ -13,11 +13,11 @@ impl RbDataFrame {
13
13
  schema: Option<Wrap<Schema>>,
14
14
  ) -> RbResult<Self> {
15
15
  let mut data = Vec::with_capacity(rb_rows.len());
16
- for v in rb_rows.each() {
17
- let rb_row = RArray::try_convert(v?)?;
16
+ for v in rb_rows.into_iter() {
17
+ let rb_row = RArray::try_convert(v)?;
18
18
  let mut row = Vec::with_capacity(rb_row.len());
19
- for val in rb_row.each() {
20
- row.push(Wrap::<AnyValue>::try_convert(val?)?.0);
19
+ for val in rb_row.into_iter() {
20
+ row.push(Wrap::<AnyValue>::try_convert(val)?.0);
21
21
  }
22
22
  data.push(Row(row));
23
23
  }
@@ -130,8 +130,7 @@ where
130
130
  fn dicts_to_rows<'a>(data: &Value, names: &'a [String], _strict: bool) -> RbResult<Vec<Row<'a>>> {
131
131
  let (data, len) = get_rbseq(*data)?;
132
132
  let mut rows = Vec::with_capacity(len);
133
- for d in data.each() {
134
- let d = d?;
133
+ for d in data.into_iter() {
135
134
  let d = RHash::try_convert(d)?;
136
135
 
137
136
  let mut row = Vec::with_capacity(names.len());
@@ -170,8 +169,7 @@ fn infer_schema_names_from_data(
170
169
  .unwrap_or(data_len);
171
170
 
172
171
  let mut names = PlIndexSet::new();
173
- for d in data.each().take(infer_schema_length) {
174
- let d = d?;
172
+ for d in data.into_iter().take(infer_schema_length) {
175
173
  let d = RHash::try_convert(d)?;
176
174
  d.foreach(|name: Value, _value: Value| {
177
175
  if let Some(v) = Symbol::from_value(name) {
@@ -15,8 +15,8 @@ use crate::{RbDataFrame, RbExpr, RbLazyFrame, RbPolarsErr, RbResult, RbSeries};
15
15
  impl RbDataFrame {
16
16
  pub fn init(columns: RArray) -> RbResult<Self> {
17
17
  let mut cols = Vec::new();
18
- for i in columns.each() {
19
- cols.push(<&RbSeries>::try_convert(i?)?.series.borrow().clone());
18
+ for i in columns.into_iter() {
19
+ cols.push(<&RbSeries>::try_convert(i)?.series.borrow().clone());
20
20
  }
21
21
  let df = DataFrame::new(cols).map_err(RbPolarsErr::from)?;
22
22
  Ok(RbDataFrame::new(df))
@@ -341,45 +341,42 @@ impl RbDataFrame {
341
341
  RbDataFrame::new(self.df.borrow().clone())
342
342
  }
343
343
 
344
- pub fn melt(
344
+ pub fn unpivot(
345
345
  &self,
346
- id_vars: Vec<String>,
347
- value_vars: Vec<String>,
346
+ on: Vec<String>,
347
+ index: Vec<String>,
348
348
  value_name: Option<String>,
349
349
  variable_name: Option<String>,
350
350
  ) -> RbResult<Self> {
351
- let args = MeltArgs {
352
- id_vars: strings_to_smartstrings(id_vars),
353
- value_vars: strings_to_smartstrings(value_vars),
351
+ let args = UnpivotArgs {
352
+ on: strings_to_smartstrings(on),
353
+ index: strings_to_smartstrings(index),
354
354
  value_name: value_name.map(|s| s.into()),
355
355
  variable_name: variable_name.map(|s| s.into()),
356
356
  streamable: false,
357
357
  };
358
358
 
359
- let df = self.df.borrow().melt2(args).map_err(RbPolarsErr::from)?;
359
+ let df = self.df.borrow().unpivot2(args).map_err(RbPolarsErr::from)?;
360
360
  Ok(RbDataFrame::new(df))
361
361
  }
362
362
 
363
363
  #[allow(clippy::too_many_arguments)]
364
364
  pub fn pivot_expr(
365
365
  &self,
366
- index: Vec<String>,
367
- columns: Vec<String>,
366
+ on: Vec<String>,
367
+ index: Option<Vec<String>>,
368
368
  values: Option<Vec<String>>,
369
369
  maintain_order: bool,
370
370
  sort_columns: bool,
371
371
  aggregate_expr: Option<&RbExpr>,
372
372
  separator: Option<String>,
373
373
  ) -> RbResult<Self> {
374
- let fun = match maintain_order {
375
- true => pivot_stable,
376
- false => pivot,
377
- };
374
+ let fun = if maintain_order { pivot_stable } else { pivot };
378
375
  let agg_expr = aggregate_expr.map(|aggregate_expr| aggregate_expr.inner.clone());
379
376
  let df = fun(
380
377
  &self.df.borrow(),
378
+ on,
381
379
  index,
382
- columns,
383
380
  values,
384
381
  sort_columns,
385
382
  agg_expr,
@@ -569,23 +566,16 @@ impl RbDataFrame {
569
566
  by: Vec<String>,
570
567
  index_column: String,
571
568
  every: String,
572
- offset: String,
573
569
  stable: bool,
574
570
  ) -> RbResult<Self> {
575
571
  let out = if stable {
576
- self.df.borrow().upsample_stable(
577
- by,
578
- &index_column,
579
- Duration::parse(&every),
580
- Duration::parse(&offset),
581
- )
572
+ self.df
573
+ .borrow()
574
+ .upsample_stable(by, &index_column, Duration::parse(&every))
582
575
  } else {
583
- self.df.borrow().upsample(
584
- by,
585
- &index_column,
586
- Duration::parse(&every),
587
- Duration::parse(&offset),
588
- )
576
+ self.df
577
+ .borrow()
578
+ .upsample(by, &index_column, Duration::parse(&every))
589
579
  };
590
580
  let out = out.map_err(RbPolarsErr::from)?;
591
581
  Ok(out.into())