polars-df 0.1.2 → 0.1.4

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -0
  3. data/CHANGELOG.md +9 -0
  4. data/Cargo.lock +74 -3
  5. data/Cargo.toml +3 -0
  6. data/README.md +1 -1
  7. data/ext/polars/Cargo.toml +18 -1
  8. data/ext/polars/src/conversion.rs +115 -2
  9. data/ext/polars/src/dataframe.rs +228 -11
  10. data/ext/polars/src/error.rs +4 -0
  11. data/ext/polars/src/lazy/dataframe.rs +5 -5
  12. data/ext/polars/src/lazy/dsl.rs +157 -2
  13. data/ext/polars/src/lib.rs +185 -10
  14. data/ext/polars/src/list_construction.rs +100 -0
  15. data/ext/polars/src/series.rs +217 -29
  16. data/ext/polars/src/set.rs +91 -0
  17. data/ext/polars/src/utils.rs +19 -0
  18. data/lib/polars/batched_csv_reader.rb +1 -0
  19. data/lib/polars/cat_expr.rb +39 -0
  20. data/lib/polars/cat_name_space.rb +54 -0
  21. data/lib/polars/data_frame.rb +2384 -140
  22. data/lib/polars/date_time_expr.rb +1282 -7
  23. data/lib/polars/date_time_name_space.rb +1484 -0
  24. data/lib/polars/exceptions.rb +20 -0
  25. data/lib/polars/expr.rb +4374 -53
  26. data/lib/polars/expr_dispatch.rb +22 -0
  27. data/lib/polars/functions.rb +219 -0
  28. data/lib/polars/group_by.rb +518 -0
  29. data/lib/polars/io.rb +421 -2
  30. data/lib/polars/lazy_frame.rb +1267 -69
  31. data/lib/polars/lazy_functions.rb +412 -24
  32. data/lib/polars/lazy_group_by.rb +80 -0
  33. data/lib/polars/list_expr.rb +507 -5
  34. data/lib/polars/list_name_space.rb +346 -0
  35. data/lib/polars/meta_expr.rb +21 -0
  36. data/lib/polars/series.rb +2256 -242
  37. data/lib/polars/slice.rb +104 -0
  38. data/lib/polars/string_expr.rb +847 -10
  39. data/lib/polars/string_name_space.rb +690 -0
  40. data/lib/polars/struct_expr.rb +73 -0
  41. data/lib/polars/struct_name_space.rb +64 -0
  42. data/lib/polars/utils.rb +71 -3
  43. data/lib/polars/version.rb +2 -1
  44. data/lib/polars/when.rb +1 -0
  45. data/lib/polars/when_then.rb +1 -0
  46. data/lib/polars.rb +12 -10
  47. metadata +15 -2
@@ -5,6 +5,8 @@ use polars::series::IsSorted;
5
5
  use std::cell::RefCell;
6
6
 
7
7
  use crate::conversion::*;
8
+ use crate::list_construction::rb_seq_to_list;
9
+ use crate::set::set_at_idx;
8
10
  use crate::{RbDataFrame, RbPolarsErr, RbResult, RbValueError};
9
11
 
10
12
  #[magnus::wrap(class = "Polars::RbSeries")]
@@ -122,6 +124,19 @@ impl RbSeries {
122
124
  RbSeries::new(s)
123
125
  }
124
126
 
127
+ pub fn new_object(name: String, val: RArray, _strict: bool) -> RbResult<Self> {
128
+ let val = val
129
+ .each()
130
+ .map(|v| v.map(ObjectValue::from))
131
+ .collect::<RbResult<Vec<ObjectValue>>>()?;
132
+ let s = ObjectChunked::<ObjectValue>::new_from_vec(&name, val).into_series();
133
+ Ok(s.into())
134
+ }
135
+
136
+ pub fn new_list(name: String, seq: Value, dtype: Wrap<DataType>) -> RbResult<Self> {
137
+ rb_seq_to_list(&name, seq, &dtype.0).map(|s| s.into())
138
+ }
139
+
125
140
  pub fn estimated_size(&self) -> usize {
126
141
  self.series.borrow().estimated_size()
127
142
  }
@@ -474,6 +489,12 @@ impl RbSeries {
474
489
  s.into_iter().collect()
475
490
  } else if let Ok(s) = series.utf8() {
476
491
  s.into_iter().collect()
492
+ } else if let Ok(_s) = series.date() {
493
+ let a = RArray::with_capacity(series.len());
494
+ for v in series.iter() {
495
+ a.push::<Value>(Wrap(v).into()).unwrap();
496
+ }
497
+ a
477
498
  } else {
478
499
  unimplemented!();
479
500
  }
