polars-df 0.8.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -1
  3. data/Cargo.lock +159 -66
  4. data/Cargo.toml +0 -3
  5. data/LICENSE.txt +1 -1
  6. data/README.md +3 -2
  7. data/ext/polars/Cargo.toml +18 -8
  8. data/ext/polars/src/batched_csv.rs +7 -5
  9. data/ext/polars/src/conversion/anyvalue.rs +186 -0
  10. data/ext/polars/src/conversion/chunked_array.rs +140 -0
  11. data/ext/polars/src/{conversion.rs → conversion/mod.rs} +273 -342
  12. data/ext/polars/src/dataframe.rs +108 -66
  13. data/ext/polars/src/expr/array.rs +78 -0
  14. data/ext/polars/src/expr/datetime.rs +29 -58
  15. data/ext/polars/src/expr/general.rs +83 -36
  16. data/ext/polars/src/expr/list.rs +58 -6
  17. data/ext/polars/src/expr/meta.rs +48 -0
  18. data/ext/polars/src/expr/rolling.rs +1 -0
  19. data/ext/polars/src/expr/string.rs +62 -11
  20. data/ext/polars/src/expr/struct.rs +8 -4
  21. data/ext/polars/src/file.rs +158 -11
  22. data/ext/polars/src/functions/aggregation.rs +6 -0
  23. data/ext/polars/src/functions/lazy.rs +120 -50
  24. data/ext/polars/src/functions/meta.rs +45 -1
  25. data/ext/polars/src/functions/string_cache.rs +14 -0
  26. data/ext/polars/src/functions/whenthen.rs +47 -17
  27. data/ext/polars/src/{lazyframe.rs → lazyframe/mod.rs} +195 -40
  28. data/ext/polars/src/lib.rs +246 -179
  29. data/ext/polars/src/map/dataframe.rs +17 -9
  30. data/ext/polars/src/series/aggregation.rs +20 -0
  31. data/ext/polars/src/series/mod.rs +35 -4
  32. data/lib/polars/array_expr.rb +453 -0
  33. data/lib/polars/array_name_space.rb +346 -0
  34. data/lib/polars/batched_csv_reader.rb +4 -2
  35. data/lib/polars/cat_expr.rb +24 -0
  36. data/lib/polars/cat_name_space.rb +75 -0
  37. data/lib/polars/config.rb +2 -2
  38. data/lib/polars/data_frame.rb +306 -96
  39. data/lib/polars/data_types.rb +191 -28
  40. data/lib/polars/date_time_expr.rb +41 -18
  41. data/lib/polars/date_time_name_space.rb +9 -3
  42. data/lib/polars/exceptions.rb +12 -1
  43. data/lib/polars/expr.rb +898 -215
  44. data/lib/polars/functions/aggregation/horizontal.rb +246 -0
  45. data/lib/polars/functions/aggregation/vertical.rb +282 -0
  46. data/lib/polars/functions/as_datatype.rb +248 -0
  47. data/lib/polars/functions/col.rb +47 -0
  48. data/lib/polars/functions/eager.rb +182 -0
  49. data/lib/polars/functions/lazy.rb +1280 -0
  50. data/lib/polars/functions/len.rb +49 -0
  51. data/lib/polars/functions/lit.rb +35 -0
  52. data/lib/polars/functions/random.rb +16 -0
  53. data/lib/polars/functions/range/date_range.rb +103 -0
  54. data/lib/polars/functions/range/int_range.rb +51 -0
  55. data/lib/polars/functions/repeat.rb +144 -0
  56. data/lib/polars/functions/whenthen.rb +96 -0
  57. data/lib/polars/functions.rb +29 -416
  58. data/lib/polars/group_by.rb +2 -2
  59. data/lib/polars/io.rb +36 -31
  60. data/lib/polars/lazy_frame.rb +405 -88
  61. data/lib/polars/list_expr.rb +158 -8
  62. data/lib/polars/list_name_space.rb +102 -0
  63. data/lib/polars/meta_expr.rb +175 -7
  64. data/lib/polars/series.rb +282 -41
  65. data/lib/polars/string_cache.rb +75 -0
  66. data/lib/polars/string_expr.rb +413 -96
  67. data/lib/polars/string_name_space.rb +4 -4
  68. data/lib/polars/testing.rb +507 -0
  69. data/lib/polars/utils.rb +106 -8
  70. data/lib/polars/version.rb +1 -1
  71. data/lib/polars/whenthen.rb +83 -0
  72. data/lib/polars.rb +16 -4
  73. metadata +37 -8
  74. data/lib/polars/lazy_functions.rb +0 -1181
  75. data/lib/polars/when.rb +0 -16
  76. data/lib/polars/when_then.rb +0 -19
