polars-df 0.25.1 → 0.26.1

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -1
  3. data/Cargo.lock +278 -106
  4. data/Cargo.toml +0 -3
  5. data/LICENSE.txt +1 -1
  6. data/README.md +7 -3
  7. data/ext/polars/Cargo.toml +18 -18
  8. data/ext/polars/src/catalog/unity.rs +15 -20
  9. data/ext/polars/src/conversion/any_value.rs +25 -24
  10. data/ext/polars/src/conversion/chunked_array.rs +58 -56
  11. data/ext/polars/src/conversion/datetime.rs +58 -7
  12. data/ext/polars/src/conversion/mod.rs +205 -142
  13. data/ext/polars/src/dataframe/export.rs +15 -12
  14. data/ext/polars/src/dataframe/general.rs +5 -4
  15. data/ext/polars/src/dataframe/map.rs +6 -4
  16. data/ext/polars/src/error.rs +1 -1
  17. data/ext/polars/src/expr/array.rs +0 -24
  18. data/ext/polars/src/expr/datatype.rs +3 -2
  19. data/ext/polars/src/expr/datetime.rs +4 -4
  20. data/ext/polars/src/expr/general.rs +27 -15
  21. data/ext/polars/src/expr/list.rs +0 -26
  22. data/ext/polars/src/functions/business.rs +2 -2
  23. data/ext/polars/src/functions/io.rs +4 -3
  24. data/ext/polars/src/functions/lazy.rs +58 -46
  25. data/ext/polars/src/functions/meta.rs +6 -5
  26. data/ext/polars/src/functions/mod.rs +0 -1
  27. data/ext/polars/src/functions/utils.rs +4 -2
  28. data/ext/polars/src/interop/arrow/mod.rs +4 -2
  29. data/ext/polars/src/interop/arrow/to_rb.rs +17 -12
  30. data/ext/polars/src/interop/numo/to_numo_series.rs +26 -25
  31. data/ext/polars/src/io/scan_options.rs +6 -3
  32. data/ext/polars/src/io/sink_options.rs +2 -0
  33. data/ext/polars/src/lazyframe/general.rs +32 -16
  34. data/ext/polars/src/lazyframe/optflags.rs +2 -1
  35. data/ext/polars/src/lib.rs +21 -37
  36. data/ext/polars/src/map/lazy.rs +5 -2
  37. data/ext/polars/src/map/series.rs +19 -18
  38. data/ext/polars/src/on_startup.rs +16 -7
  39. data/ext/polars/src/ruby/capsule.rs +27 -0
  40. data/ext/polars/src/ruby/mod.rs +1 -0
  41. data/ext/polars/src/ruby/numo.rs +3 -4
  42. data/ext/polars/src/ruby/rb_modules.rs +2 -4
  43. data/ext/polars/src/ruby/ruby_udf.rs +7 -9
  44. data/ext/polars/src/ruby/utils.rs +12 -1
  45. data/ext/polars/src/series/aggregation.rs +13 -1
  46. data/ext/polars/src/series/export.rs +38 -38
  47. data/ext/polars/src/series/general.rs +4 -3
  48. data/ext/polars/src/series/map.rs +3 -2
  49. data/ext/polars/src/series/scatter.rs +4 -4
  50. data/ext/polars/src/utils.rs +31 -7
  51. data/lib/polars/array_expr.rb +23 -7
  52. data/lib/polars/array_name_space.rb +16 -2
  53. data/lib/polars/binary_name_space.rb +32 -0
  54. data/lib/polars/data_frame.rb +83 -11
  55. data/lib/polars/date_time_expr.rb +91 -3
  56. data/lib/polars/date_time_name_space.rb +7 -1
  57. data/lib/polars/expr.rb +122 -44
  58. data/lib/polars/functions/aggregation/horizontal.rb +4 -4
  59. data/lib/polars/functions/business.rb +2 -2
  60. data/lib/polars/functions/eager.rb +80 -7
  61. data/lib/polars/functions/lazy.rb +5 -2
  62. data/lib/polars/iceberg_dataset.rb +81 -14
  63. data/lib/polars/io/csv.rb +27 -5
  64. data/lib/polars/io/delta.rb +4 -0
  65. data/lib/polars/io/ipc.rb +1 -1
  66. data/lib/polars/io/lines.rb +4 -4
  67. data/lib/polars/io/sink_options.rb +4 -2
  68. data/lib/polars/lazy_frame.rb +97 -14
  69. data/lib/polars/list_expr.rb +21 -7
  70. data/lib/polars/list_name_space.rb +16 -2
  71. data/lib/polars/query_opt_flags.rb +22 -5
  72. data/lib/polars/schema.rb +9 -0
  73. data/lib/polars/selectors.rb +1 -1
  74. data/lib/polars/series.rb +106 -19
  75. data/lib/polars/sql_context.rb +2 -2
  76. data/lib/polars/string_cache.rb +19 -72
  77. data/lib/polars/string_expr.rb +1 -7
  78. data/lib/polars/string_name_space.rb +1 -7
  79. data/lib/polars/utils/construction/series.rb +8 -3
  80. data/lib/polars/utils/convert.rb +16 -6
  81. data/lib/polars/utils/parse.rb +7 -0
  82. data/lib/polars/utils/reduce_balanced.rb +43 -0
  83. data/lib/polars/utils/various.rb +5 -0
  84. data/lib/polars/version.rb +1 -1
  85. data/lib/polars.rb +5 -1
  86. metadata +4 -17
  87. data/ext/polars/src/functions/string_cache.rs +0 -24
@@ -1,4 +1,4 @@
1
- use magnus::Value;
1
+ use magnus::{Ruby, Value};
2
2
  use num_traits::{Float, NumCast};
3
3
  use polars_core::prelude::*;
4
4
 
@@ -10,34 +10,35 @@ use crate::series::RbSeries;
10
10
 