@@ -594,44 +615,211 @@ impl RbSeries {
594
615
  }
595
616
  }
596
617
 
597
- // dispatch dynamically in future?
598
-
599
- pub fn cumsum(&self, reverse: bool) -> Self {
600
- self.series.borrow().cumsum(reverse).into()
618
+ pub fn set_at_idx(&self, idx: &RbSeries, values: &RbSeries) -> RbResult<()> {
619
+ let mut s = self.series.borrow_mut();
620
+ match set_at_idx(s.clone(), &idx.series.borrow(), &values.series.borrow()) {
621
+ Ok(out) => {
622
+ *s = out;
623
+ Ok(())
624
+ }
625
+ Err(e) => Err(RbPolarsErr::from(e)),
626
+ }
601
627
  }
628
+ }
602
629
 
603
- pub fn cummax(&self, reverse: bool) -> Self {
604
- self.series.borrow().cummax(reverse).into()
605
- }
630
+ macro_rules! impl_eq_num {
631
+ ($name:ident, $type:ty) => {
632
+ impl RbSeries {
633
+ pub fn $name(&self, rhs: $type) -> RbResult<Self> {
634
+ let s = self.series.borrow().equal(rhs).map_err(RbPolarsErr::from)?;
635
+ Ok(RbSeries::new(s.into_series()))
636
+ }
637
+ }
638
+ };
639
+ }
606
640
 
607
- pub fn cummin(&self, reverse: bool) -> Self {
608
- self.series.borrow().cummin(reverse).into()
609
- }
641
+ impl_eq_num!(eq_u8, u8);
642
+ impl_eq_num!(eq_u16, u16);
643
+ impl_eq_num!(eq_u32, u32);
644
+ impl_eq_num!(eq_u64, u64);
645
+ impl_eq_num!(eq_i8, i8);
646
+ impl_eq_num!(eq_i16, i16);
647
+ impl_eq_num!(eq_i32, i32);
648
+ impl_eq_num!(eq_i64, i64);
649
+ impl_eq_num!(eq_f32, f32);
650
+ impl_eq_num!(eq_f64, f64);
651
+ // impl_eq_num!(eq_str, &str);
652
+
653
+ macro_rules! impl_neq_num {
654
+ ($name:ident, $type:ty) => {
655
+ impl RbSeries {
656
+ pub fn $name(&self, rhs: $type) -> RbResult<Self> {
657
+ let s = self
658
+ .series
659
+ .borrow()
660
+ .not_equal(rhs)
661
+ .map_err(RbPolarsErr::from)?;
662
+ Ok(RbSeries::new(s.into_series()))
663
+ }
664
+ }
665
+ };
666
+ }
610
667
 
611
- pub fn cumprod(&self, reverse: bool) -> Self {
612
- self.series.borrow().cumprod(reverse).into()
613
- }
668
+ impl_neq_num!(neq_u8, u8);
669
+ impl_neq_num!(neq_u16, u16);
670
+ impl_neq_num!(neq_u32, u32);
671
+ impl_neq_num!(neq_u64, u64);
672
+ impl_neq_num!(neq_i8, i8);
673
+ impl_neq_num!(neq_i16, i16);
674
+ impl_neq_num!(neq_i32, i32);
675
+ impl_neq_num!(neq_i64, i64);
676
+ impl_neq_num!(neq_f32, f32);
677
+ impl_neq_num!(neq_f64, f64);
678
+ // impl_neq_num!(neq_str, &str);
679
+
680
+ macro_rules! impl_gt_num {
681
+ ($name:ident, $type:ty) => {
682
+ impl RbSeries {
683
+ pub fn $name(&self, rhs: $type) -> RbResult<Self> {
684
+ let s = self.series.borrow().gt(rhs).map_err(RbPolarsErr::from)?;
685
+ Ok(RbSeries::new(s.into_series()))
686
+ }
687
+ }
688
+ };
689
+ }
614
690
 
