polars-df 0.10.0 → 0.11.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 (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
+ }