11
11
  impl RbSeries {
12
12
  /// Convert this Series to a Numo array.
13
- pub fn to_numo(&self) -> RbResult<Value> {
14
- series_to_numo(&self.series.read())
13
+ pub fn to_numo(rb: &Ruby, self_: &Self) -> RbResult<Value> {
14
+ series_to_numo(rb, &self_.series.read())
15
15
  }
16
16
  }
17
17
 
18
18
  /// Convert a Series to a Numo array.
19
- fn series_to_numo(s: &Series) -> RbResult<Value> {
20
- series_to_numo_with_copy(s)
19
+ fn series_to_numo(rb: &Ruby, s: &Series) -> RbResult<Value> {
20
+ series_to_numo_with_copy(rb, s)
21
21
  }
22
22
 
23
23
  /// Convert a Series to a Numo array, copying data in the process.
24
- fn series_to_numo_with_copy(s: &Series) -> RbResult<Value> {
24
+ fn series_to_numo_with_copy(rb: &Ruby, s: &Series) -> RbResult<Value> {
25
25
  use DataType::*;
26
26
  match s.dtype() {
27
- Int8 => numeric_series_to_numpy::<Int8Type, f32>(s),
28
- Int16 => numeric_series_to_numpy::<Int16Type, f32>(s),
29
- Int32 => numeric_series_to_numpy::<Int32Type, f64>(s),
30
- Int64 => numeric_series_to_numpy::<Int64Type, f64>(s),
31
- UInt8 => numeric_series_to_numpy::<UInt8Type, f32>(s),
32
- UInt16 => numeric_series_to_numpy::<UInt16Type, f32>(s),
33
- UInt32 => numeric_series_to_numpy::<UInt32Type, f64>(s),
34
- UInt64 => numeric_series_to_numpy::<UInt64Type, f64>(s),
35
- Float32 => numeric_series_to_numpy::<Float32Type, f32>(s),
36
- Float64 => numeric_series_to_numpy::<Float64Type, f64>(s),
37
- Boolean => boolean_series_to_numo(s),
27
+ Int8 => numeric_series_to_numo::<Int8Type, f32>(rb, s),
28
+ Int16 => numeric_series_to_numo::<Int16Type, f32>(rb, s),
29
+ Int32 => numeric_series_to_numo::<Int32Type, f64>(rb, s),
30
+ Int64 => numeric_series_to_numo::<Int64Type, f64>(rb, s),
31
+ UInt8 => numeric_series_to_numo::<UInt8Type, f32>(rb, s),
32
+ UInt16 => numeric_series_to_numo::<UInt16Type, f32>(rb, s),
33
+ UInt32 => numeric_series_to_numo::<UInt32Type, f64>(rb, s),
34
+ UInt64 => numeric_series_to_numo::<UInt64Type, f64>(rb, s),
35
+ Float32 => numeric_series_to_numo::<Float32Type, f32>(rb, s),
36
+ Float64 => numeric_series_to_numo::<Float64Type, f64>(rb, s),
37
+ Boolean => boolean_series_to_numo(rb, s),
38
38
  String => {
39
39
  let ca = s.str().unwrap();
40
- RbArray1::from_iter(ca)
40
+ let values = ca.iter();
41
+ RbArray1::from_iter(rb, values)
41
42
  }
42
43
  dt => {
43
44
  raise_err!(
@@ -49,7 +50,7 @@ fn series_to_numo_with_copy(s: &Series) -> RbResult<Value> {
49
50
  }
50
51
 
51
52
  /// Convert numeric types to f32 or f64 with NaN representing a null value.
52
- fn numeric_series_to_numpy<T, U>(s: &Series) -> RbResult<Value>
53
+ fn numeric_series_to_numo<T, U>(rb: &Ruby, s: &Series) -> RbResult<Value>
53
54
  where
54
55
  T: PolarsNumericType,
55
56
  T::Native: Element,
@@ -58,25 +59,25 @@ where
58
59
  let ca: &ChunkedArray<T> = s.as_ref().as_ref();
59
60
  if s.null_count() == 0 {
60
61
  let values = ca.into_no_null_iter();
61
- RbArray1::<T::Native>::from_iter(values)
62
+ RbArray1::<T::Native>::from_iter(rb, values)
62
63
  } else {
63
64
  let mapper = |opt_v: Option<T::Native>| match opt_v {
64
65
  Some(v) => NumCast::from(v).unwrap(),
65
66
  None => U::nan(),
66
67
  };
67
68
  let values = ca.iter().map(mapper);
68
- RbArray1::from_iter(values)
69
+ RbArray1::from_iter(rb, values)
69
70
  }
70
71
  }
71
72
 
72
73
  /// Convert booleans to bit if no nulls are present, otherwise convert to objects.
73
- fn boolean_series_to_numo(s: &Series) -> RbResult<Value> {
74
+ fn boolean_series_to_numo(rb: &Ruby, s: &Series) -> RbResult<Value> {
74
75
  let ca = s.bool().unwrap();
75
76
  if s.null_count() == 0 {
76
- let values = ca.into_no_null_iter();
77
- RbArray1::<bool>::from_iter(values)
77
+ let values = ca.no_null_iter();
78
+ RbArray1::<bool>::from_iter(rb, values)
78
79
  } else {
79
80
  let values = ca.iter();
80
- RbArray1::from_iter(values)
81
+ RbArray1::from_iter(rb, values)
81
82
  }
82
83
  }
@@ -11,9 +11,9 @@ use polars_io::{HiveOptions, RowIndex};
11
11
  use polars_utils::IdxSize;
12
12
  use polars_utils::slice_enum::Slice;
13
13
 
14
- use crate::RbResult;
15
14
  use crate::io::cloud_options::OptRbCloudOptions;
16
15
  use crate::prelude::Wrap;
16
+ use crate::{RbDataFrame, RbResult};
17
17
 
18
18
  /// Interface to `class ScanOptions` on the Ruby side
19
19
  pub struct RbScanOptions(Value);
@@ -25,8 +25,11 @@ impl TryConvert for RbScanOptions {
25
25
  }
26
26
 
27
27
  impl TryConvert for Wrap<TableStatistics> {
28
- fn try_convert(_ob: Value) -> RbResult<Self> {
29
- todo!();
28
+ fn try_convert(ob: Value) -> RbResult<Self> {
29
+ let attr: Value = ob.funcall("_df", ())?;
30
+ Ok(Wrap(TableStatistics(Arc::new(
31
+ <&RbDataFrame>::try_convert(attr)?.clone().df.into_inner(),
32
+ ))))
30
33
  }
31
34
  }
32
35
 
@@ -26,6 +26,7 @@ impl RbSinkOptions {
26
26
  let sync_on_close: Option<Wrap<SyncOnCloseType>> = self.0.funcall("sync_on_close", ())?;
27
27
  let storage_options: OptRbCloudOptions = self.0.funcall("storage_options", ())?;
28
28
  let credential_provider: Option<Value> = self.0.funcall("credential_provider", ())?;
29
+ let sinked_paths_callback: Option<Value> = self.0.funcall("sinked_paths_callback", ())?;
29
30
 
30
31
  let cloud_options =
31
32
  storage_options.extract_opt_cloud_options(cloud_scheme, credential_provider)?;
@@ -37,6 +38,7 @@ impl RbSinkOptions {
37
38
  maintain_order,
38
39
  sync_on_close,
39
40
  cloud_options: cloud_options.map(Arc::new),
41
+ sinked_paths_callback: sinked_paths_callback.map(|_| todo!()),
40
42
  };
41
43
 
42
44
  Ok(unified_sink_args)
@@ -3,11 +3,14 @@ use magnus::{
3
3
  IntoValue, RArray, RHash, Ruby, TryConvert, Value, r_hash::ForEach, value::ReprValue,
4
4
  };
5
5
  use parking_lot::Mutex;
6
+ use polars::frame::PivotColumnNaming;
6
7
  use polars::io::RowIndex;
7
8
  use polars::lazy::frame::LazyFrame;
8
9
  use polars::prelude::*;
10
+ use polars_core::query_result::QueryResult;
9
11
  use polars_plan::dsl::ScanSources;
10
12
  use polars_plan::plans::{HintIR, Sorted};
13
+ use std::ffi::CString;
11
14
  use std::num::NonZeroUsize;
12
15
 
13
16
  use super::{RbLazyFrame, RbOptFlags};
@@ -22,10 +25,10 @@ use crate::ruby::gvl::GvlExt;
22
25
  use crate::ruby::lazy::RubyUdfLazyFrameExt;
23
26
  use crate::ruby::plan_callback::PlanCallbackExt;
24
27
  use crate::ruby::ruby_function::RubyObject;
28
+ use crate::ruby::utils::TryIntoValue;
25
29
  use crate::utils::{EnterPolarsExt, to_rb_err};
26
30
  use crate::{
27
- RbArrowArrayStream, RbDataFrame, RbExpr, RbLazyGroupBy, RbPolarsErr, RbResult, RbTypeError,
28
- RbValueError,
31
+ RbCapsule, RbDataFrame, RbExpr, RbLazyGroupBy, RbPolarsErr, RbResult, RbTypeError, RbValueError,
29
32
  };
30
33
 
31
34
  fn rbobject_to_first_path_and_scan_sources(
@@ -134,6 +137,7 @@ impl RbLazyFrame {
134
137
  let cloud_options = OptRbCloudOptions::try_convert(arguments[28])?;
135
138
  let credential_provider = Option::<Value>::try_convert(arguments[29])?;
136
139
  let include_file_paths = Option::<String>::try_convert(arguments[30])?;
140
+ let missing_columns = Option::<Wrap<MissingColumnsPolicy>>::try_convert(arguments[31])?;
137
141
  // end arguments
138
142
 
139
143
  let null_values = null_values.map(|w| w.0);
@@ -195,7 +199,8 @@ impl RbLazyFrame {
195
199
  .with_decimal_comma(decimal_comma)
196
200
  .with_glob(glob)
197
201
  .with_raise_if_empty(raise_if_empty)
198
- .with_include_file_paths(include_file_paths.map(|x| x.into()));
202
+ .with_include_file_paths(include_file_paths.map(|x| x.into()))
203
+ .with_missing_columns_policy(missing_columns.map(|x| x.0));
199
204
 
200
205
  if let Some(lambda) = with_schema_modify {
201
206
  let f = |schema: Schema| {
@@ -413,7 +418,11 @@ impl RbLazyFrame {
413
418
  pub fn collect(rb: &Ruby, self_: &Self, engine: Wrap<Engine>) -> RbResult<RbDataFrame> {
414
419
  rb.enter_polars_df(|| {
415
420
  let ldf = self_.ldf.read().clone();
416
- ldf.collect_with_engine(engine.0)
421
+ ldf.collect_with_engine(engine.0).map(|r| match r {
422
+ QueryResult::Single(df) => df,
423
+ // TODO: Should return query results
424
+ QueryResult::Multiple(_) => DataFrame::empty(),
425
+ })
417
426
  })
418
427
  }
419
428
 
@@ -810,6 +819,12 @@ impl RbLazyFrame {
810
819
  .into())
811
820
  }
812
821
 
822
+ pub fn gather(&self, idxs: &Self, null_on_oob: bool) -> Self {
823
+ let ldf = self.ldf.read().clone();
824
+ let idxs = idxs.clone().ldf.into_inner();
825
+ ldf.gather(idxs, null_on_oob).into()
826
+ }
827
+
813
828
  pub fn with_column(&self, expr: &RbExpr) -> Self {
814
829
  let ldf = self.ldf.read().clone();
815
830
  ldf.with_column(expr.inner.clone()).into()
@@ -1090,6 +1105,7 @@ impl RbLazyFrame {
1090
1105
  agg: &RbExpr,
1091
1106
  maintain_order: bool,
1092
1107
  separator: String,
1108
+ column_naming: Wrap<PivotColumnNaming>,
1093
1109
  ) -> Self {
1094
1110
  let ldf = self.ldf.read().clone();
1095
1111
  ldf.pivot(
@@ -1100,6 +1116,7 @@ impl RbLazyFrame {
1100
1116
  agg.inner.clone(),
1101
1117
  maintain_order,
1102
1118
  separator.into(),
1119
+ column_naming.0,
1103
1120
  )
1104
1121
  .into()
1105
1122
  }
@@ -1141,7 +1158,7 @@ impl RbLazyFrame {
1141
1158
  opt.set(OptFlags::PREDICATE_PUSHDOWN, predicate_pushdown);
1142
1159
  opt.set(OptFlags::PROJECTION_PUSHDOWN, projection_pushdown);
1143
1160
  opt.set(OptFlags::SLICE_PUSHDOWN, slice_pushdown);
1144
- opt.set(OptFlags::NEW_STREAMING, streamable);
1161
+ opt.set(OptFlags::STREAMING, streamable);
1145
1162
 
1146
1163
  self.ldf
1147
1164
  .read()
@@ -1182,14 +1199,12 @@ impl RbLazyFrame {
1182
1199
  let schema = rb.enter_polars(|| self_.ldf.write().collect_schema())?;
1183
1200
 
1184
1201
  let schema_dict = rb.hash_new();
1185
- schema.iter_fields().for_each(|fld| {
1186
- schema_dict
1187
- .aset::<String, Value>(
1188
- fld.name().to_string(),
1189
- Wrap(fld.dtype().clone()).into_value_with(rb),
1190
- )
1191
- .unwrap();
1192
- });
1202
+ for fld in schema.iter_fields() {
1203
+ schema_dict.aset(
1204
+ fld.name().to_string(),
1205
+ Wrap(fld.dtype().clone()).try_into_value_with(rb)?,
1206
+ )?;
1207
+ }
1193
1208
  Ok(schema_dict)
1194
1209
  }
1195
1210
 
@@ -1209,12 +1224,12 @@ impl RbLazyFrame {
1209
1224
  ldf.count().into()
1210
1225
  }
1211
1226
 
1212
- pub fn merge_sorted(&self, other: &Self, key: String) -> RbResult<Self> {
1227
+ pub fn merge_sorted(&self, other: &Self, key: String, maintain_order: bool) -> RbResult<Self> {
1213
1228
  let out = self
1214
1229
  .ldf
1215
1230
  .read()
1216
1231
  .clone()
1217
- .merge_sorted(other.ldf.read().clone(), &key)
1232
+ .merge_sorted(other.ldf.read().clone(), &key, maintain_order)
1218
1233
  .map_err(RbPolarsErr::from)?;
1219
1234
  Ok(out.into())
1220
1235
  }
@@ -1299,7 +1314,8 @@ impl RbCollectBatches {
1299
1314
  let iter = Box::new(ArrowStreamIterator::new(self_.inner.clone(), dtype.clone()));
1300
1315
  let field = ArrowField::new(PlSmallStr::EMPTY, dtype, false);
1301
1316
  let stream = export_iterator(iter, field);
1302
- Ok(RbArrowArrayStream { stream }.into_value_with(rb))
1317
+ let stream_capsule_name = CString::new("arrow_array_stream").unwrap();
1318
+ Ok(RbCapsule::new(stream, Some(stream_capsule_name)).into_value_with(rb))
1303
1319
  }
1304
1320
  }
1305
1321
 
@@ -52,7 +52,8 @@ flag_getter_setters! {
52
52
  (COMM_SUBEXPR_ELIM, get_comm_subexpr_elim, set_comm_subexpr_elim, clear=true)
53
53
  (CHECK_ORDER_OBSERVE, get_check_order_observe, set_check_order_observe, clear=true)
54
54
  (FAST_PROJECTION, get_fast_projection, set_fast_projection, clear=true)
55
+ (SORT_COLLAPSE, get_sort_collapse, set_sort_collapse, clear=true)
55
56
 
56
57
  (EAGER, get_eager, set_eager, clear=true)
57
- (NEW_STREAMING, get_streaming, set_streaming, clear=true)
58
+ (STREAMING, get_streaming, set_streaming, clear=true)
58
59
  }
@@ -25,6 +25,10 @@ mod testing;
25
25
  mod timeout;
26
26
  mod utils;
27
27
 
28
+ use crate::conversion::Wrap;
29
+
30
+ pub type RbDataType = Wrap<polars_core::datatypes::DataType>;
31
+
28
32
  use catalog::unity::RbCatalogClient;
29
33
  use conversion::*;
30
34
  use dataframe::RbDataFrame;
@@ -32,12 +36,12 @@ use error::RbPolarsErr;
32
36
  use expr::RbExpr;
33
37
  use expr::datatype::RbDataTypeExpr;
34
38
  use expr::selector::RbSelector;
35
- use functions::string_cache::RbStringCacheHolder;
36
39
  use functions::whenthen::{RbChainedThen, RbChainedWhen, RbThen, RbWhen};
37
- use interop::arrow::to_rb::{RbArrowArrayStream, RbArrowSchema};
40
+ use interop::arrow::to_rb::RbArrowSchema;
38
41
  use lazyframe::{RbCollectBatches, RbInProcessQuery, RbLazyFrame, RbOptFlags};
39
42
  use lazygroupby::RbLazyGroupBy;
40
43
  use magnus::{Ruby, function, method, prelude::*};
44
+ use ruby::capsule::RbCapsule;
41
45
  use ruby::exceptions::{RbTypeError, RbValueError};
42
46
  use series::RbSeries;
43
47
  use sql::RbSQLContext;
@@ -203,7 +207,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
203
207
  class.define_method("first", method!(RbExpr::first, 1))?;
204
208
  class.define_method("last", method!(RbExpr::last, 1))?;
205
209
  class.define_method("item", method!(RbExpr::item, 1))?;
206
- class.define_method("implode", method!(RbExpr::implode, 0))?;
210
+ class.define_method("implode", method!(RbExpr::implode, 1))?;
207
211
  class.define_method("quantile", method!(RbExpr::quantile, 2))?;
208
212
  class.define_method("cut", method!(RbExpr::cut, 4))?;
209
213
  class.define_method("qcut", method!(RbExpr::qcut, 5))?;
@@ -229,7 +233,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
229
233
  class.define_method("arg_min", method!(RbExpr::arg_min, 0))?;
230
234
  class.define_method("index_of", method!(RbExpr::index_of, 1))?;
231
235
  class.define_method("search_sorted", method!(RbExpr::search_sorted, 3))?;
232
- class.define_method("gather", method!(RbExpr::gather, 1))?;
236
+ class.define_method("gather", method!(RbExpr::gather, 2))?;
233
237
  class.define_method("get", method!(RbExpr::get, 2))?;
234
238
  class.define_method("sort_by", method!(RbExpr::sort_by, 5))?;
235
239
  class.define_method("shift", method!(RbExpr::shift, 2))?;
@@ -260,6 +264,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
260
264
  class.define_method("rechunk", method!(RbExpr::rechunk, 0))?;
261
265
  class.define_method("round", method!(RbExpr::round, 2))?;
262
266
  class.define_method("round_sig_figs", method!(RbExpr::round_sig_figs, 1))?;
267
+ class.define_method("truncate", method!(RbExpr::truncate, 1))?;
263
268
  class.define_method("floor", method!(RbExpr::floor, 0))?;
264
269
  class.define_method("ceil", method!(RbExpr::ceil, 0))?;
265
270
  class.define_method("clip", method!(RbExpr::clip, 2))?;
@@ -337,13 +342,8 @@ fn init(ruby: &Ruby) -> RbResult<()> {
337
342
  class.define_method("arr_var", method!(RbExpr::arr_var, 1))?;
338
343
  class.define_method("arr_mean", method!(RbExpr::arr_mean, 0))?;
339
344
  class.define_method("arr_median", method!(RbExpr::arr_median, 0))?;
340
- class.define_method("arr_unique", method!(RbExpr::arr_unique, 1))?;
341
- class.define_method("arr_n_unique", method!(RbExpr::arr_n_unique, 0))?;
342
345
  class.define_method("arr_to_list", method!(RbExpr::arr_to_list, 0))?;
343
- class.define_method("arr_all", method!(RbExpr::arr_all, 0))?;
344
- class.define_method("arr_any", method!(RbExpr::arr_any, 0))?;
345
346
  class.define_method("arr_sort", method!(RbExpr::arr_sort, 2))?;
346
- class.define_method("arr_reverse", method!(RbExpr::arr_reverse, 0))?;
347
347
  class.define_method("arr_arg_min", method!(RbExpr::arr_arg_min, 0))?;
348
348
  class.define_method("arr_arg_max", method!(RbExpr::arr_arg_max, 0))?;
349
349
  class.define_method("arr_get", method!(RbExpr::arr_get, 2))?;
@@ -479,7 +479,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
479
479
  class.define_method("dt_replace", method!(RbExpr::dt_replace, 8))?;
480
480
  class.define_method("dt_combine", method!(RbExpr::dt_combine, 2))?;
481
481
  class.define_method("dot", method!(RbExpr::dot, 1))?;
482
- class.define_method("reinterpret", method!(RbExpr::reinterpret, 1))?;
482
+ class.define_method("reinterpret", method!(RbExpr::reinterpret, 2))?;
483
483
  class.define_method("mode", method!(RbExpr::mode, 1))?;
484
484
  class.define_method("interpolate", method!(RbExpr::interpolate, 1))?;
485
485
  class.define_method("interpolate_by", method!(RbExpr::interpolate_by, 1))?;
@@ -527,16 +527,11 @@ fn init(ruby: &Ruby) -> RbResult<()> {
527
527
  class.define_method("list_var", method!(RbExpr::list_var, 1))?;
528
528
  class.define_method("list_tail", method!(RbExpr::list_tail, 1))?;
529
529
  class.define_method("list_sort", method!(RbExpr::list_sort, 2))?;
530
- class.define_method("list_reverse", method!(RbExpr::list_reverse, 0))?;
531
- class.define_method("list_n_unique", method!(RbExpr::list_n_unique, 0))?;
532
- class.define_method("list_unique", method!(RbExpr::list_unique, 1))?;
533
530
  class.define_method("list_set_operation", method!(RbExpr::list_set_operation, 2))?;
534
531
  class.define_method("list_get", method!(RbExpr::list_get, 2))?;
535
532
  class.define_method("list_join", method!(RbExpr::list_join, 2))?;
536
533
  class.define_method("list_arg_min", method!(RbExpr::list_arg_min, 0))?;
537
534
  class.define_method("list_arg_max", method!(RbExpr::list_arg_max, 0))?;
538
- class.define_method("list_all", method!(RbExpr::list_all, 0))?;
539
- class.define_method("list_any", method!(RbExpr::list_any, 0))?;
540
535
  class.define_method("list_diff", method!(RbExpr::list_diff, 2))?;
541
536
  class.define_method("list_shift", method!(RbExpr::list_shift, 1))?;
542
537
  class.define_method("list_slice", method!(RbExpr::list_slice, 2))?;
@@ -570,6 +565,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
570
565
  class.define_method("extend_constant", method!(RbExpr::extend_constant, 2))?;
571
566
  class.define_method("any", method!(RbExpr::any, 1))?;
572
567
  class.define_method("all", method!(RbExpr::all, 1))?;
568
+ class.define_method("is_empty", method!(RbExpr::is_empty, 1))?;
573
569
  class.define_method(
574
570
  "struct_field_by_name",
575
571
  method!(RbExpr::struct_field_by_name, 1),
@@ -593,7 +589,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
593
589
  class.define_method("exp", method!(RbExpr::exp, 0))?;
594
590
  class.define_method("entropy", method!(RbExpr::entropy, 2))?;
595
591
  class.define_method("_hash", method!(RbExpr::hash, 4))?;
596
- class.define_method("set_sorted_flag", method!(RbExpr::set_sorted_flag, 1))?;
592
+ class.define_method("set_sorted_flag", method!(RbExpr::set_sorted_flag, 2))?;
597
593
  class.define_method("replace", method!(RbExpr::replace, 2))?;
598
594
  class.define_method("replace_strict", method!(RbExpr::replace_strict, 4))?;
599
595
  class.define_method("hist", method!(RbExpr::hist, 4))?;
@@ -801,18 +797,6 @@ fn init(ruby: &Ruby) -> RbResult<()> {
801
797
  "thread_pool_size",
802
798
  function!(functions::meta::thread_pool_size, 0),
803
799
  )?;
804
- class.define_singleton_method(
805
- "enable_string_cache",
806
- function!(functions::string_cache::enable_string_cache, 0),
807
- )?;
808
- class.define_singleton_method(
809
- "disable_string_cache",
810
- function!(functions::string_cache::disable_string_cache, 0),
811
- )?;
812
- class.define_singleton_method(
813
- "using_string_cache",
814
- function!(functions::string_cache::using_string_cache, 0),
815
- )?;
816
800
  class.define_singleton_method(
817
801
  "set_float_fmt",
818
802
  function!(functions::meta::set_float_fmt, 1),
@@ -961,6 +945,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
961
945
  class.define_method("join_asof", method!(RbLazyFrame::join_asof, 14))?;
962
946
  class.define_method("join", method!(RbLazyFrame::join, 11))?;
963
947
  class.define_method("join_where", method!(RbLazyFrame::join_where, 3))?;
948
+ class.define_method("gather", method!(RbLazyFrame::gather, 2))?;
964
949
  class.define_method("with_column", method!(RbLazyFrame::with_column, 1))?;
965
950
  class.define_method("with_columns", method!(RbLazyFrame::with_columns, 1))?;
966
951
  class.define_method(
@@ -991,7 +976,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
991
976
  class.define_method("drop_nulls", method!(RbLazyFrame::drop_nulls, 1))?;
992
977
  class.define_method("slice", method!(RbLazyFrame::slice, 2))?;
993
978
  class.define_method("tail", method!(RbLazyFrame::tail, 1))?;
994
- class.define_method("pivot", method!(RbLazyFrame::pivot, 7))?;
979
+ class.define_method("pivot", method!(RbLazyFrame::pivot, 8))?;
995
980
  class.define_method("unpivot", method!(RbLazyFrame::unpivot, 4))?;
996
981
  class.define_method("with_row_index", method!(RbLazyFrame::with_row_index, 2))?;
997
982
  class.define_method("map_batches", method!(RbLazyFrame::map_batches, 7))?;
@@ -1002,7 +987,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
1002
987
  class.define_method("collect_schema", method!(RbLazyFrame::collect_schema, 0))?;
1003
988
  class.define_method("unnest", method!(RbLazyFrame::unnest, 2))?;
1004
989
  class.define_method("count", method!(RbLazyFrame::count, 0))?;
1005
- class.define_method("merge_sorted", method!(RbLazyFrame::merge_sorted, 2))?;
990
+ class.define_method("merge_sorted", method!(RbLazyFrame::merge_sorted, 3))?;
1006
991
  class.define_method("hint_sorted", method!(RbLazyFrame::hint_sorted, 3))?;
1007
992
  class.define_method(
1008
993
  "collect_concurrently",
@@ -1124,6 +1109,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
1124
1109
  class.define_method("slice", method!(RbSeries::slice, 2))?;
1125
1110
  class.define_method("any", method!(RbSeries::any, 1))?;
1126
1111
  class.define_method("all", method!(RbSeries::all, 1))?;
1112
+ class.define_method("is_empty", method!(RbSeries::is_empty, 1))?;
1127
1113
  class.define_method("arg_min", method!(RbSeries::arg_min, 0))?;
1128
1114
  class.define_method("arg_max", method!(RbSeries::arg_max, 0))?;
1129
1115
  class.define_method(
@@ -1150,6 +1136,7 @@ fn init(ruby: &Ruby) -> RbResult<()> {
1150
1136
  class.define_method("to_s", method!(RbSeries::as_str, 0))?;
1151
1137
  class.define_method("len", method!(RbSeries::len, 0))?;
1152
1138
  class.define_method("to_a", method!(RbSeries::to_a, 0))?;
1139
+ class.define_method("arrow_c_stream", method!(RbSeries::__arrow_c_stream__, 0))?;
1153
1140
  class.define_method("median", method!(RbSeries::median, 0))?;
1154
1141
  class.define_method("quantile", method!(RbSeries::quantile, 2))?;
1155
1142
  class.define_method("_clone", method!(RbSeries::clone, 0))?;
@@ -1355,13 +1342,10 @@ fn init(ruby: &Ruby) -> RbResult<()> {
1355
1342
  class.define_method("register", method!(RbSQLContext::register, 2))?;
1356
1343
  class.define_method("unregister", method!(RbSQLContext::unregister, 1))?;
1357
1344
 
1358
- // string cache
1359
- let class = module.define_class("RbStringCacheHolder", ruby.class_object())?;
1360
- class.define_singleton_method("hold", function!(RbStringCacheHolder::hold, 0))?;
1361
-
1362
- // arrow array stream
1363
- let class = module.define_class("ArrowArrayStream", ruby.class_object())?;
1364
- class.define_method("to_i", method!(RbArrowArrayStream::to_i, 0))?;
1345
+ // capsule
1346
+ let class = module.define_class("Capsule", ruby.class_object())?;
1347
+ class.define_method("to_i", method!(RbCapsule::to_i, 0))?;
1348
+ class.define_method("name", method!(RbCapsule::name, 0))?;
1365
1349
 
1366
1350
  // arrow schema
1367
1351
  let class = module.define_class("ArrowSchema", ruby.class_object())?;
@@ -4,7 +4,7 @@ use polars::prelude::*;
4
4
  use crate::expr::ToExprs;
5
5
  use crate::expr::datatype::RbDataTypeExpr;
6
6
  use crate::ruby::ruby_udf::{RubyUdfExpression, RubyUdfExt};
7
- use crate::ruby::utils::to_pl_err;
7
+ use crate::ruby::utils::{TryIntoValue, to_pl_err};
8
8
  use crate::{RbExpr, RbResult, RbSeries, Wrap};
9
9
 
10
10
  pub(crate) fn call_lambda_with_series(
@@ -17,7 +17,10 @@ pub(crate) fn call_lambda_with_series(
17
17
 
18
18
  // Set return_dtype in kwargs
19
19
  let dict = rb.hash_new();
20
- let output_dtype = output_dtype.map(Wrap);
20
+ let output_dtype = output_dtype
21
+ .map(|v| Wrap(v).try_into_value_with(rb))
22
+ .transpose()
23
+ .map_err(to_pl_err)?;
21
24
  dict.aset(rb.sym_new("return_dtype"), output_dtype)
22
25
  .map_err(to_pl_err)?;
23
26
 
@@ -4,6 +4,7 @@ use super::*;
4
4
  use crate::Wrap;
5
5
  use crate::error::RbPolarsErr;
6
6
  use crate::prelude::ObjectValue;
7
+ use crate::ruby::utils::TryIntoValue;
7
8
 
8
9
  pub trait ApplyLambdaGeneric {
9
10
  fn apply_generic(&self, rb: &Ruby, lambda: Value, skip_nulls: bool) -> RbResult<Series>;
@@ -25,7 +26,7 @@ fn call_and_collect_anyvalues<T, I>(
25
26
  skip_nulls: bool,
26
27
  ) -> RbResult<Vec<AnyValue<'static>>>
27
28
  where
28
- T: IntoValue,
29
+ T: TryIntoValue,
29
30
  I: Iterator<Item = Option<T>>,
30
31
  {
31
32
  let mut avs = Vec::with_capacity(len);
@@ -36,7 +37,7 @@ where
36
37
  continue;
37
38
  }
38
39
  None => rb.qnil().into_value_with(rb),
39
- Some(val) => val.into_value_with(rb),
40
+ Some(val) => val.try_into_value_with(rb)?,
40
41
  };
41
42
  let av: Option<Wrap<AnyValue>> = lambda.funcall("call", (arg,))?;
42
43
  avs.push(av.map(|w| w.0).unwrap_or(AnyValue::Null));
@@ -46,7 +47,7 @@ where
46
47
 
47
48
  impl ApplyLambdaGeneric for BooleanChunked {
48
49
  fn apply_generic(&self, rb: &Ruby, lambda: Value, skip_nulls: bool) -> RbResult<Series> {
49
- let avs = call_and_collect_anyvalues(rb, lambda, self.len(), self.into_iter(), skip_nulls)?;
50
+ let avs = call_and_collect_anyvalues(rb, lambda, self.len(), self.iter(), skip_nulls)?;
50
51
  Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(RbPolarsErr::from)?)
51
52
  }
52
53
 
@@ -57,7 +58,7 @@ impl ApplyLambdaGeneric for BooleanChunked {
57
58
  datatype: &DataType,
58
59
  skip_nulls: bool,
59
60
  ) -> RbResult<Series> {
60
- let avs = call_and_collect_anyvalues(rb, lambda, self.len(), self.into_iter(), skip_nulls)?;
61
+ let avs = call_and_collect_anyvalues(rb, lambda, self.len(), self.iter(), skip_nulls)?;
61
62
  Ok(
62
63
  Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
63
64
  .map_err(RbPolarsErr::from)?,
@@ -71,7 +72,7 @@ where
71
72
  T::Native: IntoValue + TryConvert,
72
73
  {
73
74
  fn apply_generic(&self, rb: &Ruby, lambda: Value, skip_nulls: bool) -> RbResult<Series> {
74
- let avs = call_and_collect_anyvalues(rb, lambda, self.len(), self.into_iter(), skip_nulls)?;
75
+ let avs = call_and_collect_anyvalues(rb, lambda, self.len(), self.iter(), skip_nulls)?;
75
76
  Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(RbPolarsErr::from)?)
76
77
  }
77
78
 
@@ -82,7 +83,7 @@ where
82
83
  datatype: &DataType,
83
84
  skip_nulls: bool,
84
85
  ) -> RbResult<Series> {
85
- let avs = call_and_collect_anyvalues(rb, lambda, self.len(), self.into_iter(), skip_nulls)?;
86
+ let avs = call_and_collect_anyvalues(rb, lambda, self.len(), self.iter(), skip_nulls)?;
86
87
  Ok(
87
88
  Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
88
89
  .map_err(RbPolarsErr::from)?,
@@ -92,7 +93,7 @@ where
92
93
 
93
94
  impl ApplyLambdaGeneric for StringChunked {
94
95
  fn apply_generic(&self, rb: &Ruby, lambda: Value, skip_nulls: bool) -> RbResult<Series> {
95
- let avs = call_and_collect_anyvalues(rb, lambda, self.len(), self.into_iter(), skip_nulls)?;
96
+ let avs = call_and_collect_anyvalues(rb, lambda, self.len(), self.iter(), skip_nulls)?;
96
97
  Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(RbPolarsErr::from)?)
97
98
  }
98
99
 
@@ -103,7 +104,7 @@ impl ApplyLambdaGeneric for StringChunked {
103
104
  datatype: &DataType,
104
105
  skip_nulls: bool,
105
106
  ) -> RbResult<Series> {
106
- let avs = call_and_collect_anyvalues(rb, lambda, self.len(), self.into_iter(), skip_nulls)?;
107
+ let avs = call_and_collect_anyvalues(rb, lambda, self.len(), self.iter(), skip_nulls)?;
107
108
  Ok(
108
109
  Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
109
110
  .map_err(RbPolarsErr::from)?,
@@ -113,7 +114,7 @@ impl ApplyLambdaGeneric for StringChunked {
113
114
 
114
115
  impl ApplyLambdaGeneric for ListChunked {
115
116
  fn apply_generic(&self, rb: &Ruby, lambda: Value, skip_nulls: bool) -> RbResult<Series> {
116
- let it = self.into_iter().map(|opt_s| opt_s.map(Wrap));
117
+ let it = self.series_iter().map(|opt_s| opt_s.map(Wrap));
117
118
  let avs = call_and_collect_anyvalues(rb, lambda, self.len(), it, skip_nulls)?;
118
119
  Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(RbPolarsErr::from)?)
119
120
  }
@@ -125,7 +126,7 @@ impl ApplyLambdaGeneric for ListChunked {
125
126
  datatype: &DataType,
126
127
  skip_nulls: bool,
127
128
  ) -> RbResult<Series> {
128
- let it = self.into_iter().map(|opt_s| opt_s.map(Wrap));
129
+ let it = self.series_iter().map(|opt_s| opt_s.map(Wrap));
129
130
  let avs = call_and_collect_anyvalues(rb, lambda, self.len(), it, skip_nulls)?;
130
131
  Ok(
131
132
  Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
@@ -136,7 +137,7 @@ impl ApplyLambdaGeneric for ListChunked {
136
137
 
137
138
  impl ApplyLambdaGeneric for ArrayChunked {
138
139
  fn apply_generic(&self, rb: &Ruby, lambda: Value, skip_nulls: bool) -> RbResult<Series> {
139
- let it = self.into_iter().map(|opt_s| Some(RbSeries::new(opt_s?)));
140
+ let it = self.series_iter().map(|opt_s| Some(RbSeries::new(opt_s?)));
140
141
  let avs = call_and_collect_anyvalues(rb, lambda, self.len(), it, skip_nulls)?;
141
142
  Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(RbPolarsErr::from)?)
142
143
  }
@@ -148,7 +149,7 @@ impl ApplyLambdaGeneric for ArrayChunked {
148
149
  datatype: &DataType,
149
150
  skip_nulls: bool,
150
151
  ) -> RbResult<Series> {
151
- let it = self.into_iter().map(|opt_s| Some(RbSeries::new(opt_s?)));
152
+ let it = self.series_iter().map(|opt_s| Some(RbSeries::new(opt_s?)));
152
153
  let avs = call_and_collect_anyvalues(rb, lambda, self.len(), it, skip_nulls)?;
153
154
  Ok(
154
155
  Series::from_any_values_and_dtype(self.name().clone(), &avs, datatype, true)
@@ -159,12 +160,12 @@ impl ApplyLambdaGeneric for ArrayChunked {
159
160
 
160
161
  impl ApplyLambdaGeneric for ObjectChunked<ObjectValue> {
161
162
  fn apply_generic(&self, rb: &Ruby, lambda: Value, skip_nulls: bool) -> RbResult<Series> {
162
- // TODO improve into_iter
163
+ // TODO improve iter
163
164
  let avs = call_and_collect_anyvalues(
164
165
  rb,
165
166
  lambda,
166
167
  self.len(),
167
- self.into_iter().map(|v| v.cloned()),
168
+ self.iter().map(|v| v.cloned()),
168
169
  skip_nulls,
169
170
  )?;
170
171
  Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(RbPolarsErr::from)?)
@@ -177,12 +178,12 @@ impl ApplyLambdaGeneric for ObjectChunked<ObjectValue> {
177
178
  datatype: &DataType,
178
179
  skip_nulls: bool,
179
180
  ) -> RbResult<Series> {
180
- // TODO improve into_iter
181
+ // TODO improve iter
181
182
  let avs = call_and_collect_anyvalues(
182
183
  rb,
183
184
  lambda,
184
185
  self.len(),
185
- self.into_iter().map(|v| v.cloned()),
186
+ self.iter().map(|v| v.cloned()),
186
187
  skip_nulls,
187
188
  )?;
188
189
  Ok(
@@ -223,7 +224,7 @@ impl ApplyLambdaGeneric for BinaryChunked {
223
224
  rb,
224
225
  lambda,
225
226
  self.len(),
226
- self.into_iter().map(|v| v.map(|v2| rb.str_from_slice(v2))),
227
+ self.iter().map(|v| v.map(|v2| rb.str_from_slice(v2))),
227
228
  skip_nulls,
228
229
  )?;
229
230
  Ok(Series::from_any_values(self.name().clone(), &avs, true).map_err(RbPolarsErr::from)?)
@@ -240,7 +241,7 @@ impl ApplyLambdaGeneric for BinaryChunked {
240
241
  rb,
241
242
  lambda,
242
243
  self.len(),
243
- self.into_iter().map(|v| v.map(|v2| rb.str_from_slice(v2))),
244
+ self.iter().map(|v| v.map(|v2| rb.str_from_slice(v2))),
244
245
  skip_nulls,
245
246
  )?;
246
247
  Ok(