polars-df 0.4.0 → 0.6.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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/Cargo.lock +447 -410
  4. data/Cargo.toml +0 -1
  5. data/README.md +6 -5
  6. data/ext/polars/Cargo.toml +10 -5
  7. data/ext/polars/src/apply/dataframe.rs +2 -2
  8. data/ext/polars/src/{lazy/apply.rs → apply/lazy.rs} +1 -2
  9. data/ext/polars/src/apply/mod.rs +8 -3
  10. data/ext/polars/src/batched_csv.rs +7 -5
  11. data/ext/polars/src/conversion.rs +269 -59
  12. data/ext/polars/src/dataframe.rs +38 -40
  13. data/ext/polars/src/error.rs +6 -2
  14. data/ext/polars/src/expr/array.rs +15 -0
  15. data/ext/polars/src/expr/binary.rs +69 -0
  16. data/ext/polars/src/expr/categorical.rs +10 -0
  17. data/ext/polars/src/expr/datetime.rs +223 -0
  18. data/ext/polars/src/expr/general.rs +963 -0
  19. data/ext/polars/src/expr/list.rs +151 -0
  20. data/ext/polars/src/{lazy → expr}/meta.rs +16 -6
  21. data/ext/polars/src/expr/string.rs +314 -0
  22. data/ext/polars/src/expr/struct.rs +15 -0
  23. data/ext/polars/src/expr.rs +34 -0
  24. data/ext/polars/src/functions/eager.rs +93 -0
  25. data/ext/polars/src/functions/io.rs +34 -0
  26. data/ext/polars/src/functions/lazy.rs +249 -0
  27. data/ext/polars/src/functions/meta.rs +8 -0
  28. data/ext/polars/src/functions/mod.rs +5 -0
  29. data/ext/polars/src/functions/whenthen.rs +43 -0
  30. data/ext/polars/src/{lazy/dataframe.rs → lazyframe.rs} +26 -35
  31. data/ext/polars/src/lazygroupby.rs +29 -0
  32. data/ext/polars/src/lib.rs +223 -316
  33. data/ext/polars/src/object.rs +1 -1
  34. data/ext/polars/src/rb_modules.rs +12 -0
  35. data/ext/polars/src/series/aggregation.rs +83 -0
  36. data/ext/polars/src/series/arithmetic.rs +88 -0
  37. data/ext/polars/src/series/comparison.rs +251 -0
  38. data/ext/polars/src/series/construction.rs +190 -0
  39. data/ext/polars/src/series.rs +151 -551
  40. data/lib/polars/array_expr.rb +84 -0
  41. data/lib/polars/array_name_space.rb +77 -0
  42. data/lib/polars/batched_csv_reader.rb +1 -1
  43. data/lib/polars/convert.rb +2 -2
  44. data/lib/polars/data_frame.rb +289 -96
  45. data/lib/polars/data_types.rb +169 -33
  46. data/lib/polars/date_time_expr.rb +142 -2
  47. data/lib/polars/date_time_name_space.rb +17 -3
  48. data/lib/polars/expr.rb +145 -78
  49. data/lib/polars/functions.rb +0 -1
  50. data/lib/polars/group_by.rb +1 -22
  51. data/lib/polars/lazy_frame.rb +84 -31
  52. data/lib/polars/lazy_functions.rb +71 -32
  53. data/lib/polars/list_expr.rb +94 -45
  54. data/lib/polars/list_name_space.rb +13 -13
  55. data/lib/polars/rolling_group_by.rb +4 -2
  56. data/lib/polars/series.rb +249 -87
  57. data/lib/polars/string_expr.rb +277 -45
  58. data/lib/polars/string_name_space.rb +137 -22
  59. data/lib/polars/struct_name_space.rb +32 -0
  60. data/lib/polars/utils.rb +138 -54
  61. data/lib/polars/version.rb +1 -1
  62. data/lib/polars.rb +5 -2
  63. metadata +29 -11
  64. data/ext/polars/src/lazy/dsl.rs +0 -1775
  65. data/ext/polars/src/lazy/mod.rs +0 -5
  66. data/ext/polars/src/lazy/utils.rs +0 -13
  67. data/ext/polars/src/list_construction.rs +0 -100
  68. /data/ext/polars/src/{numo.rs → series/export.rs} +0 -0
  69. /data/ext/polars/src/{set.rs → series/set_at_idx.rs} +0 -0