615
- pub fn slice(&self, offset: i64, length: usize) -> Self {
616
- let series = self.series.borrow().slice(offset, length);
617
- series.into()
618
- }
691
+ impl_gt_num!(gt_u8, u8);
692
+ impl_gt_num!(gt_u16, u16);
693
+ impl_gt_num!(gt_u32, u32);
694
+ impl_gt_num!(gt_u64, u64);
695
+ impl_gt_num!(gt_i8, i8);
696
+ impl_gt_num!(gt_i16, i16);
697
+ impl_gt_num!(gt_i32, i32);
698
+ impl_gt_num!(gt_i64, i64);
699
+ impl_gt_num!(gt_f32, f32);
700
+ impl_gt_num!(gt_f64, f64);
701
+ // impl_gt_num!(gt_str, &str);
702
+
703
+ macro_rules! impl_gt_eq_num {
704
+ ($name:ident, $type:ty) => {
705
+ impl RbSeries {
706
+ pub fn $name(&self, rhs: $type) -> RbResult<Self> {
707
+ let s = self.series.borrow().gt_eq(rhs).map_err(RbPolarsErr::from)?;
708
+ Ok(RbSeries::new(s.into_series()))
709
+ }
710
+ }
711
+ };
712
+ }
619
713
 
620
- pub fn ceil(&self) -> RbResult<Self> {
621
- let s = self.series.borrow().ceil().map_err(RbPolarsErr::from)?;
622
- Ok(s.into())
623
- }
714
+ impl_gt_eq_num!(gt_eq_u8, u8);
715
+ impl_gt_eq_num!(gt_eq_u16, u16);
716
+ impl_gt_eq_num!(gt_eq_u32, u32);
717
+ impl_gt_eq_num!(gt_eq_u64, u64);
718
+ impl_gt_eq_num!(gt_eq_i8, i8);
719
+ impl_gt_eq_num!(gt_eq_i16, i16);
720
+ impl_gt_eq_num!(gt_eq_i32, i32);
721
+ impl_gt_eq_num!(gt_eq_i64, i64);
722
+ impl_gt_eq_num!(gt_eq_f32, f32);
723
+ impl_gt_eq_num!(gt_eq_f64, f64);
724
+ // impl_gt_eq_num!(gt_eq_str, &str);
725
+
726
+ macro_rules! impl_lt_num {
727
+ ($name:ident, $type:ty) => {
728
+ impl RbSeries {
729
+ pub fn $name(&self, rhs: $type) -> RbResult<RbSeries> {
730
+ let s = self.series.borrow().lt(rhs).map_err(RbPolarsErr::from)?;
731
+ Ok(RbSeries::new(s.into_series()))
732
+ }
733
+ }
734
+ };
735
+ }
624
736
 
625
- pub fn round(&self, decimals: u32) -> RbResult<Self> {
626
- let s = self
627
- .series
628
- .borrow()
629
- .round(decimals)
630
- .map_err(RbPolarsErr::from)?;
631
- Ok(s.into())
632
- }
737
+ impl_lt_num!(lt_u8, u8);
738
+ impl_lt_num!(lt_u16, u16);
739
+ impl_lt_num!(lt_u32, u32);
740
+ impl_lt_num!(lt_u64, u64);
741
+ impl_lt_num!(lt_i8, i8);
742
+ impl_lt_num!(lt_i16, i16);
743
+ impl_lt_num!(lt_i32, i32);
744
+ impl_lt_num!(lt_i64, i64);
745
+ impl_lt_num!(lt_f32, f32);
746
+ impl_lt_num!(lt_f64, f64);
747
+ // impl_lt_num!(lt_str, &str);
748
+
749
+ macro_rules! impl_lt_eq_num {
750
+ ($name:ident, $type:ty) => {
751
+ impl RbSeries {
752
+ pub fn $name(&self, rhs: $type) -> RbResult<Self> {
753
+ let s = self.series.borrow().lt_eq(rhs).map_err(RbPolarsErr::from)?;
754
+ Ok(RbSeries::new(s.into_series()))
755
+ }
756
+ }
757
+ };
758
+ }
759
+
760
+ impl_lt_eq_num!(lt_eq_u8, u8);
761
+ impl_lt_eq_num!(lt_eq_u16, u16);
762
+ impl_lt_eq_num!(lt_eq_u32, u32);
763
+ impl_lt_eq_num!(lt_eq_u64, u64);
764
+ impl_lt_eq_num!(lt_eq_i8, i8);
765
+ impl_lt_eq_num!(lt_eq_i16, i16);
766
+ impl_lt_eq_num!(lt_eq_i32, i32);
767
+ impl_lt_eq_num!(lt_eq_i64, i64);
768
+ impl_lt_eq_num!(lt_eq_f32, f32);
769
+ impl_lt_eq_num!(lt_eq_f64, f64);
770
+ // impl_lt_eq_num!(lt_eq_str, &str);
771
+
772
+ pub fn to_series_collection(rs: RArray) -> RbResult<Vec<Series>> {
773
+ let mut series = Vec::new();
774
+ for item in rs.each() {
775
+ series.push(item?.try_convert::<&RbSeries>()?.series.borrow().clone());
776
+ }
777
+ Ok(series)
633
778
  }
