polars-df 0.1.1 → 0.1.3

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -0
  3. data/CHANGELOG.md +8 -0
  4. data/Cargo.lock +2 -1
  5. data/README.md +1 -1
  6. data/ext/polars/Cargo.toml +7 -1
  7. data/ext/polars/src/batched_csv.rs +120 -0
  8. data/ext/polars/src/conversion.rs +139 -6
  9. data/ext/polars/src/dataframe.rs +360 -15
  10. data/ext/polars/src/error.rs +9 -0
  11. data/ext/polars/src/file.rs +8 -7
  12. data/ext/polars/src/lazy/apply.rs +7 -0
  13. data/ext/polars/src/lazy/dataframe.rs +135 -3
  14. data/ext/polars/src/lazy/dsl.rs +97 -2
  15. data/ext/polars/src/lazy/meta.rs +1 -1
  16. data/ext/polars/src/lazy/mod.rs +1 -0
  17. data/ext/polars/src/lib.rs +227 -12
  18. data/ext/polars/src/series.rs +190 -38
  19. data/ext/polars/src/set.rs +91 -0
  20. data/ext/polars/src/utils.rs +19 -0
  21. data/lib/polars/batched_csv_reader.rb +96 -0
  22. data/lib/polars/cat_expr.rb +39 -0
  23. data/lib/polars/data_frame.rb +2813 -100
  24. data/lib/polars/date_time_expr.rb +1282 -7
  25. data/lib/polars/exceptions.rb +20 -0
  26. data/lib/polars/expr.rb +631 -11
  27. data/lib/polars/expr_dispatch.rb +14 -0
  28. data/lib/polars/functions.rb +219 -0
  29. data/lib/polars/group_by.rb +517 -0
  30. data/lib/polars/io.rb +763 -4
  31. data/lib/polars/lazy_frame.rb +1415 -67
  32. data/lib/polars/lazy_functions.rb +430 -9
  33. data/lib/polars/lazy_group_by.rb +79 -0
  34. data/lib/polars/list_expr.rb +5 -0
  35. data/lib/polars/meta_expr.rb +21 -0
  36. data/lib/polars/series.rb +2244 -192
  37. data/lib/polars/slice.rb +104 -0
  38. data/lib/polars/string_expr.rb +663 -2
  39. data/lib/polars/struct_expr.rb +73 -0
  40. data/lib/polars/utils.rb +76 -3
  41. data/lib/polars/version.rb +2 -1
  42. data/lib/polars/when.rb +1 -0
  43. data/lib/polars/when_then.rb +1 -0
  44. data/lib/polars.rb +8 -2
  45. metadata +12 -2
@@ -5,6 +5,7 @@ use polars::series::IsSorted;
5
5
  use std::cell::RefCell;
6
6
 
7
7
  use crate::conversion::*;
8
+ use crate::set::set_at_idx;
8
9
  use crate::{RbDataFrame, RbPolarsErr, RbResult, RbValueError};
9
10
 
10
11
  #[magnus::wrap(class = "Polars::RbSeries")]
@@ -116,11 +117,10 @@ init_method_opt!(new_opt_f32, Float32Type, f32);
116
117
  init_method_opt!(new_opt_f64, Float64Type, f64);
117
118
 
118
119
  impl RbSeries {
119
- pub fn new_str(name: String, val: RArray, _strict: bool) -> RbResult<Self> {
120
- let v = val.try_convert::<Vec<Option<String>>>()?;
121
- let mut s = Utf8Chunked::new(&name, v).into_series();
120
+ pub fn new_str(name: String, val: Wrap<Utf8Chunked>, _strict: bool) -> Self {
121
+ let mut s = val.0.into_series();
122
122
  s.rename(&name);
123
- Ok(RbSeries::new(s))
123
+ RbSeries::new(s)
124
124
  }
125
125
 
126
126
  pub fn estimated_size(&self) -> usize {
@@ -199,16 +199,16 @@ impl RbSeries {
199
199
  self.series.borrow_mut().rename(&name);
200
200
  }
201
201
 
202
- pub fn dtype(&self) -> String {
203
- self.series.borrow().dtype().to_string()
202
+ pub fn dtype(&self) -> Value {
203
+ Wrap(self.series.borrow().dtype().clone()).into()
204
204
  }
205
205
 
206
- pub fn inner_dtype(&self) -> Option<String> {
206
+ pub fn inner_dtype(&self) -> Option<Value> {
207
207
  self.series
208
208
  .borrow()
209
209
  .dtype()
210
210
  .inner_dtype()
211
- .map(|dt| dt.to_string())
211
+ .map(|dt| Wrap(dt.clone()).into())
212
212
  }
213
213
 
214
214
  pub fn set_sorted(&self, reverse: bool) -> Self {
@@ -475,6 +475,12 @@ impl RbSeries {
475
475
  s.into_iter().collect()
476
476
  } else if let Ok(s) = series.utf8() {
477
477
  s.into_iter().collect()
478
+ } else if let Ok(_s) = series.date() {
479
+ let a = RArray::with_capacity(series.len());
480
+ for v in series.iter() {
481
+ a.push::<Value>(Wrap(v).into()).unwrap();
482
+ }
483
+ a
478
484
  } else {
479
485
  unimplemented!();
480
486
  }
@@ -595,44 +601,190 @@ impl RbSeries {
595
601
  }
596
602
  }
597
603
 
598
- // dispatch dynamically in future?
599
-
600
- pub fn cumsum(&self, reverse: bool) -> Self {
601
- self.series.borrow().cumsum(reverse).into()
604
+ pub fn set_at_idx(&self, idx: &RbSeries, values: &RbSeries) -> RbResult<()> {
605
+ let mut s = self.series.borrow_mut();
606
+ match set_at_idx(s.clone(), &idx.series.borrow(), &values.series.borrow()) {
607
+ Ok(out) => {
608
+ *s = out;
609
+ Ok(())
610
+ }
611
+ Err(e) => Err(RbPolarsErr::from(e)),
612
+ }
602
613
  }
614
+ }
603
615
 
604
- pub fn cummax(&self, reverse: bool) -> Self {
605
- self.series.borrow().cummax(reverse).into()
606
- }
616
+ macro_rules! impl_eq_num {
617
+ ($name:ident, $type:ty) => {
618
+ impl RbSeries {
619
+ pub fn $name(&self, rhs: $type) -> RbResult<Self> {
620
+ let s = self.series.borrow().equal(rhs).map_err(RbPolarsErr::from)?;
621
+ Ok(RbSeries::new(s.into_series()))
622
+ }
623
+ }
624
+ };
625
+ }
607
626
 
608
- pub fn cummin(&self, reverse: bool) -> Self {
609
- self.series.borrow().cummin(reverse).into()
610
- }
627
+ impl_eq_num!(eq_u8, u8);
628
+ impl_eq_num!(eq_u16, u16);
629
+ impl_eq_num!(eq_u32, u32);
630
+ impl_eq_num!(eq_u64, u64);
631
+ impl_eq_num!(eq_i8, i8);
632
+ impl_eq_num!(eq_i16, i16);
633
+ impl_eq_num!(eq_i32, i32);
634
+ impl_eq_num!(eq_i64, i64);
635
+ impl_eq_num!(eq_f32, f32);
636
+ impl_eq_num!(eq_f64, f64);
637
+ // impl_eq_num!(eq_str, &str);
638
+
639
+ macro_rules! impl_neq_num {
640
+ ($name:ident, $type:ty) => {
641
+ impl RbSeries {
642
+ pub fn $name(&self, rhs: $type) -> RbResult<Self> {
643
+ let s = self
644
+ .series
645
+ .borrow()
646
+ .not_equal(rhs)
647
+ .map_err(RbPolarsErr::from)?;
648
+ Ok(RbSeries::new(s.into_series()))
649
+ }
650
+ }
651
+ };
652
+ }
611
653
 
612
- pub fn cumprod(&self, reverse: bool) -> Self {
613
- self.series.borrow().cumprod(reverse).into()
614
- }
654
+ impl_neq_num!(neq_u8, u8);
655
+ impl_neq_num!(neq_u16, u16);
656
+ impl_neq_num!(neq_u32, u32);
657
+ impl_neq_num!(neq_u64, u64);
658
+ impl_neq_num!(neq_i8, i8);
659
+ impl_neq_num!(neq_i16, i16);
660
+ impl_neq_num!(neq_i32, i32);
661
+ impl_neq_num!(neq_i64, i64);
662
+ impl_neq_num!(neq_f32, f32);
663
+ impl_neq_num!(neq_f64, f64);
664
+ // impl_neq_num!(neq_str, &str);
665
+
666
+ macro_rules! impl_gt_num {
667
+ ($name:ident, $type:ty) => {
668
+ impl RbSeries {
669
+ pub fn $name(&self, rhs: $type) -> RbResult<Self> {
670
+ let s = self.series.borrow().gt(rhs).map_err(RbPolarsErr::from)?;
671
+ Ok(RbSeries::new(s.into_series()))
672
+ }
673
+ }
674
+ };
675
+ }
615
676
 
616
- pub fn slice(&self, offset: i64, length: usize) -> Self {
617
- let series = self.series.borrow().slice(offset, length);
618
- series.into()
619
- }
677
+ impl_gt_num!(gt_u8, u8);
678
+ impl_gt_num!(gt_u16, u16);
679
+ impl_gt_num!(gt_u32, u32);
680
+ impl_gt_num!(gt_u64, u64);
681
+ impl_gt_num!(gt_i8, i8);
682
+ impl_gt_num!(gt_i16, i16);
683
+ impl_gt_num!(gt_i32, i32);
684
+ impl_gt_num!(gt_i64, i64);
685
+ impl_gt_num!(gt_f32, f32);
686
+ impl_gt_num!(gt_f64, f64);
687
+ // impl_gt_num!(gt_str, &str);
688
+
689
+ macro_rules! impl_gt_eq_num {
690
+ ($name:ident, $type:ty) => {
691
+ impl RbSeries {
692
+ pub fn $name(&self, rhs: $type) -> RbResult<Self> {
693
+ let s = self.series.borrow().gt_eq(rhs).map_err(RbPolarsErr::from)?;
694
+ Ok(RbSeries::new(s.into_series()))
695
+ }
696
+ }
697
+ };
698
+ }
620
699
 
621
- pub fn ceil(&self) -> RbResult<Self> {
622
- let s = self.series.borrow().ceil().map_err(RbPolarsErr::from)?;
623
- Ok(s.into())
624
- }
700
+ impl_gt_eq_num!(gt_eq_u8, u8);
701
+ impl_gt_eq_num!(gt_eq_u16, u16);
702
+ impl_gt_eq_num!(gt_eq_u32, u32);
703
+ impl_gt_eq_num!(gt_eq_u64, u64);
704
+ impl_gt_eq_num!(gt_eq_i8, i8);
705
+ impl_gt_eq_num!(gt_eq_i16, i16);
706
+ impl_gt_eq_num!(gt_eq_i32, i32);
707
+ impl_gt_eq_num!(gt_eq_i64, i64);
708
+ impl_gt_eq_num!(gt_eq_f32, f32);
709
+ impl_gt_eq_num!(gt_eq_f64, f64);
710
+ // impl_gt_eq_num!(gt_eq_str, &str);
711
+
712
+ macro_rules! impl_lt_num {
713
+ ($name:ident, $type:ty) => {
714
+ impl RbSeries {
715
+ pub fn $name(&self, rhs: $type) -> RbResult<RbSeries> {
716
+ let s = self.series.borrow().lt(rhs).map_err(RbPolarsErr::from)?;
717
+ Ok(RbSeries::new(s.into_series()))
718
+ }
719
+ }
720
+ };
721
+ }
625
722
 
626
- pub fn round(&self, decimals: u32) -> RbResult<Self> {
627
- let s = self
628
- .series
629
- .borrow()
630
- .round(decimals)
631
- .map_err(RbPolarsErr::from)?;
632
- Ok(s.into())
633
- }
723
+ impl_lt_num!(lt_u8, u8);
724
+ impl_lt_num!(lt_u16, u16);
725
+ impl_lt_num!(lt_u32, u32);
726
+ impl_lt_num!(lt_u64, u64);
727
+ impl_lt_num!(lt_i8, i8);
728
+ impl_lt_num!(lt_i16, i16);
729
+ impl_lt_num!(lt_i32, i32);
730
+ impl_lt_num!(lt_i64, i64);
731
+ impl_lt_num!(lt_f32, f32);
732
+ impl_lt_num!(lt_f64, f64);
733
+ // impl_lt_num!(lt_str, &str);
734
+
735
+ macro_rules! impl_lt_eq_num {
736
+ ($name:ident, $type:ty) => {
737
+ impl RbSeries {
738
+ pub fn $name(&self, rhs: $type) -> RbResult<Self> {
739
+ let s = self.series.borrow().lt_eq(rhs).map_err(RbPolarsErr::from)?;
740
+ Ok(RbSeries::new(s.into_series()))
741
+ }
742
+ }
743
+ };
744
+ }
745
+
746
+ impl_lt_eq_num!(lt_eq_u8, u8);
747
+ impl_lt_eq_num!(lt_eq_u16, u16);
748
+ impl_lt_eq_num!(lt_eq_u32, u32);
749
+ impl_lt_eq_num!(lt_eq_u64, u64);
750
+ impl_lt_eq_num!(lt_eq_i8, i8);
751
+ impl_lt_eq_num!(lt_eq_i16, i16);
752
+ impl_lt_eq_num!(lt_eq_i32, i32);
753
+ impl_lt_eq_num!(lt_eq_i64, i64);
754
+ impl_lt_eq_num!(lt_eq_f32, f32);
755
+ impl_lt_eq_num!(lt_eq_f64, f64);
756
+ // impl_lt_eq_num!(lt_eq_str, &str);
757
+
758
+ pub fn to_series_collection(rs: RArray) -> RbResult<Vec<Series>> {
759
+ let mut series = Vec::new();
760
+ for item in rs.each() {
761
+ series.push(item?.try_convert::<&RbSeries>()?.series.borrow().clone());
762
+ }
763
+ Ok(series)
634
764
  }
635
765
 
636
766
  pub fn to_rbseries_collection(s: Vec<Series>) -> Vec<RbSeries> {
637
- s.into_iter().map(|v| RbSeries::new(v)).collect()
767
+ s.into_iter().map(RbSeries::new).collect()
768
+ }
769
+
770
+ impl RbSeries {
771
+ pub fn new_opt_date(name: String, values: RArray, _strict: Option<bool>) -> RbResult<Self> {
772
+ let len = values.len();
773
+ let mut builder = PrimitiveChunkedBuilder::<Int32Type>::new(&name, len);
774
+ for item in values.each() {
775
+ let v = item?;
776
+ if v.is_nil() {
777
+ builder.append_null();
778
+ } else {
779
+ // convert to DateTime for UTC
780
+ let v: Value = v.funcall("to_datetime", ())?;
781
+ let v: Value = v.funcall("to_time", ())?;
782
+ let v: Value = v.funcall("to_i", ())?;
783
+ // TODO use strict
784
+ builder.append_value(v.try_convert::<i32>()? / 86400);
785
+ }
786
+ }
787
+ let ca: ChunkedArray<Int32Type> = builder.finish();
788
+ Ok(ca.into_date().into_series().into())
789
+ }
638
790
  }
@@ -0,0 +1,91 @@
1
+ // use polars::export::arrow2::array::Array;
2
+ use polars::prelude::*;
3
+
4
+ pub fn set_at_idx(mut s: Series, idx: &Series, values: &Series) -> PolarsResult<Series> {
5
+ let logical_dtype = s.dtype().clone();
6
+ let idx = idx.cast(&IDX_DTYPE)?;
7
+ let idx = idx.rechunk();
8
+ let idx = idx.idx().unwrap();
9
+ let idx = idx.downcast_iter().next().unwrap();
10
+
11
+ // if idx.null_count() > 0 {
12
+ // return Err(PolarsError::ComputeError(
13
+ // "index values should not be null".into(),
14
+ // ));
15
+ // }
16
+
17
+ let idx = idx.values().as_slice();
18
+
19
+ let values = values.to_physical_repr().cast(&s.dtype().to_physical())?;
20
+
21
+ // do not shadow, otherwise s is not dropped immediately
22
+ // and we want to have mutable access
23
+ s = s.to_physical_repr().into_owned();
24
+ let mutable_s = s._get_inner_mut();
25
+
26
+ let s = match logical_dtype.to_physical() {
27
+ DataType::Int8 => {
28
+ let ca: &mut ChunkedArray<Int8Type> = mutable_s.as_mut();
29
+ let values = values.i8()?;
30
+ std::mem::take(ca).set_at_idx2(idx, values.into_iter())
31
+ }
32
+ DataType::Int16 => {
33
+ let ca: &mut ChunkedArray<Int16Type> = mutable_s.as_mut();
34
+ let values = values.i16()?;
35
+ std::mem::take(ca).set_at_idx2(idx, values.into_iter())
36
+ }
37
+ DataType::Int32 => {
38
+ let ca: &mut ChunkedArray<Int32Type> = mutable_s.as_mut();
39
+ let values = values.i32()?;
40
+ std::mem::take(ca).set_at_idx2(idx, values.into_iter())
41
+ }
42
+ DataType::Int64 => {
43
+ let ca: &mut ChunkedArray<Int64Type> = mutable_s.as_mut();
44
+ let values = values.i64()?;
45
+ std::mem::take(ca).set_at_idx2(idx, values.into_iter())
46
+ }
47
+ DataType::UInt8 => {
48
+ let ca: &mut ChunkedArray<UInt8Type> = mutable_s.as_mut();
49
+ let values = values.u8()?;
50
+ std::mem::take(ca).set_at_idx2(idx, values.into_iter())
51
+ }
52
+ DataType::UInt16 => {
53
+ let ca: &mut ChunkedArray<UInt16Type> = mutable_s.as_mut();
54
+ let values = values.u16()?;
55
+ std::mem::take(ca).set_at_idx2(idx, values.into_iter())
56
+ }
57
+ DataType::UInt32 => {
58
+ let ca: &mut ChunkedArray<UInt32Type> = mutable_s.as_mut();
59
+ let values = values.u32()?;
60
+ std::mem::take(ca).set_at_idx2(idx, values.into_iter())
61
+ }
62
+ DataType::UInt64 => {
63
+ let ca: &mut ChunkedArray<UInt64Type> = mutable_s.as_mut();
64
+ let values = values.u64()?;
65
+ std::mem::take(ca).set_at_idx2(idx, values.into_iter())
66
+ }
67
+ DataType::Float32 => {
68
+ let ca: &mut ChunkedArray<Float32Type> = mutable_s.as_mut();
69
+ let values = values.f32()?;
70
+ std::mem::take(ca).set_at_idx2(idx, values.into_iter())
71
+ }
72
+ DataType::Float64 => {
73
+ let ca: &mut ChunkedArray<Float64Type> = mutable_s.as_mut();
74
+ let values = values.f64()?;
75
+ std::mem::take(ca).set_at_idx2(idx, values.into_iter())
76
+ }
77
+ DataType::Boolean => {
78
+ let ca = s.bool()?;
79
+ let values = values.bool()?;
80
+ ca.set_at_idx2(idx, values)
81
+ }
82
+ DataType::Utf8 => {
83
+ let ca = s.utf8()?;
84
+ let values = values.utf8()?;
85
+ ca.set_at_idx2(idx, values)
86
+ }
87
+ _ => panic!("not yet implemented for dtype: {}", logical_dtype),
88
+ };
89
+
90
+ s.and_then(|s| s.cast(&logical_dtype))
91
+ }
@@ -0,0 +1,19 @@
1
+ use polars::prelude::*;
2
+
3
+ pub fn reinterpret(s: &Series, signed: bool) -> polars::prelude::PolarsResult<Series> {
4
+ match (s.dtype(), signed) {
5
+ (DataType::UInt64, true) => {
6
+ let ca = s.u64().unwrap();
7
+ Ok(ca.reinterpret_signed().into_series())
8
+ }
9
+ (DataType::UInt64, false) => Ok(s.clone()),
10
+ (DataType::Int64, false) => {
11
+ let ca = s.i64().unwrap();
12
+ Ok(ca.reinterpret_unsigned().into_series())
13
+ }
14
+ (DataType::Int64, true) => Ok(s.clone()),
15
+ _ => Err(PolarsError::ComputeError(
16
+ "reinterpret is only allowed for 64bit integers dtype, use cast otherwise".into(),
17
+ )),
18
+ }
19
+ }
@@ -0,0 +1,96 @@
1
+ module Polars
2
+ # @private
3
+ class BatchedCsvReader
4
+ attr_accessor :_reader, :new_columns
5
+
6
+ def initialize(
7
+ file,
8
+ has_header: true,
9
+ columns: nil,
10
+ sep: ",",
11
+ comment_char: nil,
12
+ quote_char: '"',
13
+ skip_rows: 0,
14
+ dtypes: nil,
15
+ null_values: nil,
16
+ ignore_errors: false,
17
+ parse_dates: false,
18
+ n_threads: nil,
19
+ infer_schema_length: 100,
20
+ batch_size: 50_000,
21
+ n_rows: nil,
22
+ encoding: "utf8",
23
+ low_memory: false,
24
+ rechunk: true,
25
+ skip_rows_after_header: 0,
26
+ row_count_name: nil,
27
+ row_count_offset: 0,
28
+ sample_size: 1024,
29
+ eol_char: "\n",
30
+ new_columns: nil
31
+ )
32
+ if file.is_a?(String) || (defined?(Pathname) && file.is_a?(Pathname))
33
+ path = Utils.format_path(file)
34
+ end
35
+
36
+ dtype_list = nil
37
+ dtype_slice = nil
38
+ if !dtypes.nil?
39
+ if dtypes.is_a?(Hash)
40
+ dtype_list = []
41
+ dtypes.each do|k, v|
42
+ dtype_list << [k, Utils.rb_type_to_dtype(v)]
43
+ end
44
+ elsif dtypes.is_a?(Array)
45
+ dtype_slice = dtypes
46
+ else
47
+ raise ArgumentError, "dtype arg should be list or dict"
48
+ end
49
+ end
50
+
51
+ processed_null_values = Utils._process_null_values(null_values)
52
+ projection, columns = Utils.handle_projection_columns(columns)
53
+
54
+ self._reader = RbBatchedCsv.new(
55
+ infer_schema_length,
56
+ batch_size,
57
+ has_header,
58
+ ignore_errors,
59
+ n_rows,
60
+ skip_rows,
61
+ projection,
62
+ sep,
63
+ rechunk,
64
+ columns,
65
+ encoding,
66
+ n_threads,
67
+ path,
68
+ dtype_list,
69
+ dtype_slice,
70
+ low_memory,
71
+ comment_char,
72
+ quote_char,
73
+ processed_null_values,
74
+ parse_dates,
75
+ skip_rows_after_header,
76
+ Utils._prepare_row_count_args(row_count_name, row_count_offset),
77
+ sample_size,
78
+ eol_char
79
+ )
80
+ self.new_columns = new_columns
81
+ end
82
+
83
+ def next_batches(n)
84
+ batches = _reader.next_batches(n)
85
+ if !batches.nil?
86
+ if new_columns
87
+ batches.map { |df| Utils._update_columns(Utils.wrap_df(df), new_columns) }
88
+ else
89
+ batches.map { |df| Utils.wrap_df(df) }
90
+ end
91
+ else
92
+ nil
93
+ end
94
+ end
95
+ end
96
+ end
@@ -1,11 +1,50 @@
1
1
  module Polars
2
+ # Namespace for categorical related expressions.
2
3
  class CatExpr
4
+ # @private
3
5
  attr_accessor :_rbexpr
4
6
 
7
+ # @private
5
8
  def initialize(expr)
6
9
  self._rbexpr = expr._rbexpr
7
10
  end
8
11
 
12
+ # Determine how this categorical series should be sorted.
13
+ #
14
+ # @param ordering ["physical", "lexical"]
15
+ # Ordering type:
16
+ #
17
+ # - 'physical' -> Use the physical representation of the categories to determine the order (default).
18
+ # - 'lexical' -> Use the string values to determine the ordering.
19
+ #
20
+ # @return [Expr]
21
+ #
22
+ # @example
23
+ # df = Polars::DataFrame.new(
24
+ # {"cats" => ["z", "z", "k", "a", "b"], "vals" => [3, 1, 2, 2, 3]}
25
+ # ).with_columns(
26
+ # [
27
+ # Polars.col("cats").cast(:cat).cat.set_ordering("lexical")
28
+ # ]
29
+ # )
30
+ # df.sort(["cats", "vals"])
31
+ # # =>
32
+ # # shape: (5, 2)
33
+ # # ┌──────┬──────┐
34
+ # # │ cats ┆ vals │
35
+ # # │ --- ┆ --- │
36
+ # # │ cat ┆ i64 │
37
+ # # ╞══════╪══════╡
38
+ # # │ a ┆ 2 │
39
+ # # ├╌╌╌╌╌╌┼╌╌╌╌╌╌┤
40
+ # # │ b ┆ 3 │
41
+ # # ├╌╌╌╌╌╌┼╌╌╌╌╌╌┤
42
+ # # │ k ┆ 2 │
43
+ # # ├╌╌╌╌╌╌┼╌╌╌╌╌╌┤
44
+ # # │ z ┆ 1 │
45
+ # # ├╌╌╌╌╌╌┼╌╌╌╌╌╌┤
46
+ # # │ z ┆ 3 │
47
+ # # └──────┴──────┘
9
48
  def set_ordering(ordering)
10
49
  Utils.wrap_expr(_rbexpr.cat_set_ordering(ordering))
11
50
  end