@@ -0,0 +1,249 @@
1
+ use magnus::encoding::{self, EncodingCapable};
2
+ use magnus::{class, Float, Integer, RArray, RString, Value};
3
+ use polars::lazy::dsl;
4
+ use polars::prelude::*;
5
+
6
+ use crate::apply::lazy::binary_lambda;
7
+ use crate::conversion::{get_lf, get_rbseq, Wrap};
8
+ use crate::prelude::vec_extract_wrapped;
9
+ use crate::rb_exprs_to_exprs;
10
+ use crate::{RbDataFrame, RbExpr, RbLazyFrame, RbPolarsErr, RbResult, RbSeries, RbValueError};
11
+
12
+ macro_rules! set_unwrapped_or_0 {
13
+ ($($var:ident),+ $(,)?) => {
14
+ $(let $var = $var.map(|e| e.inner.clone()).unwrap_or(polars::lazy::dsl::lit(0));)+
15
+ };
16
+ }
17
+
18
+ pub fn arange(low: &RbExpr, high: &RbExpr, step: i64) -> RbExpr {
19
+ dsl::arange(low.inner.clone(), high.inner.clone(), step).into()
20
+ }
21
+
22
+ pub fn arg_sort_by(by: RArray, descending: Vec<bool>) -> RbResult<RbExpr> {
23
+ let by = rb_exprs_to_exprs(by)?;
24
+ Ok(dsl::arg_sort_by(by, &descending).into())
25
+ }
26
+
27
+ pub fn arg_where(condition: &RbExpr) -> RbExpr {
28
+ dsl::arg_where(condition.inner.clone()).into()
29
+ }
30
+
31
+ pub fn as_struct(exprs: RArray) -> RbResult<RbExpr> {
32
+ let exprs = rb_exprs_to_exprs(exprs)?;
33
+ Ok(dsl::as_struct(&exprs).into())
34
+ }
35
+
36
+ pub fn coalesce(exprs: RArray) -> RbResult<RbExpr> {
37
+ let exprs = rb_exprs_to_exprs(exprs)?;
38
+ Ok(dsl::coalesce(&exprs).into())
39
+ }
40
+
41
+ pub fn col(name: String) -> RbExpr {
42
+ dsl::col(&name).into()
43
+ }
44
+
45
+ pub fn collect_all(lfs: RArray) -> RbResult<RArray> {
46
+ let lfs = lfs
47
+ .each()
48
+ .map(|v| v?.try_convert::<&RbLazyFrame>())
49
+ .collect::<RbResult<Vec<&RbLazyFrame>>>()?;
50
+
51
+ Ok(RArray::from_iter(lfs.iter().map(|lf| {
52
+ let df = lf.ldf.clone().collect().unwrap();
53
+ RbDataFrame::new(df)
54
+ })))
55
+ }
56
+
57
+ pub fn cols(names: Vec<String>) -> RbExpr {
58
+ dsl::cols(names).into()
59
+ }
60
+
61
+ pub fn concat_lf(
62
+ lfs: Value,
63
+ rechunk: bool,
64
+ parallel: bool,
65
+ to_supertypes: bool,
66
+ ) -> RbResult<RbLazyFrame> {
67
+ let (seq, len) = get_rbseq(lfs)?;
68
+ let mut lfs = Vec::with_capacity(len);
69
+
70
+ for res in seq.each() {
71
+ let item = res?;
72
+ let lf = get_lf(item)?;
73
+ lfs.push(lf);
74
+ }
75
+
76
+ let lf = dsl::concat(
77
+ lfs,
78
+ UnionArgs {
79
+ rechunk,
80
+ parallel,
81
+ to_supertypes,
82
+ },
83
+ )
84
+ .map_err(RbPolarsErr::from)?;
85
+ Ok(lf.into())
86
+ }
87
+
88
+ #[allow(clippy::too_many_arguments)]
89
+ pub fn duration(
90
+ days: Option<&RbExpr>,
91
+ seconds: Option<&RbExpr>,
92
+ nanoseconds: Option<&RbExpr>,
93
+ microseconds: Option<&RbExpr>,
94
+ milliseconds: Option<&RbExpr>,
95
+ minutes: Option<&RbExpr>,
96
+ hours: Option<&RbExpr>,
97
+ weeks: Option<&RbExpr>,
98
+ ) -> RbExpr {
99
+ set_unwrapped_or_0!(
100
+ days,
101
+ seconds,
102
+ nanoseconds,
103
+ microseconds,
104
+ milliseconds,
105
+ minutes,
106
+ hours,
107
+ weeks,
108
+ );
109
+ let args = DurationArgs {
110
+ days,
111
+ seconds,
112
+ nanoseconds,
113
+ microseconds,
114
+ milliseconds,
115
+ minutes,
116
+ hours,
117
+ weeks,
118
+ };
119
+ dsl::duration(args).into()
120
+ }
121
+
122
+ pub fn count() -> RbExpr {
123
+ dsl::count().into()
124
+ }
125
+
126
+ pub fn first() -> RbExpr {
127
+ dsl::first().into()
128
+ }
129
+
130
+ pub fn last() -> RbExpr {
131
+ dsl::last().into()
132
+ }
133
+
134
+ pub fn dtype_cols(dtypes: Vec<DataType>) -> RbExpr {
135
+ dsl::dtype_cols(dtypes).into()
136
+ }
137
+
138
+ pub fn fold(acc: &RbExpr, lambda: Value, exprs: RArray) -> RbResult<RbExpr> {
139
+ let exprs = rb_exprs_to_exprs(exprs)?;
140
+
141
+ let func = move |a: Series, b: Series| binary_lambda(lambda, a, b);
142
+ Ok(polars::lazy::dsl::fold_exprs(acc.inner.clone(), func, exprs).into())
143
+ }
144
+
145
+ pub fn cumfold(acc: &RbExpr, lambda: Value, exprs: RArray, include_init: bool) -> RbResult<RbExpr> {
146
+ let exprs = rb_exprs_to_exprs(exprs)?;
147
+
148
+ let func = move |a: Series, b: Series| binary_lambda(lambda, a, b);
149
+ Ok(polars::lazy::dsl::cumfold_exprs(acc.inner.clone(), func, exprs, include_init).into())
150
+ }
151
+
152
+ pub fn lit(value: Value, allow_object: bool) -> RbResult<RbExpr> {
153
+ if value.is_kind_of(class::true_class()) || value.is_kind_of(class::false_class()) {
154
+ Ok(dsl::lit(value.try_convert::<bool>()?).into())
155
+ } else if let Some(v) = Integer::from_value(value) {
156
+ match v.try_convert::<i64>() {
157
+ Ok(val) => {
158
+ if val > 0 && val < i32::MAX as i64 || val < 0 && val > i32::MIN as i64 {
159
+ Ok(dsl::lit(val as i32).into())
160
+ } else {
161
+ Ok(dsl::lit(val).into())
162
+ }
163
+ }
164
+ _ => {
165
+ let val = value.try_convert::<u64>()?;
166
+ Ok(dsl::lit(val).into())
167
+ }
168
+ }
169
+ } else if let Some(v) = Float::from_value(value) {
170
+ Ok(dsl::lit(v.try_convert::<f64>()?).into())
171
+ } else if let Some(v) = RString::from_value(value) {
172
+ if v.enc_get() == encoding::Index::utf8() {
173
+ Ok(dsl::lit(v.try_convert::<String>()?).into())
174
+ } else {
175
+ Ok(dsl::lit(unsafe { v.as_slice() }).into())
176
+ }
177
+ } else if let Ok(series) = value.try_convert::<&RbSeries>() {
178
+ Ok(dsl::lit(series.series.borrow().clone()).into())
179
+ } else if value.is_nil() {
180
+ Ok(dsl::lit(Null {}).into())
181
+ } else if allow_object {
182
+ todo!()
183
+ } else {
184
+ Err(RbValueError::new_err(format!(
185
+ "could not convert value {:?} as a Literal",
186
+ value.to_string()
187
+ )))
188
+ }
189
+ }
190
+
191
+ pub fn repeat(value: &RbExpr, n: &RbExpr, dtype: Option<Wrap<DataType>>) -> RbResult<RbExpr> {
192
+ let mut value = value.inner.clone();
193
+ let n = n.inner.clone();
194
+
195
+ if let Some(dtype) = dtype {
196
+ value = value.cast(dtype.0);
197
+ }
198
+
199
+ if let Expr::Literal(lv) = &value {
200
+ let av = lv.to_anyvalue().unwrap();
201
+ // Integer inputs that fit in Int32 are parsed as such
202
+ if let DataType::Int64 = av.dtype() {
203
+ let int_value = av.try_extract::<i64>().unwrap();
204
+ if int_value >= i32::MIN as i64 && int_value <= i32::MAX as i64 {
205
+ value = value.cast(DataType::Int32);
206
+ }
207
+ }
208
+ }
209
+ Ok(dsl::repeat(value, n).into())
210
+ }
211
+
212
+ pub fn pearson_corr(a: &RbExpr, b: &RbExpr, ddof: u8) -> RbExpr {
213
+ polars::lazy::dsl::pearson_corr(a.inner.clone(), b.inner.clone(), ddof).into()
214
+ }
215
+
216
+ pub fn spearman_rank_corr(a: &RbExpr, b: &RbExpr, ddof: u8, propagate_nans: bool) -> RbExpr {
217
+ polars::lazy::dsl::spearman_rank_corr(a.inner.clone(), b.inner.clone(), ddof, propagate_nans)
218
+ .into()
219
+ }
220
+
221
+ pub fn cov(a: &RbExpr, b: &RbExpr) -> RbExpr {
222
+ polars::lazy::dsl::cov(a.inner.clone(), b.inner.clone()).into()
223
+ }
224
+
225
+ pub fn concat_str(s: RArray, sep: String) -> RbResult<RbExpr> {
226
+ let s = rb_exprs_to_exprs(s)?;
227
+ Ok(dsl::concat_str(s, &sep).into())
228
+ }
229
+
230
+ pub fn concat_lst(s: RArray) -> RbResult<RbExpr> {
231
+ let s = rb_exprs_to_exprs(s)?;
232
+ let expr = dsl::concat_list(s).map_err(RbPolarsErr::from)?;
233
+ Ok(expr.into())
234
+ }
235
+
236
+ pub fn dtype_cols2(dtypes: RArray) -> RbResult<RbExpr> {
237
+ let dtypes = dtypes
238
+ .each()
239
+ .map(|v| v?.try_convert::<Wrap<DataType>>())
240
+ .collect::<RbResult<Vec<Wrap<DataType>>>>()?;
241
+ let dtypes = vec_extract_wrapped(dtypes);
242
+ Ok(crate::functions::lazy::dtype_cols(dtypes))
243
+ }
244
+
245
+ // TODO rename to sum_horizontal
246
+ pub fn sum_exprs(exprs: RArray) -> RbResult<RbExpr> {
247
+ let exprs = rb_exprs_to_exprs(exprs)?;
248
+ Ok(polars::lazy::dsl::sum_horizontal(exprs).into())
249
+ }
@@ -0,0 +1,8 @@
1
+ use magnus::{IntoValue, Value};
2
+ use polars_core::prelude::IDX_DTYPE;
3
+
4
+ use crate::conversion::Wrap;
5
+
6
+ pub fn get_idx_type() -> Value {
7
+ Wrap(IDX_DTYPE).into_value()
8
+ }
@@ -0,0 +1,5 @@
1
+ pub mod eager;
2
+ pub mod io;
3
+ pub mod lazy;
4
+ pub mod meta;
5
+ pub mod whenthen;
@@ -0,0 +1,43 @@
1
+ use polars::lazy::dsl;
2
+
3
+ use crate::RbExpr;
4
+
5
+ #[magnus::wrap(class = "Polars::RbWhen")]
6
+ #[derive(Clone)]
7
+ pub struct RbWhen {
8
+ pub inner: dsl::When,
9
+ }
10
+
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")]
18
+ #[derive(Clone)]
19
+ pub struct RbWhenThen {
20
+ pub inner: dsl::WhenThen,
21
+ }
22
+
23
+ impl From<dsl::WhenThen> for RbWhenThen {
24
+ fn from(inner: dsl::WhenThen) -> Self {
25
+ RbWhenThen { inner }
26
+ }
27
+ }
28
+
29
+ impl RbWhen {
30
+ pub fn then(&self, expr: &RbExpr) -> RbWhenThen {
31
+ self.inner.clone().then(expr.inner.clone()).into()
32
+ }
33
+ }
34
+
35
+ impl RbWhenThen {
36
+ pub fn overwise(&self, expr: &RbExpr) -> RbExpr {
37
+ self.inner.clone().otherwise(expr.inner.clone()).into()
38
+ }
39
+ }
40
+
41
+ pub fn when(predicate: &RbExpr) -> RbWhen {
42
+ dsl::when(predicate.inner.clone()).into()
43
+ }
@@ -1,38 +1,15 @@
1
1
  use magnus::{IntoValue, RArray, RHash, Value};
