polars-df 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }