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
@@ -5,6 +5,7 @@ use std::sync::OnceLock;
5
5
 
6
6
  use arrow::array::Array;
7
7
  use magnus::{IntoValue, Ruby, Value, prelude::*, value::Opaque};
8
+ use polars::chunked_array::object::ObjectArray;
8
9
  use polars::prelude::*;
9
10
  use polars_core::chunked_array::object::builder::ObjectChunkedBuilder;
10
11
  use polars_core::chunked_array::object::registry;
@@ -23,7 +24,7 @@ use crate::ruby::gvl::GvlExt;
23
24
  use crate::ruby::ruby_convert_registry::{FromRubyConvertRegistry, RubyConvertRegistry};
24
25
  use crate::ruby::ruby_udf;
25
26
  use crate::ruby::thread::{is_non_ruby_thread, run_in_ruby_thread};
26
- use crate::ruby::utils::to_pl_err;
27
+ use crate::ruby::utils::{TryIntoValue, to_pl_err};
27
28
  use crate::series::RbSeries;
28
29
 
29
30
  fn ruby_function_caller_series(
@@ -86,16 +87,23 @@ pub unsafe fn register_startup_deps(catch_keyboard_interrupt: bool) {
86
87
 
87
88
  let object_converter = Arc::new(|av: AnyValue| {
88
89
  let object = Ruby::attach(|rb| ObjectValue {
89
- inner: Wrap(av).into_value_with(rb).into(),
90
+ inner: Wrap(av).try_into_value_with(rb).unwrap().into(),
90
91
  });
91
92
  Box::new(object) as Box<dyn Any>
92
93
  });
93
94
  let rbobject_converter = Arc::new(|av: AnyValue| {
94
- let object = Ruby::attach(|rb| Wrap(av).into_value_with(rb));
95
+ let object = Ruby::attach(|rb| Wrap(av).try_into_value_with(rb).unwrap());
95
96
  Box::new(object) as Box<dyn Any>
96
97
  });
97
- fn object_array_getter(_arr: &dyn Array, _idx: usize) -> Option<AnyValue<'_>> {
98
- todo!();
98
+ fn object_array_getter(arr: &dyn Array, idx: usize) -> Option<AnyValue<'_>> {
99
+ let arr = arr
100
+ .as_any()
101
+ .downcast_ref::<ObjectArray<ObjectValue>>()
102
+ .unwrap();
103
+ arr.get(idx).map(|v| AnyValue::Object(v))
104
+ }
105
+ fn with_gil(f: &mut dyn FnMut()) {
106
+ Ruby::attach(|_| f())
99
107
  }
100
108
 
101
109
  crate::ruby::ruby_convert_registry::register_converters(RubyConvertRegistry {
@@ -170,13 +178,13 @@ pub unsafe fn register_startup_deps(catch_keyboard_interrupt: bool) {
170
178
  }),
171
179
  schema: Arc::new(|schema| {
172
180
  Ruby::attach(|rb| {
173
- Ok(Wrap(
181
+ Wrap(
174
182
  schema
175
183
  .downcast_ref::<polars_core::schema::Schema>()
176
184
  .unwrap()
177
185
  .clone(),
178
186
  )
179
- .into_value_with(rb))
187
+ .try_into_value_with(rb)
180
188
  })
181
189
  }),
182
190
  },
@@ -190,6 +198,7 @@ pub unsafe fn register_startup_deps(catch_keyboard_interrupt: bool) {
190
198
  rbobject_converter,
191
199
  physical_dtype,
192
200
  Arc::new(object_array_getter),
201
+ Arc::new(with_gil),
193
202
  );
194
203
 
195
204
  // Register SERIES UDF.
@@ -0,0 +1,27 @@
1
+ use std::any::Any;
2
+ use std::ffi::CString;
3
+
4
+ #[magnus::wrap(class = "Polars::Capsule")]
5
+ pub struct RbCapsule {
6
+ value: Box<dyn Any + Send>,
7
+ name: Option<CString>,
8
+ }
9
+
10
+ impl RbCapsule {
11
+ pub fn new<T: 'static + Send>(value: T, name: Option<CString>) -> Self {
12
+ Self {
13
+ value: Box::new(value),
14
+ name,
15
+ }
16
+ }
17
+
18
+ pub fn to_i(&self) -> usize {
19
+ (&*self.value as *const dyn Any as *const ()) as usize
20
+ }
21
+
22
+ // TODO use &CStr when Magnus supports it
23
+ // https://github.com/matsadler/magnus/pull/182
24
+ pub fn name(&self) -> Option<&str> {
25
+ self.name.as_deref().map(|v| v.to_str().unwrap())
26
+ }
27
+ }
@@ -1,3 +1,4 @@
1
+ pub mod capsule;
1
2
  pub mod exceptions;
2
3
  pub mod gvl;
3
4
  pub mod lazy;
@@ -40,14 +40,13 @@ where
40
40
  pub struct RbArray1<T>(T);
41
41
 
42
42
  impl<T: Element> RbArray1<T> {
43
- pub fn from_iter<I>(values: I) -> RbResult<Value>
43
+ pub fn from_iter<I>(rb: &Ruby, values: I) -> RbResult<Value>
44
44
  where
45
45
  I: IntoIterator<Item = T>,
46
46
  {
47
- let ruby = Ruby::get().unwrap();
48
- ruby.class_object()
47
+ rb.class_object()
49
48
  .const_get::<_, RModule>("Numo")?
50
49
  .const_get::<_, RClass>(T::class_name())?
51
- .funcall("cast", (ruby.ary_from_iter(values),))
50
+ .funcall("cast", (rb.ary_from_iter(values),))
52
51
  }
53
52
  }
@@ -1,12 +1,10 @@
1
1
  use magnus::{Module, RClass, Ruby, value::Lazy};
2
2
 
3
- static BIGDECIMAL: Lazy<RClass> =
4
- Lazy::new(|rb| rb.class_object().const_get("BigDecimal").unwrap());
5
3
  static DATE: Lazy<RClass> = Lazy::new(|rb| rb.class_object().const_get("Date").unwrap());
6
4
  static DATETIME: Lazy<RClass> = Lazy::new(|rb| rb.class_object().const_get("DateTime").unwrap());
7
5
 
8
- pub(crate) fn bigdecimal(rb: &Ruby) -> RClass {
9
- rb.get_inner(&BIGDECIMAL)
6
+ pub(crate) fn bigdecimal(rb: &Ruby) -> Option<RClass> {
7
+ rb.class_object().const_get("BigDecimal").ok()
10
8
  }
11
9
 
12
10
  pub(crate) fn date(rb: &Ruby) -> RClass {
@@ -82,15 +82,13 @@ impl AnonymousColumnsUdf for RubyUdfExpression {
82
82
 
83
83
  fn deep_clone(self: Arc<Self>) -> Arc<dyn AnonymousColumnsUdf> {
84
84
  Arc::new(Self {
85
- ruby_function: ArcValue::new(
86
- Ruby::attach(|rb| {
87
- // TODO fix
88
- rb.get_inner(*self.ruby_function.0)
89
- .funcall::<_, _, Value>("dup", ())
90
- .map(Opaque::from)
91
- })
92
- .unwrap(),
93
- ),
85
+ ruby_function: ArcValue::new(Ruby::attach(|rb| {
86
+ // TODO fix
87
+ rb.get_inner(*self.ruby_function.0)
88
+ .funcall::<_, _, Value>("dup", ())
89
+ .map(Opaque::from)
90
+ .unwrap()
91
+ })),
94
92
  output_type: self.output_type.clone(),
95
93
  materialized_field: OnceLock::new(),
96
94
  is_elementwise: self.is_elementwise,
@@ -1,8 +1,9 @@
1
1
  use std::sync::Arc;
2
2
 
3
- use magnus::{Error, Ruby, Value, gc, value::Opaque};
3
+ use magnus::{Error, IntoValue, Ruby, Value, gc, value::Opaque};
4
4
  use polars::error::PolarsError;
5
5
 
6
+ use crate::RbResult;
6
7
  use crate::ruby::gvl::GvlExt;
7
8
 
8
9
  pub(crate) fn to_pl_err(e: Error) -> PolarsError {
@@ -26,3 +27,13 @@ impl Drop for ArcValue {
26
27
  Ruby::attach(|_| gc::unregister_address(&*self.0));
27
28
  }
28
29
  }
30
+
31
+ pub trait TryIntoValue {
32
+ fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value>;
33
+ }
34
+
35
+ impl<T: IntoValue> TryIntoValue for T {
36
+ fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
37
+ Ok(self.into_value_with(ruby))
38
+ }
39
+ }
@@ -3,11 +3,12 @@ use magnus::{IntoValue, Ruby, Value};
3
3
  use polars::prelude::*;
4
4
 
5
5
  use crate::prelude::*;
6
+ use crate::ruby::utils::TryIntoValue;
6
7
  use crate::utils::EnterPolarsExt;
7
8
  use crate::{RbResult, RbSeries};
8
9
 
9
10
  fn scalar_to_rb(scalar: RbResult<Scalar>, rb: &Ruby) -> RbResult<Value> {
10
- Ok(Wrap(scalar?.as_any_value()).into_value_with(rb))
11
+ Wrap(scalar?.as_any_value()).try_into_value_with(rb)
11
12
  }
12
13
 
13
14
  impl RbSeries {
@@ -35,6 +36,17 @@ impl RbSeries {
35
36
  })
36
37
  }
37
38
 
39
+ pub fn is_empty(rb: &Ruby, self_: &Self, ignore_nulls: bool) -> RbResult<bool> {
40
+ rb.enter_polars(|| {
41
+ let s = self_.series.read();
42
+ PolarsResult::Ok(if ignore_nulls {
43
+ s.is_full_null()
44
+ } else {
45
+ s.is_empty()
46
+ })
47
+ })
48
+ }
49
+
38
50
  pub fn arg_max(rb: &Ruby, self_: &Self) -> RbResult<Option<usize>> {
39
51
  rb.enter_polars_ok(|| self_.series.read().arg_max())
40
52
  }
@@ -1,50 +1,51 @@
1
1
  use magnus::{IntoValue, Ruby, Value, value::ReprValue};
2
2
  use polars_core::prelude::*;
3
3
 
4
+ use crate::interop::arrow::to_rb::series_to_stream;
4
5
  use crate::prelude::*;
6
+ use crate::ruby::utils::TryIntoValue;
5
7
  use crate::{RbPolarsErr, RbResult, RbSeries};
6
8
 
7
9
  impl RbSeries {
8
10
  /// Convert this Series to a Ruby array.
9
11
  /// This operation copies data.
10
- pub fn to_a(&self) -> RbResult<Value> {
11
- let series = &self.series.read();
12
+ pub fn to_a(ruby: &Ruby, self_: &Self) -> RbResult<Value> {
13
+ let series = &self_.series.read();
12
14
 
13
- fn to_a_recursive(series: &Series) -> RbResult<Value> {
14
- let ruby = Ruby::get().unwrap();
15
+ fn to_a_recursive(ruby: &Ruby, series: &Series) -> RbResult<Value> {
15
16
  let rblist = match series.dtype() {
16
17
  DataType::Boolean => ruby
17
- .ary_from_iter(series.bool().map_err(RbPolarsErr::from)?)
18
+ .ary_from_iter(series.bool().map_err(RbPolarsErr::from)?.iter())
18
19
  .as_value(),
19
20
  DataType::UInt8 => ruby
20
- .ary_from_iter(series.u8().map_err(RbPolarsErr::from)?)
21
+ .ary_from_iter(series.u8().map_err(RbPolarsErr::from)?.iter())
21
22
  .as_value(),
22
23
  DataType::UInt16 => ruby
23
- .ary_from_iter(series.u16().map_err(RbPolarsErr::from)?)
24
+ .ary_from_iter(series.u16().map_err(RbPolarsErr::from)?.iter())
24
25
  .as_value(),
25
26
  DataType::UInt32 => ruby
26
- .ary_from_iter(series.u32().map_err(RbPolarsErr::from)?)
27
+ .ary_from_iter(series.u32().map_err(RbPolarsErr::from)?.iter())
27
28
  .as_value(),
28
29
  DataType::UInt64 => ruby
29
- .ary_from_iter(series.u64().map_err(RbPolarsErr::from)?)
30
+ .ary_from_iter(series.u64().map_err(RbPolarsErr::from)?.iter())
30
31
  .as_value(),
31
32
  DataType::UInt128 => ruby
32
- .ary_from_iter(series.u128().map_err(RbPolarsErr::from)?)
33
+ .ary_from_iter(series.u128().map_err(RbPolarsErr::from)?.iter())
33
34
  .as_value(),
34
35
  DataType::Int8 => ruby
35
- .ary_from_iter(series.i8().map_err(RbPolarsErr::from)?)
36
+ .ary_from_iter(series.i8().map_err(RbPolarsErr::from)?.iter())
36
37
  .as_value(),
37
38
  DataType::Int16 => ruby
38
- .ary_from_iter(series.i16().map_err(RbPolarsErr::from)?)
39
+ .ary_from_iter(series.i16().map_err(RbPolarsErr::from)?.iter())
39
40
  .as_value(),
40
41
  DataType::Int32 => ruby
41
- .ary_from_iter(series.i32().map_err(RbPolarsErr::from)?)
42
+ .ary_from_iter(series.i32().map_err(RbPolarsErr::from)?.iter())
42
43
  .as_value(),
43
44
  DataType::Int64 => ruby
44
- .ary_from_iter(series.i64().map_err(RbPolarsErr::from)?)
45
+ .ary_from_iter(series.i64().map_err(RbPolarsErr::from)?.iter())
45
46
  .as_value(),
46
47
  DataType::Int128 => ruby
47
- .ary_from_iter(series.i128().map_err(RbPolarsErr::from)?)
48
+ .ary_from_iter(series.i128().map_err(RbPolarsErr::from)?.iter())
48
49
  .as_value(),
49
50
  DataType::Float16 => ruby
50
51
  .ary_from_iter(
@@ -53,14 +54,15 @@ impl RbSeries {
53
54
  .cast(&DataType::Float32)
54
55
  .map_err(RbPolarsErr::from)?
55
56
  .f32()
56
- .map_err(RbPolarsErr::from)?,
57
+ .map_err(RbPolarsErr::from)?
58
+ .iter(),
57
59
  )
58
60
  .as_value(),
59
61
  DataType::Float32 => ruby
60
- .ary_from_iter(series.f32().map_err(RbPolarsErr::from)?)
62
+ .ary_from_iter(series.f32().map_err(RbPolarsErr::from)?.iter())
61
63
  .as_value(),
62
64
  DataType::Float64 => ruby
63
- .ary_from_iter(series.f64().map_err(RbPolarsErr::from)?)
65
+ .ary_from_iter(series.f64().map_err(RbPolarsErr::from)?.iter())
64
66
  .as_value(),
65
67
  DataType::Categorical(_, _) | DataType::Enum(_, _) => {
66
68
  with_match_categorical_physical_type!(series.dtype().cat_physical().unwrap(), |$C| {
@@ -71,10 +73,7 @@ impl RbSeries {
71
73
  let v = ruby.ary_new_capa(series.len());
72
74
  for i in 0..series.len() {
73
75
  let obj: Option<&ObjectValue> = series.get_object(i).map(|any| any.into());
74
- match obj {
75
- Some(val) => v.push(val.to_value()).unwrap(),
76
- None => v.push(ruby.qnil()).unwrap(),
77
- };
76
+ v.push(obj.cloned().into_value_with(ruby))?;
78
77
  }
79
78
  v.as_value()
80
79
  }
@@ -87,7 +86,7 @@ impl RbSeries {
87
86
  v.push(ruby.qnil()).unwrap();
88
87
  }
89
88
  Some(s) => {
90
- let rblst = to_a_recursive(s.as_ref())?;
89
+ let rblst = to_a_recursive(ruby, s.as_ref())?;
91
90
  v.push(rblst)?;
92
91
  }
93
92
  }
@@ -103,7 +102,7 @@ impl RbSeries {
103
102
  v.push(ruby.qnil()).unwrap();
104
103
  }
105
104
  Some(s) => {
106
- let rblst = to_a_recursive(s.as_ref())?;
105
+ let rblst = to_a_recursive(ruby, s.as_ref())?;
107
106
  v.push(rblst)?;
108
107
  }
109
108
  }
@@ -112,35 +111,35 @@ impl RbSeries {
112
111
  }
113
112
  DataType::Date => {
114
113
  let ca = series.date().map_err(RbPolarsErr::from)?;
115
- return Ok(Wrap(ca).into_value_with(&ruby));
114
+ return Wrap(ca).try_into_value_with(ruby);
116
115
  }
117
116
  DataType::Time => {
118
117
  let ca = series.time().map_err(RbPolarsErr::from)?;
119
- return Ok(Wrap(ca).into_value_with(&ruby));
118
+ return Wrap(ca).try_into_value_with(ruby);
120
119
  }
121
120
  DataType::Datetime(_, _) => {
122
121
  let ca = series.datetime().map_err(RbPolarsErr::from)?;
123
- return Ok(Wrap(ca).into_value_with(&ruby));
122
+ return Wrap(ca).try_into_value_with(ruby);
124
123
  }
125
124
  DataType::Decimal(_, _) => {
126
125
  let ca = series.decimal().map_err(RbPolarsErr::from)?;
127
- return Ok(Wrap(ca).into_value_with(&ruby));
126
+ return Wrap(ca).try_into_value_with(ruby);
128
127
  }
129
128
  DataType::String => {
130
129
  let ca = series.str().map_err(RbPolarsErr::from)?;
131
- return Ok(Wrap(ca).into_value_with(&ruby));
130
+ return Wrap(ca).try_into_value_with(ruby);
132
131
  }
133
132
  DataType::Struct(_) => {
134
133
  let ca = series.struct_().map_err(RbPolarsErr::from)?;
135
- return Ok(Wrap(ca).into_value_with(&ruby));
134
+ return Wrap(ca).try_into_value_with(ruby);
136
135
  }
137
136
  DataType::Duration(_) => {
138
137
  let ca = series.duration().map_err(RbPolarsErr::from)?;
139
- return Ok(Wrap(ca).into_value_with(&ruby));
138
+ return Wrap(ca).try_into_value_with(ruby);
140
139
  }
141
140
  DataType::Binary => {
142
141
  let ca = series.binary().map_err(RbPolarsErr::from)?;
143
- return Ok(Wrap(ca).into_value_with(&ruby));
142
+ return Wrap(ca).try_into_value_with(ruby);
144
143
  }
145
144
  DataType::Null => {
146
145
  let null: Option<u8> = None;
@@ -163,10 +162,7 @@ impl RbSeries {
163
162
  }
164
163
  impl ExactSizeIterator for NullIter {}
165
164
 
166
- Ruby::get()
167
- .unwrap()
168
- .ary_from_iter(NullIter { iter, n })
169
- .as_value()
165
+ ruby.ary_from_iter(NullIter { iter, n }).as_value()
170
166
  }
171
167
  DataType::Unknown(_) => {
172
168
  panic!("to_a not implemented for unknown")
@@ -175,12 +171,16 @@ impl RbSeries {
175
171
  unreachable!()
176
172
  }
177
173
  DataType::Extension(_, _) => {
178
- return to_a_recursive(series.ext().unwrap().storage());
174
+ return to_a_recursive(ruby, series.ext().unwrap().storage());
179
175
  }
180
176
  };
181
177
  Ok(rblist.as_value())
182
178
  }
183
179
 
184
- to_a_recursive(series)
180
+ to_a_recursive(ruby, series)
181
+ }
182
+
183
+ pub fn __arrow_c_stream__(ruby: &Ruby, self_: &Self) -> RbResult<Value> {
184
+ series_to_stream(&self_.series.read(), ruby)
185
185
  }
186
186
  }
@@ -10,6 +10,7 @@ use crate::ruby::exceptions::{RbIndexError, RbRuntimeError, RbValueError};
10
10
  use crate::ruby::gvl::GvlExt;
11
11
  use crate::ruby::plan_callback::PlanCallbackExt;
12
12
  use crate::ruby::ruby_function::RubyObject;
13
+ use crate::ruby::utils::TryIntoValue;
13
14
  use crate::utils::EnterPolarsExt;
14
15
  use crate::{RbDataFrame, RbErr, RbPolarsErr, RbResult, RbSeries};
15
16
 
@@ -112,7 +113,7 @@ impl RbSeries {
112
113
  let rbseries = RbSeries::new(s);
113
114
  rb_modules::pl_utils(ruby).funcall("wrap_s", (rbseries,))
114
115
  }
115
- _ => Ok(Wrap(av).into_value_with(ruby)),
116
+ _ => Wrap(av).try_into_value_with(ruby),
116
117
  }
117
118
  }
118
119
 
@@ -156,8 +157,8 @@ impl RbSeries {
156
157
  self.series.write().rename(name.into());
157
158
  }
158
159
 
159
- pub fn dtype(rb: &Ruby, self_: &Self) -> Value {
160
- Wrap(self_.series.read().dtype().clone()).into_value_with(rb)
160
+ pub fn dtype(rb: &Ruby, self_: &Self) -> RbResult<Value> {
161
+ Wrap(self_.series.read().dtype().clone()).try_into_value_with(rb)
161
162
  }
162
163
 
163
164
  pub fn set_sorted_flag(&self, descending: bool) -> Self {
@@ -4,6 +4,7 @@ use super::RbSeries;
4
4
  use crate::map::series::ApplyLambdaGeneric;
5
5
  use crate::prelude::*;
6
6
  use crate::ruby::gvl::GvlExt;
7
+ use crate::ruby::utils::TryIntoValue;
7
8
  use crate::series::construction::series_from_objects;
8
9
  use crate::{RbPolarsErr, RbResult};
9
10
  use crate::{apply_all_polars_dtypes, raise_err};
@@ -78,7 +79,7 @@ fn call_and_collect_objects<T, I>(
78
79
  skip_nulls: bool,
79
80
  ) -> RbResult<Series>
80
81
  where
81
- T: IntoValue,
82
+ T: TryIntoValue,
82
83
  I: Iterator<Item = Option<T>>,
83
84
  {
84
85
  let mut objects = Vec::with_capacity(len);
@@ -91,7 +92,7 @@ where
91
92
  continue;
92
93
  }
93
94
  None => rb.qnil().into_value_with(rb),
94
- Some(val) => val.into_value_with(rb),
95
+ Some(val) => val.try_into_value_with(rb)?,
95
96
  };
96
97
  let out: Value = lambda.funcall("call", (arg,))?;
97
98
  objects.push(ObjectValue {
@@ -139,23 +139,23 @@ fn scatter_impl(
139
139
  with_match_physical_numeric_polars_type!(dt, |$T| {
140
140
  let ca: &mut ChunkedArray<$T> = mutable_s.as_mut();
141
141
  let values: &ChunkedArray<$T> = values.as_ref().as_ref();
142
- ca.scatter(idx, values)
142
+ ca.scatter(idx, values.iter())
143
143
  })
144
144
  }
145
145
  DataType::Boolean => {
146
146
  let ca: &mut ChunkedArray<BooleanType> = mutable_s.as_mut();
147
147
  let values = values.bool()?;
148
- ca.scatter(idx, values)
148
+ ca.scatter(idx, values.iter())
149
149
  }
150
150
  DataType::Binary => {
151
151
  let ca: &mut ChunkedArray<BinaryType> = mutable_s.as_mut();
152
152
  let values = values.binary()?;
153
- ca.scatter(idx, values)
153
+ ca.scatter(idx, values.iter())
154
154
  }
155
155
  DataType::String => {
156
156
  let ca: &mut ChunkedArray<StringType> = mutable_s.as_mut();
157
157
  let values = values.str()?;
158
- ca.scatter(idx, values)
158
+ ca.scatter(idx, values.iter())
159
159
  }
160
160
  _ => Err(PolarsError::ComputeError(
161
161
  format!("not yet implemented for dtype: {logical_dtype}").into(),
@@ -16,7 +16,6 @@ macro_rules! apply_all_polars_dtypes {
16
16
  ($self:expr, $method:ident, $($args:expr),*) => {
17
17
  match $self.dtype() {
18
18
  DataType::Boolean => $self.bool().unwrap().$method($($args),*),
19
- DataType::String => $self.str().unwrap().$method($($args),*),
20
19
  DataType::UInt8 => $self.u8().unwrap().$method($($args),*),
21
20
  DataType::UInt16 => $self.u16().unwrap().$method($($args),*),
22
21
  DataType::UInt32 => $self.u32().unwrap().$method($($args),*),
@@ -27,15 +26,40 @@ macro_rules! apply_all_polars_dtypes {
27
26
  DataType::Int32 => $self.i32().unwrap().$method($($args),*),
28
27
  DataType::Int64 => $self.i64().unwrap().$method($($args),*),
29
28
  DataType::Int128 => $self.i128().unwrap().$method($($args),*),
30
- DataType::Float16 => todo!(),
29
+ DataType::Float16 => $self.cast(&DataType::Float32).unwrap().f32().unwrap().$method($($args),*),
31
30
  DataType::Float32 => $self.f32().unwrap().$method($($args),*),
32
31
  DataType::Float64 => $self.f64().unwrap().$method($($args),*),
33
- DataType::Date => $self.date().unwrap().physical().$method($($args),*),
34
- DataType::Datetime(_, _) => $self.datetime().unwrap().physical().$method($($args),*),
35
- // TODO implement
36
- // DataType::List(_) => $self.list().unwrap().$method($($args),*),
32
+ DataType::String => $self.str().unwrap().$method($($args),*),
33
+ DataType::Binary => $self.binary().unwrap().$method($($args),*),
34
+ DataType::Decimal(_, _) => $self.decimal().unwrap().$method($($args),*),
35
+
36
+ DataType::Date => $self.date().unwrap().$method($($args),*),
37
+ DataType::Datetime(_, _) => $self.datetime().unwrap().$method($($args),*),
38
+ DataType::Duration(_) => $self.duration().unwrap().$method($($args),*),
39
+ DataType::Time => $self.time().unwrap().$method($($args),*),
40
+
41
+ DataType::List(_) => $self.list().unwrap().$method($($args),*),
37
42
  DataType::Struct(_) => $self.struct_().unwrap().$method($($args),*),
38
- dt => panic!("dtype {:?} not supported", dt)
43
+ DataType::Array(_, _) => $self.array().unwrap().$method($($args),*),
44
+
45
+ dt @ (DataType::Categorical(_, _) | DataType::Enum(_, _)) => match dt.cat_physical().unwrap() {
46
+ CategoricalPhysical::U8 => $self.cat8().unwrap().$method($($args),*),
47
+ CategoricalPhysical::U16 => $self.cat16().unwrap().$method($($args),*),
48
+ CategoricalPhysical::U32 => $self.cat32().unwrap().$method($($args),*),
49
+ },
50
+
51
+ DataType::Object(_) => {
52
+ $self
53
+ .as_any()
54
+ .downcast_ref::<ObjectChunked<ObjectValue>>()
55
+ .unwrap()
56
+ .$method($($args),*)
57
+ },
58
+ DataType::Extension(_, _) => $self.ext().unwrap().$method($($args),*),
59
+
60
+ DataType::Null => $self.null().unwrap().$method($($args),*),
61
+
62
+ dt @ (DataType::BinaryOffset | DataType::Unknown(_)) => panic!("dtype {:?} not supported", dt)
39
63
  }
40
64
  }
41
65
  }
@@ -377,7 +377,9 @@ module Polars
377
377
  # # │ [1, 2] │
378
378
  # # └───────────┘
379
379
  def unique(maintain_order: false)
380
- Utils.wrap_expr(_rbexpr.arr_unique(maintain_order))
380
+ eval(
381
+ F.element.unique(maintain_order: maintain_order), as_list: true
382
+ )
381
383
  end
382
384
 
383
385
  # Count the number of unique values in every sub-arrays.
@@ -403,7 +405,7 @@ module Polars
403
405
  # # │ [2, 3, 4] ┆ 3 │
404
406
  # # └───────────────┴──────────┘
405
407
  def n_unique
406
- Utils.wrap_expr(_rbexpr.arr_n_unique)
408
+ agg(F.element.n_unique)
407
409
  end
408
410
 
409
411
  # Convert an Array column into a List column with the same inner data type.
@@ -432,6 +434,13 @@ module Polars
432
434
 
433
435
  # Evaluate whether any boolean value is true for every subarray.
434
436
  #
437
+ # @param ignore_nulls [Boolean]
438
+ # * If set to `true` (default), null values are ignored. If there
439
+ # are no non-null values, the output is `false`.
440
+ # * If set to `false`, [Kleene logic](https://en.wikipedia.org/wiki/Three-valued_logic) is used to deal with nulls:
441
+ # if the column contains any null values and no `true` values,
442
+ # the output is null.
443
+ #
435
444
  # @return [Expr]
436
445
  #
437
446
  # @example
@@ -461,12 +470,19 @@ module Polars
461
470
  # # │ [null, null] ┆ false │
462
471
  # # │ null ┆ null │
463
472
  # # └────────────────┴───────┘
464
- def any
465
- Utils.wrap_expr(_rbexpr.arr_any)
473
+ def any(ignore_nulls: true)
474
+ agg(F.element.any(ignore_nulls: ignore_nulls))
466
475
  end
467
476
 
468
477
  # Evaluate whether all boolean values are true for every subarray.
469
478
  #
479
+ # @param ignore_nulls [Boolean]
480
+ # * If set to `true` (default), null values are ignored. If there
481
+ # are no non-null values, the output is `true`.
482
+ # * If set to `false`, [Kleene logic](https://en.wikipedia.org/wiki/Three-valued_logic) is used to deal with nulls:
483
+ # if the column contains any null values and no `false` values,
484
+ # the output is null.
485
+ #
470
486
  # @return [Expr]
471
487
  #
472
488
  # @example
@@ -496,8 +512,8 @@ module Polars
496
512
  # # │ [null, null] ┆ true │
497
513
  # # │ null ┆ null │
498
514
  # # └────────────────┴───────┘
499
- def all
500
- Utils.wrap_expr(_rbexpr.arr_all)
515
+ def all(ignore_nulls: true)
516
+ agg(F.element.all(ignore_nulls: ignore_nulls))
501
517
  end
502
518
 
503
519
  # Sort the arrays in this column.
@@ -567,7 +583,7 @@ module Polars
567
583
  # # │ [9, 1, 2] ┆ [2, 1, 9] │
568
584
  # # └───────────────┴───────────────┘
569
585
  def reverse
570
- Utils.wrap_expr(_rbexpr.arr_reverse)
586
+ eval(F.element.reverse)
571
587
  end
572
588
 
573
589
  # Retrieve the index of the minimal value in every sub-array.
@@ -212,6 +212,13 @@ module Polars
212
212
 
213
213
  # Evaluate whether any boolean value is true for every subarray.
214
214
  #
215
+ # @param ignore_nulls [Boolean]
216
+ # * If set to `true` (default), null values are ignored. If there
217
+ # are no non-null values, the output is `false`.
218
+ # * If set to `false`, [Kleene logic](https://en.wikipedia.org/wiki/Three-valued_logic) is used to deal with nulls:
219
+ # if the column contains any null values and no `true` values,
220
+ # the output is null.
221
+ #
215
222
  # @return [Series]
216
223
  #
217
224
  # @example
@@ -230,7 +237,7 @@ module Polars
230
237
  # # false
231
238
  # # null
232
239
  # # ]
233
- def any
240
+ def any(ignore_nulls: true)
234
241
  super
235
242
  end
236
243
 
@@ -378,6 +385,13 @@ module Polars
378
385
 
379
386
  # Evaluate whether all boolean values are true for every subarray.
380
387
  #
388
+ # @param ignore_nulls [Boolean]
389
+ # * If set to `true` (default), null values are ignored. If there
390
+ # are no non-null values, the output is `true`.
391
+ # * If set to `false`, [Kleene logic](https://en.wikipedia.org/wiki/Three-valued_logic) is used to deal with nulls:
392
+ # if the column contains any null values and no `false` values,
393
+ # the output is null.
394
+ #
381
395
  # @return [Series]
382
396
  #
383
397
  # @example
@@ -396,7 +410,7 @@ module Polars
396
410
  # # true
397
411
  # # null
398
412
  # # ]
399
- def all
413
+ def all(ignore_nulls: true)
400
414
  super
401
415
  end
402
416