@@ -1,20 +1,167 @@
1
- use magnus::{exception, prelude::*, Error, RString, Value};
2
- use polars::io::mmap::MmapBytesReader;
3
1
  use std::fs::File;
4
- use std::io::Cursor;
2
+ use std::io;
3
+ use std::io::{BufReader, Cursor, Read, Seek, SeekFrom, Write};
5
4
  use std::path::PathBuf;
6
5
 
6
+ use magnus::{exception, prelude::*, Error, RString, Value};
7
+ use polars::io::mmap::MmapBytesReader;
8
+
9
+ use crate::error::RbPolarsErr;
10
+ use crate::prelude::resolve_homedir;
7
11
  use crate::RbResult;
8
12
 
9
- pub fn get_file_like(f: Value, truncate: bool) -> RbResult<File> {
10
- let str_slice = PathBuf::try_convert(f)?;
11
- let f = if truncate {
12
- File::create(str_slice)
13
- .map_err(|e| Error::new(exception::runtime_error(), e.to_string()))?
13
+ #[derive(Clone)]
14
+ pub struct RbFileLikeObject {
15
+ inner: Value,
16
+ }
17
+
18
+ /// Wraps a `Value`, and implements read, seek, and write for it.
19
+ impl RbFileLikeObject {
20
+ /// Creates an instance of a `RbFileLikeObject` from a `Value`.
21
+ /// To assert the object has the required methods methods,
22
+ /// instantiate it with `RbFileLikeObject::require`
23
+ pub fn new(object: Value) -> Self {
24
+ RbFileLikeObject { inner: object }
25
+ }
26
+
27
+ pub fn as_buffer(&self) -> std::io::Cursor<Vec<u8>> {
28
+ let data = self.as_file_buffer().into_inner();
29
+ std::io::Cursor::new(data)
30
+ }
31
+
32
+ pub fn as_file_buffer(&self) -> Cursor<Vec<u8>> {
33
+ let bytes = self
34
+ .inner
35
+ .funcall::<_, _, RString>("read", ())
36
+ .expect("no read method found");
37
+
38
+ let buf = unsafe { bytes.as_slice() }.to_vec();
39
+
40
+ Cursor::new(buf)
41
+ }
42
+
43
+ /// Same as `RbFileLikeObject::new`, but validates that the underlying
44
+ /// ruby object has a `read`, `write`, and `seek` methods in respect to parameters.
45
+ /// Will return a `TypeError` if object does not have `read`, `seek`, and `write` methods.
46
+ pub fn with_requirements(object: Value, read: bool, write: bool, seek: bool) -> RbResult<Self> {
47
+ if read && !object.respond_to("read", false)? {
48
+ return Err(Error::new(
49
+ exception::type_error(),
50
+ "Object does not have a .read() method.",
51
+ ));
52
+ }
53
+
54
+ if seek && !object.respond_to("seek", false)? {
55
+ return Err(Error::new(
56
+ exception::type_error(),
57
+ "Object does not have a .seek() method.",
58
+ ));
59
+ }
60
+
61
+ if write && !object.respond_to("write", false)? {
62
+ return Err(Error::new(
63
+ exception::type_error(),
64
+ "Object does not have a .write() method.",
65
+ ));
66
+ }
67
+
68
+ Ok(RbFileLikeObject::new(object))
69
+ }
70
+ }
71
+
72
+ /// Extracts a string repr from, and returns an IO error to send back to rust.
73
+ fn rberr_to_io_err(e: Error) -> io::Error {
74
+ io::Error::new(io::ErrorKind::Other, e.to_string())
75
+ }
76
+
77
+ impl Read for RbFileLikeObject {
78
+ fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, io::Error> {
79
+ let bytes = self
80
+ .inner
81
+ .funcall::<_, _, RString>("read", (buf.len(),))
82
+ .map_err(rberr_to_io_err)?;
83
+
84
+ buf.write_all(unsafe { bytes.as_slice() })?;
85
+
86
+ Ok(bytes.len())
87
+ }
88
+ }
89
+
90
+ impl Write for RbFileLikeObject {
91
+ fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
92
+ let rbbytes = RString::from_slice(buf);
93
+
94
+ let number_bytes_written = self
95
+ .inner
96
+ .funcall::<_, _, usize>("write", (rbbytes,))
97
+ .map_err(rberr_to_io_err)?;
98
+
99
+ Ok(number_bytes_written)
100
+ }
101
+
102
+ fn flush(&mut self) -> Result<(), io::Error> {
103
+ self.inner
104
+ .funcall::<_, _, Value>("flush", ())
105
+ .map_err(rberr_to_io_err)?;
106
+
107
+ Ok(())
108
+ }
109
+ }
110
+
111
+ impl Seek for RbFileLikeObject {
112
+ fn seek(&mut self, pos: SeekFrom) -> Result<u64, io::Error> {
113
+ let (whence, offset) = match pos {
114
+ SeekFrom::Start(i) => (0, i as i64),
115
+ SeekFrom::Current(i) => (1, i),
116
+ SeekFrom::End(i) => (2, i),
117
+ };
118
+
119
+ let new_position = self
120
+ .inner
121
+ .funcall("seek", (offset, whence))
122
+ .map_err(rberr_to_io_err)?;
123
+
124
+ Ok(new_position)
125
+ }
126
+ }
127
+
128
+ pub trait FileLike: Read + Write + Seek {}
129
+
130
+ impl FileLike for File {}
131
+ impl FileLike for RbFileLikeObject {}
132
+
133
+ pub enum EitherRustRubyFile {
134
+ Rb(RbFileLikeObject),
135
+ Rust(BufReader<File>),
136
+ }
137
+
138
+ ///
139
+ /// # Arguments
140
+ /// * `truncate` - open or create a new file.
141
+ pub fn get_either_file(rb_f: Value, truncate: bool) -> RbResult<EitherRustRubyFile> {
142
+ if let Ok(rstring) = RString::try_convert(rb_f) {
143
+ let s = unsafe { rstring.as_str() }?;
144
+ let file_path = std::path::Path::new(&s);
145
+ let file_path = resolve_homedir(file_path);
146
+ let f = if truncate {
147
+ File::create(file_path).map_err(RbPolarsErr::io)?
148
+ } else {
149
+ polars_utils::open_file(&file_path).map_err(RbPolarsErr::from)?
150
+ };
151
+ let reader = BufReader::new(f);
152
+ Ok(EitherRustRubyFile::Rust(reader))
14
153
  } else {
15
- File::open(str_slice).map_err(|e| Error::new(exception::runtime_error(), e.to_string()))?
16
- };
17
- Ok(f)
154
+ let f = RbFileLikeObject::with_requirements(rb_f, !truncate, truncate, !truncate)?;
155
+ Ok(EitherRustRubyFile::Rb(f))
156
+ }
157
+ }
158
+
159
+ pub fn get_file_like(f: Value, truncate: bool) -> RbResult<Box<dyn FileLike>> {
160
+ use EitherRustRubyFile::*;
161
+ match get_either_file(f, truncate)? {
162
+ Rb(f) => Ok(Box::new(f)),
163
+ Rust(f) => Ok(Box::new(f.into_inner())),
164
+ }
18
165
  }
