polars-df 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Cargo.lock +90 -48
  4. data/README.md +6 -6
  5. data/ext/polars/Cargo.toml +7 -5
  6. data/ext/polars/src/batched_csv.rs +53 -52
  7. data/ext/polars/src/conversion/mod.rs +13 -60
  8. data/ext/polars/src/dataframe/construction.rs +186 -0
  9. data/ext/polars/src/dataframe/export.rs +48 -0
  10. data/ext/polars/src/dataframe/general.rs +607 -0
  11. data/ext/polars/src/dataframe/io.rs +463 -0
  12. data/ext/polars/src/dataframe/mod.rs +26 -0
  13. data/ext/polars/src/expr/datetime.rs +6 -2
  14. data/ext/polars/src/expr/general.rs +28 -6
  15. data/ext/polars/src/expr/rolling.rs +185 -69
  16. data/ext/polars/src/expr/string.rs +9 -30
  17. data/ext/polars/src/functions/lazy.rs +2 -0
  18. data/ext/polars/src/functions/range.rs +74 -0
  19. data/ext/polars/src/interop/mod.rs +1 -0
  20. data/ext/polars/src/interop/numo/mod.rs +2 -0
  21. data/ext/polars/src/interop/numo/to_numo_df.rs +23 -0
  22. data/ext/polars/src/interop/numo/to_numo_series.rs +60 -0
  23. data/ext/polars/src/lazyframe/mod.rs +54 -38
  24. data/ext/polars/src/lib.rs +46 -21
  25. data/ext/polars/src/map/lazy.rs +5 -25
  26. data/ext/polars/src/map/series.rs +7 -1
  27. data/ext/polars/src/series/aggregation.rs +47 -30
  28. data/ext/polars/src/series/export.rs +131 -49
  29. data/ext/polars/src/series/mod.rs +1 -131
  30. data/lib/polars/batched_csv_reader.rb +9 -3
  31. data/lib/polars/convert.rb +6 -1
  32. data/lib/polars/data_frame.rb +83 -302
  33. data/lib/polars/date_time_expr.rb +1 -0
  34. data/lib/polars/date_time_name_space.rb +5 -1
  35. data/lib/polars/dynamic_group_by.rb +2 -2
  36. data/lib/polars/exceptions.rb +4 -0
  37. data/lib/polars/expr.rb +1134 -20
  38. data/lib/polars/functions/range/date_range.rb +92 -0
  39. data/lib/polars/functions/range/datetime_range.rb +149 -0
  40. data/lib/polars/functions/range/time_range.rb +141 -0
  41. data/lib/polars/group_by.rb +88 -23
  42. data/lib/polars/io/avro.rb +24 -0
  43. data/lib/polars/{io.rb → io/csv.rb} +296 -490
  44. data/lib/polars/io/database.rb +73 -0
  45. data/lib/polars/io/ipc.rb +247 -0
  46. data/lib/polars/io/json.rb +18 -0
  47. data/lib/polars/io/ndjson.rb +69 -0
  48. data/lib/polars/io/parquet.rb +226 -0
  49. data/lib/polars/lazy_frame.rb +23 -166
  50. data/lib/polars/lazy_group_by.rb +100 -3
  51. data/lib/polars/rolling_group_by.rb +2 -2
  52. data/lib/polars/series.rb +2 -2
  53. data/lib/polars/string_expr.rb +37 -36
  54. data/lib/polars/utils.rb +35 -1
  55. data/lib/polars/version.rb +1 -1
  56. data/lib/polars.rb +9 -1
  57. metadata +21 -5
  58. data/ext/polars/src/dataframe.rs +0 -1208