634
779
 
635
780
  pub fn to_rbseries_collection(s: Vec<Series>) -> Vec<RbSeries> {
636
781
  s.into_iter().map(RbSeries::new).collect()
637
782
  }
783
+
784
+ impl RbSeries {
785
+ pub fn new_opt_date(name: String, values: RArray, _strict: Option<bool>) -> RbResult<Self> {
786
+ let len = values.len();
787
+ let mut builder = PrimitiveChunkedBuilder::<Int32Type>::new(&name, len);
788
+ for item in values.each() {
789
+ let v = item?;
790
+ if v.is_nil() {
791
+ builder.append_null();
792
+ } else {
793
+ // convert to DateTime for UTC
794
+ let v: Value = v.funcall("to_datetime", ())?;
795
+ let v: Value = v.funcall("to_time", ())?;
796
+ let v: Value = v.funcall("to_i", ())?;
797
+ // TODO use strict
798
+ builder.append_value(v.try_convert::<i32>()? / 86400);
799
+ }
800
+ }
801
+ let ca: ChunkedArray<Int32Type> = builder.finish();
802
+ Ok(ca.into_date().into_series().into())
803
+ }
804
+
805
+ pub fn new_opt_datetime(name: String, values: RArray, _strict: Option<bool>) -> RbResult<Self> {
806
+ let len = values.len();
807
+ let mut builder = PrimitiveChunkedBuilder::<Int64Type>::new(&name, len);
808
+ for item in values.each() {
809
+ let v = item?;
810
+ if v.is_nil() {
811
+ builder.append_null();
812
+ } else {
813
+ let sec: i64 = v.funcall("to_i", ())?;
814
+ let nsec: i64 = v.funcall("nsec", ())?;
815
+ // TODO use strict
816
+ builder.append_value(sec * 1_000_000_000 + nsec);
817
+ }
818
+ }
819
+ let ca: ChunkedArray<Int64Type> = builder.finish();
820
+ Ok(ca
821
+ .into_datetime(TimeUnit::Nanoseconds, None)
822
+ .into_series()
823
+ .into())
824
+ }
825
+ }
@@ -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
+ }
@@ -1,4 +1,5 @@
1
1
  module Polars
2
+ # @private
2
3
  class BatchedCsvReader
3
4
  attr_accessor :_reader, :new_columns
4
5
 
@@ -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
@@ -0,0 +1,54 @@
1
+ module Polars
2
+ # Series.cat namespace.
3
+ class CatNameSpace
4
+ include ExprDispatch
5
+
6
+ self._accessor = "cat"
7
+
8
+ # @private
9
+ def initialize(series)
10
+ self._s = series._s
11
+ end
12
+
13
+ # Determine how this categorical series should be sorted.
14
+ #
15
+ # @param ordering ["physical", "lexical"]
16
+ # Ordering type:
17
+ #
18
+ # - 'physical' -> Use the physical representation of the categories to
19
+ # determine the order (default).
20
+ # - 'lexical' -> Use the string values to determine the ordering.
21
+ #
22
+ # @return [Series]
23
+ #
24
+ # @example
25
+ # df = Polars::DataFrame.new(
26
+ # {"cats" => ["z", "z", "k", "a", "b"], "vals" => [3, 1, 2, 2, 3]}
27
+ # ).with_columns(
28
+ # [
29
+ # Polars.col("cats").cast(:cat).cat.set_ordering("lexical")
30
+ # ]
31
+ # )
32
+ # df.sort(["cats", "vals"])
33
+ # # =>
34
+ # # shape: (5, 2)
35
+ # # ┌──────┬──────┐
36
+ # # │ cats ┆ vals │
37
+ # # │ --- ┆ --- │
38
+ # # │ cat ┆ i64 │
39
+ # # ╞══════╪══════╡
40
+ # # │ a ┆ 2 │
41
+ # # ├╌╌╌╌╌╌┼╌╌╌╌╌╌┤
42
+ # # │ b ┆ 3 │
43
+ # # ├╌╌╌╌╌╌┼╌╌╌╌╌╌┤
44
+ # # │ k ┆ 2 │
45
+ # # ├╌╌╌╌╌╌┼╌╌╌╌╌╌┤
46
+ # # │ z ┆ 1 │
47
+ # # ├╌╌╌╌╌╌┼╌╌╌╌╌╌┤
48
+ # # │ z ┆ 3 │
49
+ # # └──────┴──────┘
50
+ def set_ordering(ordering)
51
+ super
52
+ end
53
+ end
54
+ end