2
2
  use polars::io::RowCount;
3
- use polars::lazy::frame::{LazyFrame, LazyGroupBy};
3
+ use polars::lazy::frame::LazyFrame;
4
4
  use polars::prelude::*;
5
5
  use std::cell::RefCell;
6
6
  use std::io::{BufWriter, Read};
7
7
  use std::path::PathBuf;
8
8
 
9
9
  use crate::conversion::*;
10
+ use crate::expr::rb_exprs_to_exprs;
10
11
  use crate::file::get_file_like;
11
- use crate::lazy::utils::rb_exprs_to_exprs;
12
- use crate::{RbDataFrame, RbExpr, RbPolarsErr, RbResult, RbValueError};
13
-
14
- #[magnus::wrap(class = "Polars::RbLazyGroupBy")]
15
- pub struct RbLazyGroupBy {
16
- lgb: RefCell<Option<LazyGroupBy>>,
17
- }
18
-
19
- impl RbLazyGroupBy {
20
- pub fn agg(&self, aggs: RArray) -> RbResult<RbLazyFrame> {
21
- let lgb = self.lgb.borrow_mut().take().unwrap();
22
- let aggs = rb_exprs_to_exprs(aggs)?;
23
- Ok(lgb.agg(aggs).into())
24
- }
25
-
26
- pub fn head(&self, n: usize) -> RbLazyFrame {
27
- let lgb = self.lgb.take().unwrap();
28
- lgb.head(Some(n)).into()
29
- }
30
-
31
- pub fn tail(&self, n: usize) -> RbLazyFrame {
32
- let lgb = self.lgb.take().unwrap();
33
- lgb.tail(Some(n)).into()
34
- }
35
- }
12
+ use crate::{RbDataFrame, RbExpr, RbLazyGroupBy, RbPolarsErr, RbResult, RbValueError};
36
13
 