@@ -7,187 +7,281 @@ use crate::RbExpr;
7
7
  impl RbExpr {
8
8
  pub fn rolling_sum(
9
9
  &self,
10
- window_size: String,
10
+ window_size: usize,
11
11
  weights: Option<Vec<f64>>,
12
12
  min_periods: usize,
13
13
  center: bool,
14
- by: Option<String>,
15
- closed: Option<Wrap<ClosedWindow>>,
16
14
  ) -> Self {
17
- let options = RollingOptions {
18
- window_size: Duration::parse(&window_size),
15
+ let options = RollingOptionsFixedWindow {
16
+ window_size,
19
17
  weights,
20
18
  min_periods,
21
19
  center,
22
- by,
23
- closed_window: closed.map(|c| c.0),
24
20
  ..Default::default()
25
21
  };
26
22
  self.inner.clone().rolling_sum(options).into()
27
23
  }
28
24
 
29
- pub fn rolling_min(
25
+ pub fn rolling_sum_by(
30
26
  &self,
27
+ by: &RbExpr,
31
28
  window_size: String,
29
+ min_periods: usize,
30
+ closed: Wrap<ClosedWindow>,
31
+ ) -> Self {
32
+ let options = RollingOptionsDynamicWindow {
33
+ window_size: Duration::parse(&window_size),
34
+ min_periods,
35
+ closed_window: closed.0,
36
+ fn_params: None,
37
+ };
38
+ self.inner
39
+ .clone()
40
+ .rolling_sum_by(by.inner.clone(), options)
41
+ .into()
42
+ }
43
+
44
+ pub fn rolling_min(
45
+ &self,
46
+ window_size: usize,
32
47
  weights: Option<Vec<f64>>,
33
48
  min_periods: usize,
34
49
  center: bool,
35
- by: Option<String>,
36
- closed: Option<Wrap<ClosedWindow>>,
37
50
  ) -> Self {
38
- let options = RollingOptions {
39
- window_size: Duration::parse(&window_size),
51
+ let options = RollingOptionsFixedWindow {
52
+ window_size,
40
53
  weights,
41
54
  min_periods,
42
55
  center,
43
- by,
44
- closed_window: closed.map(|c| c.0),
45
56
  ..Default::default()
46
57
  };
47
58
  self.inner.clone().rolling_min(options).into()
48
59
  }
49
60
 
50
- pub fn rolling_max(
61
+ pub fn rolling_min_by(
51
62
  &self,
63
+ by: &RbExpr,
52
64
  window_size: String,
65
+ min_periods: usize,
66
+ closed: Wrap<ClosedWindow>,
67
+ ) -> Self {
68
+ let options = RollingOptionsDynamicWindow {
69
+ window_size: Duration::parse(&window_size),
70
+ min_periods,
71
+ closed_window: closed.0,
72
+ fn_params: None,
73
+ };
74
+ self.inner
75
+ .clone()
76
+ .rolling_min_by(by.inner.clone(), options)
77
+ .into()
78
+ }
79
+
80
+ pub fn rolling_max(
81
+ &self,
82
+ window_size: usize,
53
83
  weights: Option<Vec<f64>>,
54
84
  min_periods: usize,
55
85
  center: bool,
56
- by: Option<String>,
57
- closed: Option<Wrap<ClosedWindow>>,
58
86
  ) -> Self {
59
- let options = RollingOptions {
60
- window_size: Duration::parse(&window_size),
87
+ let options = RollingOptionsFixedWindow {
88
+ window_size: window_size,
61
89
  weights,
62
90
  min_periods,
63
91
  center,
64
- by,
65
- closed_window: closed.map(|c| c.0),
66
92
  ..Default::default()
67
93
  };
68
94
  self.inner.clone().rolling_max(options).into()
69
95
  }
70
96
 
71
- pub fn rolling_mean(
97
+ pub fn rolling_max_by(
72
98
  &self,
99
+ by: &RbExpr,
73
100
  window_size: String,
101
+ min_periods: usize,
102
+ closed: Wrap<ClosedWindow>,
103
+ ) -> Self {
104
+ let options = RollingOptionsDynamicWindow {
105
+ window_size: Duration::parse(&window_size),
106
+ min_periods,
107
+ closed_window: closed.0,
108
+ fn_params: None,
109
+ };
110
+ self.inner
111
+ .clone()
112
+ .rolling_max_by(by.inner.clone(), options)
113
+ .into()
114
+ }
115
+
116
+ pub fn rolling_mean(
117
+ &self,
118
+ window_size: usize,
74
119
  weights: Option<Vec<f64>>,
75
120
  min_periods: usize,
76
121
  center: bool,
77
- by: Option<String>,
78
- closed: Option<Wrap<ClosedWindow>>,
79
122
  ) -> Self {
80
- let options = RollingOptions {
81
- window_size: Duration::parse(&window_size),
123
+ let options = RollingOptionsFixedWindow {
124
+ window_size,
82
125
  weights,
83
126
  min_periods,
84
127
  center,
85
- by,
86
- closed_window: closed.map(|c| c.0),
87
128
  ..Default::default()
88
129
  };
89
130
 
90
131
  self.inner.clone().rolling_mean(options).into()
91
132
  }
92
133
 
93
- #[allow(clippy::too_many_arguments)]
94
- pub fn rolling_std(
134
+ pub fn rolling_mean_by(
95
135
  &self,
136
+ by: &RbExpr,
96
137
  window_size: String,
138
+ min_periods: usize,
139
+ closed: Wrap<ClosedWindow>,
140
+ ) -> Self {
141
+ let options = RollingOptionsDynamicWindow {
142
+ window_size: Duration::parse(&window_size),
143
+ min_periods,
144
+ closed_window: closed.0,
145
+ fn_params: None,
146
+ };
147
+
148
+ self.inner
149
+ .clone()
150
+ .rolling_mean_by(by.inner.clone(), options)
151
+ .into()
152
+ }
153
+
154
+ pub fn rolling_std(
155
+ &self,
156
+ window_size: usize,
97
157
  weights: Option<Vec<f64>>,
98
158
  min_periods: usize,
99
159
  center: bool,
100
- by: Option<String>,
101
- closed: Option<Wrap<ClosedWindow>>,
102
160
  ddof: u8,
103
- warn_if_unsorted: bool,
104
161
  ) -> Self {
105
- let options = RollingOptions {
106
- window_size: Duration::parse(&window_size),
162
+ let options = RollingOptionsFixedWindow {
163
+ window_size,
107
164
  weights,
108
165
  min_periods,
109
166
  center,
110
- by,
111
- closed_window: closed.map(|c| c.0),
112
167
  fn_params: Some(Arc::new(RollingVarParams { ddof }) as Arc<dyn Any + Send + Sync>),
113
- warn_if_unsorted,
114
168
  };
115
169
 
116
170
  self.inner.clone().rolling_std(options).into()
117
171
  }
118
172
 
119
- #[allow(clippy::too_many_arguments)]
120
- pub fn rolling_var(
173
+ pub fn rolling_std_by(
121
174
  &self,
175
+ by: &RbExpr,
122
176
  window_size: String,
177
+ min_periods: usize,
178
+ closed: Wrap<ClosedWindow>,
179
+ ddof: u8,
180
+ ) -> Self {
181
+ let options = RollingOptionsDynamicWindow {
182
+ window_size: Duration::parse(&window_size),
183
+ min_periods,
184
+ closed_window: closed.0,
185
+ fn_params: Some(Arc::new(RollingVarParams { ddof }) as Arc<dyn Any + Send + Sync>),
186
+ };
187
+
188
+ self.inner
189
+ .clone()
190
+ .rolling_std_by(by.inner.clone(), options)
191
+ .into()
192
+ }
193
+
194
+ pub fn rolling_var(
195
+ &self,
196
+ window_size: usize,
123
197
  weights: Option<Vec<f64>>,
124
198
  min_periods: usize,
125
199
  center: bool,
126
- by: Option<String>,
127
- closed: Option<Wrap<ClosedWindow>>,
128
200
  ddof: u8,
129
- warn_if_unsorted: bool,
130
201
  ) -> Self {
131
- let options = RollingOptions {
132
- window_size: Duration::parse(&window_size),
202
+ let options = RollingOptionsFixedWindow {
203
+ window_size,
133
204
  weights,
134
205
  min_periods,
135
206
  center,
136
- by,
137
- closed_window: closed.map(|c| c.0),
138
207
  fn_params: Some(Arc::new(RollingVarParams { ddof }) as Arc<dyn Any + Send + Sync>),
139
- warn_if_unsorted,
140
208
  };
141
209
 
142
210
  self.inner.clone().rolling_var(options).into()
143
211
  }
144
212
 
145
- #[allow(clippy::too_many_arguments)]
146
- pub fn rolling_median(
213
+ pub fn rolling_var_by(
147
214
  &self,
215
+ by: &RbExpr,
148
216
  window_size: String,
217
+ min_periods: usize,
218
+ closed: Wrap<ClosedWindow>,
219
+ ddof: u8,
220
+ ) -> Self {
221
+ let options = RollingOptionsDynamicWindow {
222
+ window_size: Duration::parse(&window_size),
223
+ min_periods,
224
+ closed_window: closed.0,
225
+ fn_params: Some(Arc::new(RollingVarParams { ddof }) as Arc<dyn Any + Send + Sync>),
226
+ };
227
+
228
+ self.inner
229
+ .clone()
230
+ .rolling_var_by(by.inner.clone(), options)
231
+ .into()
232
+ }
233
+
234
+ pub fn rolling_median(
235
+ &self,
236
+ window_size: usize,
149
237
  weights: Option<Vec<f64>>,
150
238
  min_periods: usize,
151
239
  center: bool,
152
- by: Option<String>,
153
- closed: Option<Wrap<ClosedWindow>>,
154
- warn_if_unsorted: bool,
155
240
  ) -> Self {
156
- let options = RollingOptions {
157
- window_size: Duration::parse(&window_size),
158
- weights,
241
+ let options = RollingOptionsFixedWindow {
242
+ window_size,
159
243
  min_periods,
244
+ weights,
160
245
  center,
161
- by,
162
- closed_window: closed.map(|c| c.0),
163
246
  fn_params: None,
164
- warn_if_unsorted,
165
247
  };
166
248
  self.inner.clone().rolling_median(options).into()
167
249
  }
168
250
 
169
- #[allow(clippy::too_many_arguments)]
251
+ pub fn rolling_median_by(
252
+ &self,
253
+ by: &RbExpr,
254
+ window_size: String,
255
+ min_periods: usize,
256
+ closed: Wrap<ClosedWindow>,
257
+ ) -> Self {
258
+ let options = RollingOptionsDynamicWindow {
259
+ window_size: Duration::parse(&window_size),
260
+ min_periods,
261
+ closed_window: closed.0,
262
+ fn_params: None,
263
+ };
264
+ self.inner
265
+ .clone()
266
+ .rolling_median_by(by.inner.clone(), options)
267
+ .into()
268
+ }
269
+
170
270
  pub fn rolling_quantile(
171
271
  &self,
172
272
  quantile: f64,
173
273
  interpolation: Wrap<QuantileInterpolOptions>,
174
- window_size: String,
274
+ window_size: usize,
175
275
  weights: Option<Vec<f64>>,
176
276
  min_periods: usize,
177
277
  center: bool,
178
- by: Option<String>,
179
- closed: Option<Wrap<ClosedWindow>>,
180
- warn_if_unsorted: bool,
181
278
  ) -> Self {
182
- let options = RollingOptions {
183
- window_size: Duration::parse(&window_size),
279
+ let options = RollingOptionsFixedWindow {
280
+ window_size,
184
281
  weights,
185
282
  min_periods,
186
283
  center,
187
- by,
188
- closed_window: closed.map(|c| c.0),
189
284
  fn_params: None,
190
- warn_if_unsorted,
191
285
  };
192
286
 
193
287
  self.inner
@@ -196,6 +290,28 @@ impl RbExpr {
196
290
  .into()
197
291
  }
198
292
 
293
+ pub fn rolling_quantile_by(
294
+ &self,
295
+ by: &RbExpr,
296
+ quantile: f64,
297
+ interpolation: Wrap<QuantileInterpolOptions>,
298
+ window_size: String,
299
+ min_periods: usize,
300
+ closed: Wrap<ClosedWindow>,
301
+ ) -> Self {
302
+ let options = RollingOptionsDynamicWindow {
303
+ window_size: Duration::parse(&window_size),
304
+ min_periods,
305
+ closed_window: closed.0,
306
+ fn_params: None,
307
+ };
308
+
309
+ self.inner
310
+ .clone()
311
+ .rolling_quantile_by(by.inner.clone(), interpolation.0, quantile, options)
312
+ .into()
313
+ }
314
+
199
315
  pub fn rolling_skew(&self, window_size: usize, bias: bool) -> Self {
200
316
  self.inner.clone().rolling_skew(window_size, bias).into()
201
317
  }
@@ -259,39 +259,18 @@ impl RbExpr {
259
259
  infer_schema_len: Option<usize>,
260
260
  ) -> Self {
261
261
  let dtype = dtype.map(|wrap| wrap.0);
262
-
263
- let output_type = match dtype.clone() {
264
- Some(dtype) => GetOutput::from_type(dtype),
265
- None => GetOutput::from_type(DataType::Unknown),
266
- };
267
-
268
- let function = move |s: Series| {
269
- let ca = s.str()?;
270
- match ca.json_decode(dtype.clone(), infer_schema_len) {
271
- Ok(ca) => Ok(Some(ca.into_series())),
272
- Err(e) => Err(PolarsError::ComputeError(format!("{e:?}").into())),
273
- }
274
- };
275
-
276
- self.clone()
277
- .inner
278
- .map(function, output_type)
279
- .with_fmt("str.json_decode")
262
+ self.inner
263
+ .clone()
264
+ .str()
265
+ .json_decode(dtype, infer_schema_len)
280
266
  .into()
281
267
  }
282
268
 
283
- pub fn str_json_path_match(&self, pat: String) -> Self {
284
- let function = move |s: Series| {
285
- let ca = s.str()?;
286
- match ca.json_path_match(&pat) {
287
- Ok(ca) => Ok(Some(ca.into_series())),
288
- Err(e) => Err(PolarsError::ComputeError(format!("{:?}", e).into())),
289
- }
290
- };
291
- self.clone()
292
- .inner
293
- .map(function, GetOutput::from_type(DataType::String))
294
- .with_fmt("str.json_path_match")
269
+ pub fn str_json_path_match(&self, pat: &Self) -> Self {
270
+ self.inner
271
+ .clone()
272
+ .str()
273
+ .json_path_match(pat.inner.clone())
295
274
  .into()
296
275
  }
297
276
 
@@ -130,6 +130,7 @@ pub fn concat_lf(
130
130
  rechunk,
131
131
  parallel,
132
132
  to_supertypes,
133
+ ..Default::default()
133
134
  },
134
135
  )
135
136
  .map_err(RbPolarsErr::from)?;
@@ -198,6 +199,7 @@ pub fn concat_lf_diagonal(
198
199
  rechunk,
199
200
  parallel,
200
201
  to_supertypes,
202
+ ..Default::default()
201
203
  },
202
204
  )
203
205
  .map_err(RbPolarsErr::from)?;
@@ -39,3 +39,77 @@ pub fn date_range(
39
39
  let time_unit = time_unit.map(|x| x.0);
40
40
  dsl::date_range(start, end, every, closed, time_unit, time_zone).into()
41
41
  }
42
+
43
+ pub fn date_ranges(
44
+ start: &RbExpr,
45
+ end: &RbExpr,
46
+ every: String,
47
+ closed: Wrap<ClosedWindow>,
48
+ time_unit: Option<Wrap<TimeUnit>>,
49
+ time_zone: Option<TimeZone>,
50
+ ) -> RbExpr {
51
+ let start = start.inner.clone();
52
+ let end = end.inner.clone();
53
+ let every = Duration::parse(&every);
54
+ let closed = closed.0;
55
+ let time_unit = time_unit.map(|x| x.0);
56
+ dsl::date_ranges(start, end, every, closed, time_unit, time_zone).into()
57
+ }
58
+
59
+ pub fn datetime_range(
60
+ start: &RbExpr,
61
+ end: &RbExpr,
62
+ every: String,
63
+ closed: Wrap<ClosedWindow>,
64
+ time_unit: Option<Wrap<TimeUnit>>,
65
+ time_zone: Option<TimeZone>,
66
+ ) -> RbExpr {
67
+ let start = start.inner.clone();
68
+ let end = end.inner.clone();
69
+ let every = Duration::parse(&every);
70
+ let closed = closed.0;
71
+ let time_unit = time_unit.map(|x| x.0);
72
+ dsl::datetime_range(start, end, every, closed, time_unit, time_zone).into()
73
+ }
74
+
75
+ pub fn datetime_ranges(
76
+ start: &RbExpr,
77
+ end: &RbExpr,
78
+ every: String,
79
+ closed: Wrap<ClosedWindow>,
80
+ time_unit: Option<Wrap<TimeUnit>>,
81
+ time_zone: Option<TimeZone>,
82
+ ) -> RbExpr {
83
+ let start = start.inner.clone();
84
+ let end = end.inner.clone();
85
+ let every = Duration::parse(&every);
86
+ let closed = closed.0;
87
+ let time_unit = time_unit.map(|x| x.0);
88
+ dsl::datetime_ranges(start, end, every, closed, time_unit, time_zone).into()
89
+ }
90
+
91
+ pub fn time_range(
92
+ start: &RbExpr,
93
+ end: &RbExpr,
94
+ every: String,
95
+ closed: Wrap<ClosedWindow>,
96
+ ) -> RbExpr {
97
+ let start = start.inner.clone();
98
+ let end = end.inner.clone();
99
+ let every = Duration::parse(&every);
100
+ let closed = closed.0;
101
+ dsl::time_range(start, end, every, closed).into()
102
+ }
103
+
104
+ pub fn time_ranges(
105
+ start: &RbExpr,
106
+ end: &RbExpr,
107
+ every: String,
108
+ closed: Wrap<ClosedWindow>,
109
+ ) -> RbExpr {
110
+ let start = start.inner.clone();
111
+ let end = end.inner.clone();
112
+ let every = Duration::parse(&every);
113
+ let closed = closed.0;
114
+ dsl::time_ranges(start, end, every, closed).into()
115
+ }
@@ -0,0 +1 @@
1
+ pub mod numo;
@@ -0,0 +1,2 @@
1
+ pub mod to_numo_df;
2
+ pub mod to_numo_series;
@@ -0,0 +1,23 @@
1
+ use magnus::Value;
2
+ use polars_core::utils::try_get_supertype;
3
+
4
+ use crate::dataframe::RbDataFrame;
5
+
6
+ impl RbDataFrame {
7
+ pub fn to_numo(&self) -> Option<Value> {
8
+ let mut st = None;
9
+ for s in self.df.borrow().iter() {
10
+ let dt_i = s.dtype();
11
+ match st {
12
+ None => st = Some(dt_i.clone()),
13
+ Some(ref mut st) => {
14
+ *st = try_get_supertype(st, dt_i).ok()?;
15
+ }
16
+ }
17
+ }
18
+ let _st = st?;
19
+
20
+ // TODO
21
+ None
22
+ }
23
+ }
@@ -0,0 +1,60 @@
1
+ use magnus::{class, prelude::*, Module, RArray, RClass, RModule, Value};
2
+ use polars_core::prelude::*;
3
+
4
+ use crate::error::RbPolarsErr;
5
+ use crate::raise_err;
6
+ use crate::series::RbSeries;
7
+ use crate::RbResult;
8
+
9
+ impl RbSeries {
10
+ /// For numeric types, this should only be called for Series with null types.
11
+ /// This will cast to floats so that `nil = NAN`
12
+ pub fn to_numo(&self) -> RbResult<Value> {
13
+ let s = &self.series.borrow();
14
+ match s.dtype() {
15
+ DataType::String => {
16
+ let ca = s.str().unwrap();
17
+
18
+ // TODO make more efficient
19
+ let np_arr = RArray::from_iter(ca);
20
+ class::object()
21
+ .const_get::<_, RModule>("Numo")?
22
+ .const_get::<_, RClass>("RObject")?
23
+ .funcall("cast", (np_arr,))
24
+ }
25
+ dt if dt.is_numeric() => {
26
+ if s.bit_repr_is_large() {
27
+ let s = s.cast(&DataType::Float64).unwrap();
28
+ let ca = s.f64().unwrap();
29
+ // TODO make more efficient
30
+ let np_arr = RArray::from_iter(ca.into_iter().map(|opt_v| match opt_v {
31
+ Some(v) => v,
32
+ None => f64::NAN,
33
+ }));
34
+ class::object()
35
+ .const_get::<_, RModule>("Numo")?
36
+ .const_get::<_, RClass>("DFloat")?
37
+ .funcall("cast", (np_arr,))
38
+ } else {
39
+ let s = s.cast(&DataType::Float32).unwrap();
40
+ let ca = s.f32().unwrap();
41
+ // TODO make more efficient
42
+ let np_arr = RArray::from_iter(ca.into_iter().map(|opt_v| match opt_v {
43
+ Some(v) => v,
44
+ None => f32::NAN,
45
+ }));
46
+ class::object()
47
+ .const_get::<_, RModule>("Numo")?
48
+ .const_get::<_, RClass>("SFloat")?
49
+ .funcall("cast", (np_arr,))
50
+ }
51
+ }
52
+ dt => {
53
+ raise_err!(
54
+ format!("'to_numo' not supported for dtype: {dt:?}"),
55
+ ComputeError
56
+ );
57
+ }
58
+ }
59
+ }
60
+ }