19
166
 
20
167
  pub fn get_mmap_bytes_reader(rb_f: Value) -> RbResult<Box<dyn MmapBytesReader>> {
@@ -33,3 +33,9 @@ pub fn sum_horizontal(exprs: RArray) -> RbResult<RbExpr> {
33
33
  let e = dsl::sum_horizontal(exprs).map_err(RbPolarsErr::from)?;
34
34
  Ok(e.into())
35
35
  }
36
+
37
+ pub fn mean_horizontal(exprs: RArray) -> RbResult<RbExpr> {
38
+ let exprs = rb_exprs_to_exprs(exprs)?;
39
+ let e = dsl::mean_horizontal(exprs).map_err(RbPolarsErr::from)?;
40
+ Ok(e.into())
41
+ }
@@ -17,9 +17,62 @@ macro_rules! set_unwrapped_or_0 {
17
17
  };
18
18
  }
19
19
 
20
- pub fn arg_sort_by(by: RArray, descending: Vec<bool>) -> RbResult<RbExpr> {
20
+ pub fn rolling_corr(
21
+ x: &RbExpr,
22
+ y: &RbExpr,
23
+ window_size: IdxSize,
24
+ min_periods: IdxSize,
25
+ ddof: u8,
26
+ ) -> RbExpr {
27
+ dsl::rolling_corr(
28
+ x.inner.clone(),
29
+ y.inner.clone(),
30
+ RollingCovOptions {
31
+ min_periods,
32
+ window_size,
33
+ ddof,
34
+ },
35
+ )
36
+ .into()
37
+ }
38
+
39
+ pub fn rolling_cov(
40
+ x: &RbExpr,
41
+ y: &RbExpr,
42
+ window_size: IdxSize,
43
+ min_periods: IdxSize,
44
+ ddof: u8,
45
+ ) -> RbExpr {
46
+ dsl::rolling_cov(
47
+ x.inner.clone(),
48
+ y.inner.clone(),
49
+ RollingCovOptions {
50
+ min_periods,
51
+ window_size,
52
+ ddof,
53
+ },
54
+ )
55
+ .into()
56
+ }
57
+
58
+ pub fn arg_sort_by(
59
+ by: RArray,
60
+ descending: Vec<bool>,
61
+ nulls_last: bool,
62
+ multithreaded: bool,
63
+ maintain_order: bool,
64
+ ) -> RbResult<RbExpr> {
21
65
  let by = rb_exprs_to_exprs(by)?;
22
- Ok(dsl::arg_sort_by(by, &descending).into())
66
+ Ok(dsl::arg_sort_by(
67
+ by,
68
+ SortMultipleOptions {
69
+ descending,
70
+ nulls_last,
71
+ multithreaded,
72
+ maintain_order,
73
+ },
74
+ )
75
+ .into())
23
76
  }
24
77
 
25
78
  pub fn arg_where(condition: &RbExpr) -> RbExpr {
@@ -83,6 +136,47 @@ pub fn concat_lf(
83
136
  Ok(lf.into())
84
137
  }
85
138
 
139
+ pub fn concat_list(s: RArray) -> RbResult<RbExpr> {
140
+ let s = rb_exprs_to_exprs(s)?;
141
+ let expr = dsl::concat_list(s).map_err(RbPolarsErr::from)?;
142
+ Ok(expr.into())
143
+ }
144
+
145
+ pub fn concat_str(s: RArray, separator: String, ignore_nulls: bool) -> RbResult<RbExpr> {
146
+ let s = rb_exprs_to_exprs(s)?;
147
+ Ok(dsl::concat_str(s, &separator, ignore_nulls).into())
148
+ }
149
+
150
+ pub fn len() -> RbExpr {
151
+ dsl::len().into()
152
+ }
153
+
154
+ pub fn cov(a: &RbExpr, b: &RbExpr, ddof: u8) -> RbExpr {
155
+ polars::lazy::dsl::cov(a.inner.clone(), b.inner.clone(), ddof).into()
156
+ }
157
+
158
+ pub fn arctan2(y: &RbExpr, x: &RbExpr) -> RbExpr {
159
+ y.inner.clone().arctan2(x.inner.clone()).into()
160
+ }
161
+
162
+ pub fn arctan2d(y: &RbExpr, x: &RbExpr) -> RbExpr {
163
+ y.inner.clone().arctan2(x.inner.clone()).degrees().into()
164
+ }
165
+
166
+ pub fn cum_fold(
167
+ acc: &RbExpr,
168
+ lambda: Value,
169
+ exprs: RArray,
170
+ include_init: bool,
171
+ ) -> RbResult<RbExpr> {
172
+ let exprs = rb_exprs_to_exprs(exprs)?;
173
+ let lambda = Opaque::from(lambda);
174
+
175
+ let func =
176
+ move |a: Series, b: Series| binary_lambda(Ruby::get().unwrap().get_inner(lambda), a, b);
177
+ Ok(dsl::cum_fold_exprs(acc.inner.clone(), func, exprs, include_init).into())
178
+ }
179
+
86
180
  pub fn concat_lf_diagonal(
87
181
  lfs: RArray,
88
182
  rechunk: bool,
@@ -110,6 +204,19 @@ pub fn concat_lf_diagonal(
110
204
  Ok(lf.into())
111
205
  }
112
206
 
207
+ pub fn dtype_cols(dtypes: Vec<DataType>) -> RbExpr {
208
+ dsl::dtype_cols(dtypes).into()
209
+ }
210
+
211
+ pub fn dtype_cols2(dtypes: RArray) -> RbResult<RbExpr> {
212
+ let dtypes = dtypes
213
+ .each()
214
+ .map(|v| Wrap::<DataType>::try_convert(v?))
215
+ .collect::<RbResult<Vec<Wrap<DataType>>>>()?;
216
+ let dtypes = vec_extract_wrapped(dtypes);
217
+ Ok(crate::functions::lazy::dtype_cols(dtypes))
218
+ }
219
+
113
220
  #[allow(clippy::too_many_arguments)]
114
221
  pub fn duration(
115
222
  weeks: Option<&RbExpr>,
@@ -146,38 +253,21 @@ pub fn duration(
146
253
  dsl::duration(args).into()
147
254
  }
148
255
 
149
- pub fn count() -> RbExpr {
150
- dsl::count().into()
151
- }
152
-
153
256
  pub fn first() -> RbExpr {
154
257
  dsl::first().into()
155
258
  }
156
259
 
157
- pub fn last() -> RbExpr {
158
- dsl::last().into()
159
- }
160
-
161
- pub fn dtype_cols(dtypes: Vec<DataType>) -> RbExpr {
162
- dsl::dtype_cols(dtypes).into()
163
- }
164
-
165
260
  pub fn fold(acc: &RbExpr, lambda: Value, exprs: RArray) -> RbResult<RbExpr> {
166
261
  let exprs = rb_exprs_to_exprs(exprs)?;
167
262
  let lambda = Opaque::from(lambda);
168
263
 
169
264
  let func =
170
265
  move |a: Series, b: Series| binary_lambda(Ruby::get().unwrap().get_inner(lambda), a, b);
171
- Ok(polars::lazy::dsl::fold_exprs(acc.inner.clone(), func, exprs).into())
266
+ Ok(dsl::fold_exprs(acc.inner.clone(), func, exprs).into())
172
267
  }
173
268
 
174
- pub fn cumfold(acc: &RbExpr, lambda: Value, exprs: RArray, include_init: bool) -> RbResult<RbExpr> {
175
- let exprs = rb_exprs_to_exprs(exprs)?;
176
- let lambda = Opaque::from(lambda);
177
-
178
- let func =
179
- move |a: Series, b: Series| binary_lambda(Ruby::get().unwrap().get_inner(lambda), a, b);
180
- Ok(polars::lazy::dsl::cum_fold_exprs(acc.inner.clone(), func, exprs, include_init).into())
269
+ pub fn last() -> RbExpr {
270
+ dsl::last().into()
181
271
  }
182
272
 
183
273
  pub fn lit(value: Value, allow_object: bool) -> RbResult<RbExpr> {
@@ -219,6 +309,10 @@ pub fn lit(value: Value, allow_object: bool) -> RbResult<RbExpr> {
219
309
  }
220
310
  }
221
311
 
312
+ pub fn pearson_corr(a: &RbExpr, b: &RbExpr, ddof: u8) -> RbExpr {
313
+ dsl::pearson_corr(a.inner.clone(), b.inner.clone(), ddof).into()
314
+ }
315
+
222
316
  pub fn repeat(value: &RbExpr, n: &RbExpr, dtype: Option<Wrap<DataType>>) -> RbResult<RbExpr> {
223
317
  let mut value = value.inner.clone();
224
318
  let n = n.inner.clone();
@@ -228,7 +322,7 @@ pub fn repeat(value: &RbExpr, n: &RbExpr, dtype: Option<Wrap<DataType>>) -> RbRe
228
322
  }
229
323
 
230
324
  if let Expr::Literal(lv) = &value {
231
- let av = lv.to_anyvalue().unwrap();
325
+ let av = lv.to_any_value().unwrap();
232
326
  // Integer inputs that fit in Int32 are parsed as such
233
327
  if let DataType::Int64 = av.dtype() {
234
328
  let int_value = av.try_extract::<i64>().unwrap();
@@ -240,35 +334,11 @@ pub fn repeat(value: &RbExpr, n: &RbExpr, dtype: Option<Wrap<DataType>>) -> RbRe
240
334
  Ok(dsl::repeat(value, n).into())
241
335
  }
242
336
 
243
- pub fn pearson_corr(a: &RbExpr, b: &RbExpr, ddof: u8) -> RbExpr {
244
- polars::lazy::dsl::pearson_corr(a.inner.clone(), b.inner.clone(), ddof).into()
245
- }
246
-
247
337
  pub fn spearman_rank_corr(a: &RbExpr, b: &RbExpr, ddof: u8, propagate_nans: bool) -> RbExpr {
248
- polars::lazy::dsl::spearman_rank_corr(a.inner.clone(), b.inner.clone(), ddof, propagate_nans)
249
- .into()
250
- }
251
-
252
- pub fn cov(a: &RbExpr, b: &RbExpr, ddof: u8) -> RbExpr {
253
- polars::lazy::dsl::cov(a.inner.clone(), b.inner.clone(), ddof).into()
254
- }
255
-
256
- pub fn concat_str(s: RArray, sep: String) -> RbResult<RbExpr> {
257
- let s = rb_exprs_to_exprs(s)?;
258
- Ok(dsl::concat_str(s, &sep).into())
338
+ dsl::spearman_rank_corr(a.inner.clone(), b.inner.clone(), ddof, propagate_nans).into()
259
339
  }
260
340
 
261
- pub fn concat_lst(s: RArray) -> RbResult<RbExpr> {
262
- let s = rb_exprs_to_exprs(s)?;
263
- let expr = dsl::concat_list(s).map_err(RbPolarsErr::from)?;
341
+ pub fn sql_expr(sql: String) -> RbResult<RbExpr> {
342
+ let expr = polars::sql::sql_expr(sql).map_err(RbPolarsErr::from)?;
264
343
  Ok(expr.into())
265
344
  }
266
-
267
- pub fn dtype_cols2(dtypes: RArray) -> RbResult<RbExpr> {
268
- let dtypes = dtypes
269
- .each()
270
- .map(|v| Wrap::<DataType>::try_convert(v?))
271
- .collect::<RbResult<Vec<Wrap<DataType>>>>()?;
272
- let dtypes = vec_extract_wrapped(dtypes);
273
- Ok(crate::functions::lazy::dtype_cols(dtypes))
274
- }
@@ -7,7 +7,7 @@ use polars_core::POOL;
7
7
  use crate::conversion::Wrap;
8
8
  use crate::{RbResult, RbValueError};
9
9
 
10
- pub fn get_idx_type() -> Value {
10
+ pub fn get_index_type() -> Value {
11
11
  Wrap(IDX_DTYPE).into_value()
12
12
  }
13
13
 
@@ -36,3 +36,47 @@ pub fn get_float_fmt() -> RbResult<String> {
36
36
  };
37
37
  Ok(strfmt.to_string())
38
38
  }
39
+
40
+ pub fn set_float_precision(precision: Option<usize>) -> RbResult<()> {
41
+ use polars_core::fmt::set_float_precision;
42
+ set_float_precision(precision);
43
+ Ok(())
44
+ }
45
+
46
+ pub fn get_float_precision() -> RbResult<Option<usize>> {
47
+ use polars_core::fmt::get_float_precision;
48
+ Ok(get_float_precision())
49
+ }
50
+
51
+ pub fn set_thousands_separator(sep: Option<char>) -> RbResult<()> {
52
+ use polars_core::fmt::set_thousands_separator;
53
+ set_thousands_separator(sep);
54
+ Ok(())
55
+ }
56
+
57
+ pub fn get_thousands_separator() -> RbResult<Option<String>> {
58
+ use polars_core::fmt::get_thousands_separator;
59
+ Ok(Some(get_thousands_separator()))
60
+ }
61
+
62
+ pub fn set_decimal_separator(sep: Option<char>) -> RbResult<()> {
63
+ use polars_core::fmt::set_decimal_separator;
64
+ set_decimal_separator(sep);
65
+ Ok(())
66
+ }
67
+
68
+ pub fn get_decimal_separator() -> RbResult<Option<char>> {
69
+ use polars_core::fmt::get_decimal_separator;
70
+ Ok(Some(get_decimal_separator()))
71
+ }
72
+
73
+ pub fn set_trim_decimal_zeros(trim: Option<bool>) -> RbResult<()> {
74
+ use polars_core::fmt::set_trim_decimal_zeros;
75
+ set_trim_decimal_zeros(trim);
76
+ Ok(())
77
+ }
78
+
79
+ pub fn get_trim_decimal_zeros() -> RbResult<Option<bool>> {
80
+ use polars_core::fmt::get_trim_decimal_zeros;
81
+ Ok(Some(get_trim_decimal_zeros()))
82
+ }
@@ -1,3 +1,7 @@
1
+ use crate::RbResult;
2
+ use magnus::{RArray, Ruby, Value};
3
+ use polars_core::StringCacheHolder;
4
+
1
5
  pub fn enable_string_cache() {
2
6
  polars_core::enable_string_cache()
3
7
  }
@@ -9,3 +13,13 @@ pub fn disable_string_cache() {
9
13
  pub fn using_string_cache() -> bool {
10
14
  polars_core::using_string_cache()
11
15
  }
16
+
17
+ #[magnus::wrap(class = "Polars::RbStringCacheHolder")]
18
+ pub struct RbStringCacheHolder {}
19
+
20
+ impl RbStringCacheHolder {
21
+ pub fn hold() -> RbResult<Value> {
22
+ let _hold = StringCacheHolder::hold();
23
+ Ruby::get().unwrap().yield_splat(RArray::new())
24
+ }
25
+ }
@@ -2,42 +2,72 @@ use polars::lazy::dsl;
2
2
 
3
3
  use crate::RbExpr;
4
4
 
5
+ pub fn when(condition: &RbExpr) -> RbWhen {
6
+ RbWhen {
7
+ inner: dsl::when(condition.inner.clone()),
8
+ }
9
+ }
10
+
5
11
  #[magnus::wrap(class = "Polars::RbWhen")]
6
12
  #[derive(Clone)]
7
13
  pub struct RbWhen {
8
14
  pub inner: dsl::When,
9
15
  }
10
16
 
11
- impl From<dsl::When> for RbWhen {
12
- fn from(inner: dsl::When) -> Self {
13
- RbWhen { inner }
14
- }
15
- }
16
-
17
- #[magnus::wrap(class = "Polars::RbWhenThen")]
17
+ #[magnus::wrap(class = "Polars::RbThen")]
18
18
  #[derive(Clone)]
19
19
  pub struct RbThen {
20
20
  pub inner: dsl::Then,
21
21
  }
22
22
 
23
- impl From<dsl::Then> for RbThen {
24
- fn from(inner: dsl::Then) -> Self {
25
- RbThen { inner }
26
- }
23
+ #[magnus::wrap(class = "Polars::RbChainedWhen")]
24
+ #[derive(Clone)]
25
+ pub struct RbChainedWhen {
26
+ pub inner: dsl::ChainedWhen,
27
+ }
28
+
29
+ #[magnus::wrap(class = "Polars::RbChainedThen")]
30
+ #[derive(Clone)]
31
+ pub struct RbChainedThen {
32
+ pub inner: dsl::ChainedThen,
27
33
  }
28
34
 
29
35
  impl RbWhen {
30
- pub fn then(&self, expr: &RbExpr) -> RbThen {
31
- self.inner.clone().then(expr.inner.clone()).into()
36
+ pub fn then(&self, statement: &RbExpr) -> RbThen {
37
+ RbThen {
38
+ inner: self.inner.clone().then(statement.inner.clone()),
39
+ }
32
40
  }
33
41
  }
34
42
 
35
43
  impl RbThen {
36
- pub fn overwise(&self, expr: &RbExpr) -> RbExpr {
37
- self.inner.clone().otherwise(expr.inner.clone()).into()
44
+ pub fn when(&self, condition: &RbExpr) -> RbChainedWhen {
45
+ RbChainedWhen {
46
+ inner: self.inner.clone().when(condition.inner.clone()),
47
+ }
48
+ }
49
+
50
+ pub fn otherwise(&self, statement: &RbExpr) -> RbExpr {
51
+ self.inner.clone().otherwise(statement.inner.clone()).into()
38
52
  }
39
53
  }
40
54
 
41
- pub fn when(predicate: &RbExpr) -> RbWhen {
42
- dsl::when(predicate.inner.clone()).into()
55
+ impl RbChainedWhen {
56
+ pub fn then(&self, statement: &RbExpr) -> RbChainedThen {
57
+ RbChainedThen {
58
+ inner: self.inner.clone().then(statement.inner.clone()),
59
+ }
60
+ }
61
+ }
62
+
63
+ impl RbChainedThen {
64
+ pub fn when(&self, condition: &RbExpr) -> RbChainedWhen {
65
+ RbChainedWhen {
66
+ inner: self.inner.clone().when(condition.inner.clone()),
67
+ }
68
+ }
69
+
70
+ pub fn otherwise(&self, statement: &RbExpr) -> RbExpr {
71
+ self.inner.clone().otherwise(statement.inner.clone()).into()
72
+ }
43
73
  }