37
14
  #[magnus::wrap(class = "Polars::RbLazyFrame")]
38
15
  #[derive(Clone)]
@@ -132,10 +109,10 @@ impl RbLazyFrame {
132
109
  let row_count = row_count.map(|(name, offset)| RowCount { name, offset });
133
110
 
134
111
  let overwrite_dtype = overwrite_dtype.map(|overwrite_dtype| {
135
- let fields = overwrite_dtype
112
+ overwrite_dtype
136
113
  .into_iter()
137
- .map(|(name, dtype)| Field::new(&name, dtype.0));
138
- Schema::from(fields)
114
+ .map(|(name, dtype)| Field::new(&name, dtype.0))
115
+ .collect::<Schema>()
139
116
  });
140
117
  let r = LazyCsvReader::new(path)
141
118
  .with_infer_schema_length(infer_schema_length)
@@ -253,7 +230,13 @@ impl RbLazyFrame {
253
230
  ldf.into()
254
231
  }
255
232
 
256
- pub fn sort(&self, by_column: String, reverse: bool, nulls_last: bool) -> Self {
233
+ pub fn sort(
234
+ &self,
235
+ by_column: String,
236
+ reverse: bool,
237
+ nulls_last: bool,
238
+ maintain_order: bool,
239
+ ) -> Self {
257
240
  let ldf = self.ldf.clone();
258
241
  ldf.sort(
259
242
  &by_column,
@@ -261,6 +244,7 @@ impl RbLazyFrame {
261
244
  descending: reverse,
262
245
  nulls_last,
263
246
  multithreaded: true,
247
+ maintain_order,
264
248
  },
265
249
  )
266
250
  .into()
@@ -271,10 +255,13 @@ impl RbLazyFrame {
271
255
  by_column: RArray,
272
256
  reverse: Vec<bool>,
273
257
  nulls_last: bool,
258
+ maintain_order: bool,
274
259
  ) -> RbResult<Self> {
275
260
  let ldf = self.ldf.clone();
276
261
  let exprs = rb_exprs_to_exprs(by_column)?;
277
- Ok(ldf.sort_by_exprs(exprs, reverse, nulls_last).into())
262
+ Ok(ldf
263
+ .sort_by_exprs(exprs, reverse, nulls_last, maintain_order)
264
+ .into())
278
265
  }
279
266
 
280
267
  pub fn cache(&self) -> Self {
@@ -346,22 +333,25 @@ impl RbLazyFrame {
346
333
 
347
334
  pub fn groupby_rolling(
348
335
  &self,
349
- index_column: String,
336
+ index_column: &RbExpr,
350
337
  period: String,
351
338
  offset: String,
352
339
  closed: Wrap<ClosedWindow>,
353
340
  by: RArray,
341
+ check_sorted: bool,
354
342
  ) -> RbResult<RbLazyGroupBy> {
355
343
  let closed_window = closed.0;
356
344
  let ldf = self.ldf.clone();
357
345
  let by = rb_exprs_to_exprs(by)?;
358
346
  let lazy_gb = ldf.groupby_rolling(
347
+ index_column.inner.clone(),
359
348
  by,
360
349
  RollingGroupOptions {
361
- index_column: index_column.into(),
350
+ index_column: "".into(),
362
351
  period: Duration::parse(&period),
363
352
  offset: Duration::parse(&offset),
364
353
  closed_window,
354
+ check_sorted,
365
355
  },
366
356
  );
367
357
 
@@ -373,7 +363,7 @@ impl RbLazyFrame {
373
363
  #[allow(clippy::too_many_arguments)]
374
364
  pub fn groupby_dynamic(
375
365
  &self,
376
- index_column: String,
366
+ index_column: &RbExpr,
377
367
  every: String,
378
368
  period: String,
379
369
  offset: String,
@@ -387,9 +377,9 @@ impl RbLazyFrame {
387
377
  let by = rb_exprs_to_exprs(by)?;
388
378
  let ldf = self.ldf.clone();
389
379
  let lazy_gb = ldf.groupby_dynamic(
380
+ index_column.inner.clone(),
390
381
  by,
391
382
  DynamicGroupOptions {
392
- index_column: index_column.into(),
393
383
  every: Duration::parse(&every),
394
384
  period: Duration::parse(&period),
395
385
  offset: Duration::parse(&offset),
@@ -397,6 +387,7 @@ impl RbLazyFrame {
397
387
  include_boundaries,
398
388
  closed_window,
399
389
  start_by: start_by.0,
390
+ ..Default::default()
400
391
  },
401
392
  );
402
393
 
@@ -0,0 +1,29 @@
1
+ use magnus::RArray;
2
+ use polars::lazy::frame::LazyGroupBy;
3
+ use std::cell::RefCell;
4
+
5
+ use crate::expr::rb_exprs_to_exprs;
6
+ use crate::{RbLazyFrame, RbResult};
7
+
8
+ #[magnus::wrap(class = "Polars::RbLazyGroupBy")]
9
+ pub struct RbLazyGroupBy {
10
+ pub lgb: RefCell<Option<LazyGroupBy>>,
11
+ }
12
+
13
+ impl RbLazyGroupBy {
14
+ pub fn agg(&self, aggs: RArray) -> RbResult<RbLazyFrame> {
15
+ let lgb = self.lgb.borrow_mut().take().unwrap();
16
+ let aggs = rb_exprs_to_exprs(aggs)?;
17
+ Ok(lgb.agg(aggs).into())
18
+ }
19
+
20
+ pub fn head(&self, n: usize) -> RbLazyFrame {
21
+ let lgb = self.lgb.take().unwrap();
22
+ lgb.head(Some(n)).into()
23
+ }
24
+
25
+ pub fn tail(&self, n: usize) -> RbLazyFrame {
26
+ let lgb = self.lgb.take().unwrap();
27
+ lgb.tail(Some(n)).into()
28
+ }
29
+ }