polars-df 0.25.1 → 0.26.1

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -1
  3. data/Cargo.lock +278 -106
  4. data/Cargo.toml +0 -3
  5. data/LICENSE.txt +1 -1
  6. data/README.md +7 -3
  7. data/ext/polars/Cargo.toml +18 -18
  8. data/ext/polars/src/catalog/unity.rs +15 -20
  9. data/ext/polars/src/conversion/any_value.rs +25 -24
  10. data/ext/polars/src/conversion/chunked_array.rs +58 -56
  11. data/ext/polars/src/conversion/datetime.rs +58 -7
  12. data/ext/polars/src/conversion/mod.rs +205 -142
  13. data/ext/polars/src/dataframe/export.rs +15 -12
  14. data/ext/polars/src/dataframe/general.rs +5 -4
  15. data/ext/polars/src/dataframe/map.rs +6 -4
  16. data/ext/polars/src/error.rs +1 -1
  17. data/ext/polars/src/expr/array.rs +0 -24
  18. data/ext/polars/src/expr/datatype.rs +3 -2
  19. data/ext/polars/src/expr/datetime.rs +4 -4
  20. data/ext/polars/src/expr/general.rs +27 -15
  21. data/ext/polars/src/expr/list.rs +0 -26
  22. data/ext/polars/src/functions/business.rs +2 -2
  23. data/ext/polars/src/functions/io.rs +4 -3
  24. data/ext/polars/src/functions/lazy.rs +58 -46
  25. data/ext/polars/src/functions/meta.rs +6 -5
  26. data/ext/polars/src/functions/mod.rs +0 -1
  27. data/ext/polars/src/functions/utils.rs +4 -2
  28. data/ext/polars/src/interop/arrow/mod.rs +4 -2
  29. data/ext/polars/src/interop/arrow/to_rb.rs +17 -12
  30. data/ext/polars/src/interop/numo/to_numo_series.rs +26 -25
  31. data/ext/polars/src/io/scan_options.rs +6 -3
  32. data/ext/polars/src/io/sink_options.rs +2 -0
  33. data/ext/polars/src/lazyframe/general.rs +32 -16
  34. data/ext/polars/src/lazyframe/optflags.rs +2 -1
  35. data/ext/polars/src/lib.rs +21 -37
  36. data/ext/polars/src/map/lazy.rs +5 -2
  37. data/ext/polars/src/map/series.rs +19 -18
  38. data/ext/polars/src/on_startup.rs +16 -7
  39. data/ext/polars/src/ruby/capsule.rs +27 -0
  40. data/ext/polars/src/ruby/mod.rs +1 -0
  41. data/ext/polars/src/ruby/numo.rs +3 -4
  42. data/ext/polars/src/ruby/rb_modules.rs +2 -4
  43. data/ext/polars/src/ruby/ruby_udf.rs +7 -9
  44. data/ext/polars/src/ruby/utils.rs +12 -1
  45. data/ext/polars/src/series/aggregation.rs +13 -1
  46. data/ext/polars/src/series/export.rs +38 -38
  47. data/ext/polars/src/series/general.rs +4 -3
  48. data/ext/polars/src/series/map.rs +3 -2
  49. data/ext/polars/src/series/scatter.rs +4 -4
  50. data/ext/polars/src/utils.rs +31 -7
  51. data/lib/polars/array_expr.rb +23 -7
  52. data/lib/polars/array_name_space.rb +16 -2
  53. data/lib/polars/binary_name_space.rb +32 -0
  54. data/lib/polars/data_frame.rb +83 -11
  55. data/lib/polars/date_time_expr.rb +91 -3
  56. data/lib/polars/date_time_name_space.rb +7 -1
  57. data/lib/polars/expr.rb +122 -44
  58. data/lib/polars/functions/aggregation/horizontal.rb +4 -4
  59. data/lib/polars/functions/business.rb +2 -2
  60. data/lib/polars/functions/eager.rb +80 -7
  61. data/lib/polars/functions/lazy.rb +5 -2
  62. data/lib/polars/iceberg_dataset.rb +81 -14
  63. data/lib/polars/io/csv.rb +27 -5
  64. data/lib/polars/io/delta.rb +4 -0
  65. data/lib/polars/io/ipc.rb +1 -1
  66. data/lib/polars/io/lines.rb +4 -4
  67. data/lib/polars/io/sink_options.rb +4 -2
  68. data/lib/polars/lazy_frame.rb +97 -14
  69. data/lib/polars/list_expr.rb +21 -7
  70. data/lib/polars/list_name_space.rb +16 -2
  71. data/lib/polars/query_opt_flags.rb +22 -5
  72. data/lib/polars/schema.rb +9 -0
  73. data/lib/polars/selectors.rb +1 -1
  74. data/lib/polars/series.rb +106 -19
  75. data/lib/polars/sql_context.rb +2 -2
  76. data/lib/polars/string_cache.rb +19 -72
  77. data/lib/polars/string_expr.rb +1 -7
  78. data/lib/polars/string_name_space.rb +1 -7
  79. data/lib/polars/utils/construction/series.rb +8 -3
  80. data/lib/polars/utils/convert.rb +16 -6
  81. data/lib/polars/utils/parse.rb +7 -0
  82. data/lib/polars/utils/reduce_balanced.rb +43 -0
  83. data/lib/polars/utils/various.rb +5 -0
  84. data/lib/polars/version.rb +1 -1
  85. data/lib/polars.rb +5 -1
  86. metadata +4 -17
  87. data/ext/polars/src/functions/string_cache.rs +0 -24
@@ -226,6 +226,38 @@ module Polars
226
226
  super
227
227
  end
228
228
 
229
+ # Get the byte value at the given index.
230
+ #
231
+ # For example, index `0` would return the first byte of every binary value
232
+ # and index `-1` would return the last byte of every binary value.
233
+ # The behavior if an index is out of bounds is determined by the argument
234
+ # `null_on_oob`.
235
+ #
236
+ # @param index [Object]
237
+ # Index to return per binary value
238
+ # @param null_on_oob [Boolean]
239
+ # Behavior if an index is out of bounds:
240
+ #
241
+ # * true -> set as null
242
+ # * false -> raise an error
243
+ #
244
+ # @return [Series]
245
+ #
246
+ # @example
247
+ # s = Polars::Series.new("a", ["\x01\x02\x03".b, "".b, "\x04\x05".b])
248
+ # s.bin.get(0, null_on_oob: true)
249
+ # # =>
250
+ # # shape: (3,)
251
+ # # Series: 'a' [u8]
252
+ # # [
253
+ # # 1
254
+ # # null
255
+ # # 4
256
+ # # ]
257
+ def get(index, null_on_oob: false)
258
+ super
259
+ end
260
+
229
261
  # Take the first `n` bytes of the binary values.
230
262
  #
231
263
  # @param n [Object]
@@ -771,7 +771,14 @@ module Polars
771
771
  s.get_index_signed(row)
772
772
  end
773
773
 
774
- # no to_arrow
774
+ # Collect the underlying arrow arrays in an Arrow Table.
775
+ #
776
+ # @return [Nanoarrow::Array]
777
+ def to_arrow
778
+ require "nanoarrow"
779
+
780
+ Nanoarrow::Array.new(self)
781
+ end
775
782
 
776
783
  # Convert DataFrame to a hash mapping column name to values.
777
784
  #
@@ -810,6 +817,8 @@ module Polars
810
817
  # df.to_numo.class
811
818
  # # => Numo::RObject
812
819
  def to_numo
820
+ require "numo/narray"
821
+
813
822
  out = _df.to_numo
814
823
  if out.nil?
815
824
  Numo::NArray.vstack(width.times.map { |i| to_series(i).to_numo }).transpose
@@ -3808,6 +3817,57 @@ module Polars
3808
3817
  .collect(optimizations: QueryOptFlags._eager)
3809
3818
  end
3810
3819
 
3820
+ # Selects rows from this DataFrame at the given indices.
3821
+ #
3822
+ # @note
3823
+ # This functionality is experimental. It may be
3824
+ # changed at any point without it being considered a breaking change.
3825
+ #
3826
+ # @param indices [Object]
3827
+ # The indices of the rows to select.
3828
+ # @param null_on_oob [Boolean]
3829
+ # If true when an index is out-of-bounds a null row will be generated
3830
+ # instead of raising an error.
3831
+ #
3832
+ # @return [DataFrame]
3833
+ #
3834
+ # @example
3835
+ # df = Polars::DataFrame.new({"x" => [2, 1, 0], "s" => ["foo", "bar", "baz"]})
3836
+ # df.gather([2, 0, 0])
3837
+ # # =>
3838
+ # # shape: (3, 2)
3839
+ # # ┌─────┬─────┐
3840
+ # # │ x ┆ s │
3841
+ # # │ --- ┆ --- │
3842
+ # # │ i64 ┆ str │
3843
+ # # ╞═════╪═════╡
3844
+ # # │ 0 ┆ baz │
3845
+ # # │ 2 ┆ foo │
3846
+ # # │ 2 ┆ foo │
3847
+ # # └─────┴─────┘
3848
+ #
3849
+ # @example
3850
+ # df.gather([0, 10, 1], null_on_oob: true)
3851
+ # # =>
3852
+ # # shape: (3, 2)
3853
+ # # ┌──────┬──────┐
3854
+ # # │ x ┆ s │
3855
+ # # │ --- ┆ --- │
3856
+ # # │ i64 ┆ str │
3857
+ # # ╞══════╪══════╡
3858
+ # # │ 2 ┆ foo │
3859
+ # # │ null ┆ null │
3860
+ # # │ 1 ┆ bar │
3861
+ # # └──────┴──────┘
3862
+ def gather(
3863
+ indices,
3864
+ null_on_oob: false
3865
+ )
3866
+ lazy
3867
+ .gather(indices, null_on_oob: null_on_oob)
3868
+ .collect(optimizations: QueryOptFlags._eager)
3869
+ end
3870
+
3811
3871
  # Apply a custom/user-defined function (UDF) over the rows of the DataFrame.
3812
3872
  #
3813
3873
  # The UDF will receive each row as a tuple of values: `udf(row)`.
@@ -4527,6 +4587,8 @@ module Polars
4527
4587
  # @param separator [String]
4528
4588
  # Used as separator/delimiter in generated column names in case of multiple
4529
4589
  # `values` columns.
4590
+ # @param column_naming ['auto', 'combine']
4591
+ # How resulting column names will be constructed.
4530
4592
  #
4531
4593
  # @return [DataFrame]
4532
4594
  #
@@ -4557,7 +4619,8 @@ module Polars
4557
4619
  aggregate_function: nil,
4558
4620
  maintain_order: true,
4559
4621
  sort_columns: false,
4560
- separator: "_"
4622
+ separator: "_",
4623
+ column_naming: "auto"
4561
4624
  )
4562
4625
  if on_columns.nil?
4563
4626
  cols = select(on).unique(maintain_order: true)
@@ -4577,7 +4640,8 @@ module Polars
4577
4640
  values: values,
4578
4641
  aggregate_function: aggregate_function,
4579
4642
  maintain_order: maintain_order,
4580
- separator: separator
4643
+ separator: separator,
4644
+ column_naming: column_naming
4581
4645
  )
4582
4646
  .collect(optimizations: QueryOptFlags._eager)
4583
4647
  end
@@ -4593,7 +4657,8 @@ module Polars
4593
4657
  #
4594
4658
  # @param on [Object]
4595
4659
  # Column(s) or selector(s) to use as values variables; if `on`
4596
- # is empty all columns that are not in `index` will be used.
4660
+ # is empty no columns will be used. If set to `nil` (default)
4661
+ # all columns that are not in `index` will be used.
4597
4662
  # @param index [Object]
4598
4663
  # Column(s) or selector(s) to use as identifier variables.
4599
4664
  # @param variable_name [Object]
@@ -4627,7 +4692,7 @@ module Polars
4627
4692
  # # │ z ┆ c ┆ 6 │
4628
4693
  # # └─────┴──────────┴───────┘
4629
4694
  def unpivot(on = nil, index: nil, variable_name: nil, value_name: nil)
4630
- on = on.nil? ? [] : Utils._expand_selectors(self, on)
4695
+ on = on.nil? ? nil : Utils._expand_selectors(self, on)
4631
4696
  index = index.nil? ? [] : Utils._expand_selectors(self, index)
4632
4697
 
4633
4698
  _from_rbdf(_df.unpivot(on, index, value_name, variable_name))
@@ -6488,7 +6553,7 @@ module Polars
6488
6553
  # # │ foo ┆ 1 ┆ a ┆ true ┆ [1, 2] ┆ baz │
6489
6554
  # # │ bar ┆ 2 ┆ b ┆ null ┆ [3] ┆ womp │
6490
6555
  # # └────────┴─────┴─────┴──────┴───────────┴───────┘
6491
- def unnest(columns, *more_columns, separator: nil)
6556
+ def unnest(columns = nil, *more_columns, separator: nil)
6492
6557
  lazy.unnest(columns, *more_columns, separator: separator).collect(optimizations: QueryOptFlags._eager)
6493
6558
  end
6494
6559
 
@@ -6504,6 +6569,10 @@ module Polars
6504
6569
  # Other DataFrame that must be merged
6505
6570
  # @param key [String]
6506
6571
  # Key that is sorted.
6572
+ # @param maintain_order [Boolean]
6573
+ # If `true`, the output is guaranteed to have left-biased ordering
6574
+ # for equal keys: rows from the left frame appear before rows from
6575
+ # the right frame when their keys are equal.
6507
6576
  #
6508
6577
  # @return [DataFrame]
6509
6578
  #
@@ -6530,8 +6599,8 @@ module Polars
6530
6599
  # # │ steve ┆ 42 │
6531
6600
  # # │ elise ┆ 44 │
6532
6601
  # # └────────┴─────┘
6533
- def merge_sorted(other, key)
6534
- lazy.merge_sorted(other.lazy, key).collect(optimizations: QueryOptFlags._eager)
6602
+ def merge_sorted(other, key, maintain_order: false)
6603
+ lazy.merge_sorted(other.lazy, key, maintain_order: maintain_order).collect(optimizations: QueryOptFlags._eager)
6535
6604
  end
6536
6605
 
6537
6606
  # Flag a column as sorted.
@@ -6545,14 +6614,17 @@ module Polars
6545
6614
  # Column that is sorted.
6546
6615
  # @param descending [Boolean]
6547
6616
  # Whether the column is sorted in descending order.
6617
+ # @param nulls_last [Boolean]
6618
+ # Whether the nulls are at the end.
6548
6619
  #
6549
6620
  # @return [DataFrame]
6550
6621
  def set_sorted(
6551
6622
  column,
6552
- descending: false
6623
+ descending: false,
6624
+ nulls_last: false
6553
6625
  )
6554
6626
  lazy
6555
- .set_sorted(column, descending: descending)
6627
+ .set_sorted(column, descending: descending, nulls_last: nulls_last)
6556
6628
  .collect(optimizations: QueryOptFlags._eager)
6557
6629
  end
6558
6630
 
@@ -7111,7 +7183,7 @@ module Polars
7111
7183
  end
7112
7184
 
7113
7185
  def _select_rows_by_slice(df, key)
7114
- return Slice.new(df).apply(key)
7186
+ Slice.new(df).apply(key)
7115
7187
  end
7116
7188
 
7117
7189
  def _select_rows_by_index(df, key)
@@ -22,6 +22,8 @@ module Polars
22
22
  # Which days of the week to count. The default is Monday to Friday.
23
23
  # If you wanted to count only Monday to Thursday, you would pass
24
24
  # `[true, true, true, true, false, false, false]`.
25
+ # @param holidays [Object]
26
+ # Holidays to exclude from the count.
25
27
  # @param roll
26
28
  # What to do when the start date lands on a non-business day. Options are:
27
29
  #
@@ -44,17 +46,67 @@ module Polars
44
46
  # # │ 2020-01-01 ┆ 2020-01-08 │
45
47
  # # │ 2020-01-02 ┆ 2020-01-09 │
46
48
  # # └────────────┴────────────┘
49
+ #
50
+ # @example You can pass a custom weekend - for example, if you only take Sunday off:
51
+ # week_mask = [true, true, true, true, true, true, false]
52
+ # df.with_columns(
53
+ # result: Polars.col("start").dt.add_business_days(5, week_mask: week_mask)
54
+ # )
55
+ # # =>
56
+ # # shape: (2, 2)
57
+ # # ┌────────────┬────────────┐
58
+ # # │ start ┆ result │
59
+ # # │ --- ┆ --- │
60
+ # # │ date ┆ date │
61
+ # # ╞════════════╪════════════╡
62
+ # # │ 2020-01-01 ┆ 2020-01-07 │
63
+ # # │ 2020-01-02 ┆ 2020-01-08 │
64
+ # # └────────────┴────────────┘
65
+ #
66
+ # @example You can also pass a list of holidays:
67
+ # holidays = [Date.new(2020, 1, 3), Date.new(2020, 1, 6)]
68
+ # df.with_columns(
69
+ # result: Polars.col("start").dt.add_business_days(5, holidays: holidays)
70
+ # )
71
+ # # =>
72
+ # # shape: (2, 2)
73
+ # # ┌────────────┬────────────┐
74
+ # # │ start ┆ result │
75
+ # # │ --- ┆ --- │
76
+ # # │ date ┆ date │
77
+ # # ╞════════════╪════════════╡
78
+ # # │ 2020-01-01 ┆ 2020-01-10 │
79
+ # # │ 2020-01-02 ┆ 2020-01-13 │
80
+ # # └────────────┴────────────┘
81
+ #
82
+ # @example Roll all dates forwards to the next business day:
83
+ # df = Polars::DataFrame.new({"start" => [Date.new(2020, 1, 5), Date.new(2020, 1, 6)]})
84
+ # df.with_columns(
85
+ # rolled_forwards: Polars.col("start").dt.add_business_days(0, roll: "forward")
86
+ # )
87
+ # # =>
88
+ # # shape: (2, 2)
89
+ # # ┌────────────┬─────────────────┐
90
+ # # │ start ┆ rolled_forwards │
91
+ # # │ --- ┆ --- │
92
+ # # │ date ┆ date │
93
+ # # ╞════════════╪═════════════════╡
94
+ # # │ 2020-01-05 ┆ 2020-01-06 │
95
+ # # │ 2020-01-06 ┆ 2020-01-06 │
96
+ # # └────────────┴─────────────────┘
47
97
  def add_business_days(
48
98
  n,
49
99
  week_mask: [true, true, true, true, true, false, false],
100
+ holidays: [],
50
101
  roll: "raise"
51
102
  )
52
103
  n_rbexpr = Utils.parse_into_expression(n)
104
+ holidays_rbexpr = Utils._holidays_to_expr(holidays)
53
105
  Utils.wrap_expr(
54
106
  _rbexpr.dt_add_business_days(
55
107
  n_rbexpr,
56
108
  week_mask,
57
- [],
109
+ holidays_rbexpr,
58
110
  roll
59
111
  )
60
112
  )
@@ -580,6 +632,8 @@ module Polars
580
632
  # Which days of the week to count. The default is Monday to Friday.
581
633
  # If you wanted to count only Monday to Thursday, you would pass
582
634
  # `[true, true, true, true, false, false, false]`.
635
+ # @param holidays [Object]
636
+ # Holidays to exclude from the count.
583
637
  #
584
638
  # @return [Expr]
585
639
  #
@@ -596,13 +650,47 @@ module Polars
596
650
  # # │ 2020-01-03 ┆ true │
597
651
  # # │ 2020-01-05 ┆ false │
598
652
  # # └────────────┴─────────────────┘
653
+ #
654
+ # @example You can pass a custom weekend - for example, if you only take Sunday off:
655
+ # week_mask = [true, true, true, true, true, true, false]
656
+ # df.with_columns(
657
+ # is_business_day: Polars.col("start").dt.is_business_day(week_mask: week_mask)
658
+ # )
659
+ # # =>
660
+ # # shape: (2, 2)
661
+ # # ┌────────────┬─────────────────┐
662
+ # # │ start ┆ is_business_day │
663
+ # # │ --- ┆ --- │
664
+ # # │ date ┆ bool │
665
+ # # ╞════════════╪═════════════════╡
666
+ # # │ 2020-01-03 ┆ true │
667
+ # # │ 2020-01-05 ┆ false │
668
+ # # └────────────┴─────────────────┘
669
+ #
670
+ # @example You can also pass a list of holidays:
671
+ # holidays = [Date.new(2020, 1, 3), Date.new(2020, 1, 6)]
672
+ # df.with_columns(
673
+ # is_business_day: Polars.col("start").dt.is_business_day(holidays: holidays)
674
+ # )
675
+ # # =>
676
+ # # shape: (2, 2)
677
+ # # ┌────────────┬─────────────────┐
678
+ # # │ start ┆ is_business_day │
679
+ # # │ --- ┆ --- │
680
+ # # │ date ┆ bool │
681
+ # # ╞════════════╪═════════════════╡
682
+ # # │ 2020-01-03 ┆ false │
683
+ # # │ 2020-01-05 ┆ false │
684
+ # # └────────────┴─────────────────┘
599
685
  def is_business_day(
600
- week_mask: [true, true, true, true, true, false, false]
686
+ week_mask: [true, true, true, true, true, false, false],
687
+ holidays: []
601
688
  )
689
+ holidays_rbexpr = Utils._holidays_to_expr(holidays)
602
690
  Utils.wrap_expr(
603
691
  _rbexpr.dt_is_business_day(
604
692
  week_mask,
605
- []
693
+ holidays_rbexpr
606
694
  )
607
695
  )
608
696
  end
@@ -31,6 +31,8 @@ module Polars
31
31
  # Which days of the week to count. The default is Monday to Friday.
32
32
  # If you wanted to count only Monday to Thursday, you would pass
33
33
  # `[true, true, true, true, false, false, false]`.
34
+ # @param holidays [Object]
35
+ # Holidays to exclude from the count.
34
36
  # roll
35
37
  # What to do when the start date lands on a non-business day. Options are:
36
38
  #
@@ -75,6 +77,7 @@ module Polars
75
77
  def add_business_days(
76
78
  n,
77
79
  week_mask: [true, true, true, true, true, false, false],
80
+ holidays: [],
78
81
  roll: "raise"
79
82
  )
80
83
  super
@@ -263,6 +266,8 @@ module Polars
263
266
  # Which days of the week to count. The default is Monday to Friday.
264
267
  # If you wanted to count only Monday to Thursday, you would pass
265
268
  # `[true, true, true, true, false, false, false]`.
269
+ # @param holidays [Object]
270
+ # Holidays to exclude from the count.
266
271
  #
267
272
  # @return [Series]
268
273
  #
@@ -288,7 +293,8 @@ module Polars
288
293
  # # false
289
294
  # # ]
290
295
  def is_business_day(
291
- week_mask: [true, true, true, true, true, false, false]
296
+ week_mask: [true, true, true, true, true, false, false],
297
+ holidays: []
292
298
  )
293
299
  super
294
300
  end
data/lib/polars/expr.rb CHANGED
@@ -252,6 +252,38 @@ module Polars
252
252
  wrap_expr(_rbexpr.all(ignore_nulls))
253
253
  end
254
254
 
255
+ # Return whether the column is empty.
256
+ #
257
+ # @note
258
+ # This functionality is considered **unstable**. It may be changed
259
+ # at any point without it being considered a breaking change.
260
+ #
261
+ # @param ignore_nulls [Boolean]
262
+ # If true a column containing only nulls will also be considered empty.
263
+ # The default is false.
264
+ #
265
+ # @return [Expr]
266
+ #
267
+ # @example
268
+ # df = Polars::DataFrame.new({"x" => [nil, nil]})
269
+ # df.select(
270
+ # a: Polars.col("x").is_empty,
271
+ # b: Polars.col("x").drop_nulls.is_empty,
272
+ # c: Polars.col("x").is_empty(ignore_nulls: true)
273
+ # )
274
+ # # =>
275
+ # # shape: (1, 3)
276
+ # # ┌───────┬──────┬──────┐
277
+ # # │ a ┆ b ┆ c │
278
+ # # │ --- ┆ --- ┆ --- │
279
+ # # │ bool ┆ bool ┆ bool │
280
+ # # ╞═══════╪══════╪══════╡
281
+ # # │ false ┆ true ┆ true │
282
+ # # └───────┴──────┴──────┘
283
+ def is_empty(ignore_nulls: false)
284
+ wrap_expr(_rbexpr.is_empty(ignore_nulls))
285
+ end
286
+
255
287
  # Return indices where expression evaluates `true`.
256
288
  #
257
289
  # @return [Expr]
@@ -1204,6 +1236,49 @@ module Polars
1204
1236
  wrap_expr(_rbexpr.round_sig_figs(digits))
1205
1237
  end
1206
1238
 
1239
+ # Truncate numeric data toward zero to `decimals` number of decimal places.
1240
+ #
1241
+ # @param decimals [Integer]
1242
+ # Number of decimal places to truncate to.
1243
+ #
1244
+ # @return [Expr]
1245
+ #
1246
+ # @note
1247
+ # Truncation discards the fractional part beyond the given number of decimals.
1248
+ # For example, when rounding to 0 decimals 0.25, -0.25, 0.99, and -0.99 will
1249
+ # all round to 0. When rounding to 1 decimal 1.9999 rounds to 1.9 and -1.9999
1250
+ # rounds to -1.9. There is no tiebreak behaviour at midpoint values as there
1251
+ # is with :meth:`round` so 0.5 and -0.5 will also round to 0 when decimals=1.
1252
+ #
1253
+ # @note
1254
+ # This method performs numeric truncation. For truncating temporal
1255
+ # data (dates/datetimes), use :func:`Expr.dt.truncate` instead.
1256
+ #
1257
+ # @example
1258
+ # df = Polars::DataFrame.new({"n" => [-9.9999, 0.12345, 1.0251, 8.8765]})
1259
+ # df.with_columns(
1260
+ # t0: Polars.col("n").truncate(0),
1261
+ # t1: Polars.col("n").truncate(1),
1262
+ # t2: Polars.col("n").truncate(2),
1263
+ # t3: Polars.col("n").truncate(3),
1264
+ # t4: Polars.col("n").truncate(4)
1265
+ # )
1266
+ # # =>
1267
+ # # shape: (4, 6)
1268
+ # # ┌─────────┬──────┬──────┬───────┬────────┬─────────┐
1269
+ # # │ n ┆ t0 ┆ t1 ┆ t2 ┆ t3 ┆ t4 │
1270
+ # # │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
1271
+ # # │ f64 ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ f64 │
1272
+ # # ╞═════════╪══════╪══════╪═══════╪════════╪═════════╡
1273
+ # # │ -9.9999 ┆ -9.0 ┆ -9.9 ┆ -9.99 ┆ -9.999 ┆ -9.9999 │
1274
+ # # │ 0.12345 ┆ 0.0 ┆ 0.1 ┆ 0.12 ┆ 0.123 ┆ 0.1234 │
1275
+ # # │ 1.0251 ┆ 1.0 ┆ 1.0 ┆ 1.02 ┆ 1.025 ┆ 1.025 │
1276
+ # # │ 8.8765 ┆ 8.0 ┆ 8.8 ┆ 8.87 ┆ 8.876 ┆ 8.8765 │
1277
+ # # └─────────┴──────┴──────┴───────┴────────┴─────────┘
1278
+ def truncate(decimals = 0)
1279
+ Utils.wrap_expr(_rbexpr.truncate(decimals))
1280
+ end
1281
+
1207
1282
  # Compute the dot/inner product between two Expressions.
1208
1283
  #
1209
1284
  # @param other [Expr]
@@ -1887,6 +1962,11 @@ module Polars
1887
1962
  #
1888
1963
  # @param indices [Expr]
1889
1964
  # An expression that leads to a `:u32` dtyped Series.
1965
+ # @param null_on_oob [Boolean]
1966
+ # Behavior if an index is out of bounds:
1967
+ #
1968
+ # - true -> set the result to null
1969
+ # - false -> raise an error
1890
1970
  #
1891
1971
  # @return [Expr]
1892
1972
  #
@@ -1915,13 +1995,13 @@ module Polars
1915
1995
  # # │ one ┆ [2, 98] │
1916
1996
  # # │ two ┆ [4, 99] │
1917
1997
  # # └───────┴───────────┘
1918
- def gather(indices)
1998
+ def gather(indices, null_on_oob: false)
1919
1999
  if indices.is_a?(::Array)
1920
2000
  indices_lit_rbexpr = Polars.lit(Series.new("", indices, dtype: Int64))._rbexpr
1921
2001
  else
1922
2002
  indices_lit_rbexpr = Utils.parse_into_expression(indices)
1923
2003
  end
1924
- wrap_expr(_rbexpr.gather(indices_lit_rbexpr))
2004
+ wrap_expr(_rbexpr.gather(indices_lit_rbexpr, null_on_oob))
1925
2005
  end
1926
2006
 
1927
2007
  # Return a single value by index.
@@ -3195,17 +3275,17 @@ module Polars
3195
3275
  # )
3196
3276
  # # =>
3197
3277
  # # shape: (5, 2)
3198
- # # ┌─────┬─────┐
3199
- # # │ foo ┆ cut
3200
- # # │ --- ┆ ---
3201
- # # │ i64 ┆ cat
3202
- # # ╞═════╪═════╡
3203
- # # │ -2 ┆ a
3204
- # # │ -1 ┆ a
3205
- # # │ 0 ┆ b
3206
- # # │ 1 ┆ b
3207
- # # │ 2 ┆ c
3208
- # # └─────┴─────┘
3278
+ # # ┌─────┬──────┐
3279
+ # # │ foo ┆ cut
3280
+ # # │ --- ┆ ---
3281
+ # # │ i64 ┆ enum
3282
+ # # ╞═════╪══════╡
3283
+ # # │ -2 ┆ a
3284
+ # # │ -1 ┆ a
3285
+ # # │ 0 ┆ b
3286
+ # # │ 1 ┆ b
3287
+ # # │ 2 ┆ c
3288
+ # # └─────┴──────┘
3209
3289
  #
3210
3290
  # @example Add both the category and the breakpoint.
3211
3291
  # df.with_columns(
@@ -3216,7 +3296,7 @@ module Polars
3216
3296
  # # ┌─────┬────────────┬────────────┐
3217
3297
  # # │ foo ┆ breakpoint ┆ category │
3218
3298
  # # │ --- ┆ --- ┆ --- │
3219
- # # │ i64 ┆ f64 ┆ cat
3299
+ # # │ i64 ┆ f64 ┆ enum
3220
3300
  # # ╞═════╪════════════╪════════════╡
3221
3301
  # # │ -2 ┆ -1.0 ┆ (-inf, -1] │
3222
3302
  # # │ -1 ┆ -1.0 ┆ (-inf, -1] │
@@ -4566,13 +4646,18 @@ module Polars
4566
4646
  wrap_expr(_rbexpr._hash(k0, k1, k2, k3))
4567
4647
  end
4568
4648
 
4569
- # Reinterpret the underlying bits as a signed/unsigned integer.
4649
+ # Reinterpret the underlying bits as a signed/unsigned integer or float.
4650
+ #
4651
+ # This operation is only allowed for numeric types of the same size.
4652
+ # For lower bits numbers, you can safely use the cast operation.
4570
4653
  #
4571
- # This operation is only allowed for 64bit integers. For lower bits integers,
4572
- # you can safely use that cast operation.
4654
+ # Either `signed` or `dtype` can be specified.
4573
4655
  #
4574
4656
  # @param signed [Boolean]
4575
- # If true, reinterpret as `Polars::Int64`. Otherwise, reinterpret as `Polars::UInt64`.
4657
+ # If true, reinterpret as signed integer. Otherwise, reinterpret
4658
+ # as unsigned integer.
4659
+ # @param dtype [Object]
4660
+ # DataType to reinterpret to.
4576
4661
  #
4577
4662
  # @return [Expr]
4578
4663
  #
@@ -4581,7 +4666,7 @@ module Polars
4581
4666
  # df = Polars::DataFrame.new([s])
4582
4667
  # df.select(
4583
4668
  # [
4584
- # Polars.col("a").reinterpret(signed: true).alias("reinterpreted"),
4669
+ # Polars.col("a").reinterpret(dtype: Polars::Int64).alias("reinterpreted"),
4585
4670
  # Polars.col("a").alias("original")
4586
4671
  # ]
4587
4672
  # )
@@ -4596,14 +4681,13 @@ module Polars
4596
4681
  # # │ 1 ┆ 1 │
4597
4682
  # # │ 2 ┆ 2 │
4598
4683
  # # └───────────────┴──────────┘
4599
- def reinterpret(signed: nil)
4600
- # TODO update
4601
- if signed.nil?
4602
- warn "The default `signed` for `reinterpret` method will change from `false` to `true` in a future version"
4603
- signed = false
4684
+ def reinterpret(signed: nil, dtype: nil)
4685
+ if signed.nil? == dtype.nil?
4686
+ msg = "reinterpret requires exactly one of `signed` or `dtype` to be specified"
4687
+ raise ArgumentError, msg
4604
4688
  end
4605
4689
 
4606
- wrap_expr(_rbexpr.reinterpret(signed))
4690
+ wrap_expr(_rbexpr.reinterpret(signed, dtype))
4607
4691
  end
4608
4692
 
4609
4693
  # Print the value that this expression evaluates to and pass on the value.
@@ -5175,7 +5259,7 @@ module Polars
5175
5259
  def rolling_sum_by(
5176
5260
  by,
5177
5261
  window_size,
5178
- min_samples: 1,
5262
+ min_samples: 0,
5179
5263
  closed: "right"
5180
5264
  )
5181
5265
  window_size = _prepare_rolling_by_window_args(window_size)
@@ -7362,17 +7446,11 @@ module Polars
7362
7446
  # # └─────┘
7363
7447
  def sample(
7364
7448
  fraction: nil,
7365
- with_replacement: nil,
7449
+ with_replacement: false,
7366
7450
  shuffle: false,
7367
7451
  seed: nil,
7368
7452
  n: nil
7369
7453
  )
7370
- # TODO update
7371
- if with_replacement.nil?
7372
- warn "The default `with_replacement` for `sample` method will change from `true` to `false` in a future version"
7373
- with_replacement = true
7374
- end
7375
-
7376
7454
  if !n.nil? && !fraction.nil?
7377
7455
  raise ArgumentError, "cannot specify both `n` and `fraction`"
7378
7456
  end
@@ -7761,13 +7839,7 @@ module Polars
7761
7839
  # # ╞═══════════╡
7762
7840
  # # │ -6.754888 │
7763
7841
  # # └───────────┘
7764
- def entropy(base: nil, normalize: true)
7765
- # TODO update (including param docs)
7766
- if base.nil?
7767
- warn "The default `base` for `entropy` method will change from `2` to `Math::E` in a future version"
7768
- base = 2
7769
- end
7770
-
7842
+ def entropy(base: Math::E, normalize: true)
7771
7843
  wrap_expr(_rbexpr.entropy(base, normalize))
7772
7844
  end
7773
7845
 
@@ -7823,6 +7895,8 @@ module Polars
7823
7895
  #
7824
7896
  # @param descending [Boolean]
7825
7897
  # Whether the `Series` order is descending.
7898
+ # @param nulls_last [Boolean]
7899
+ # Whether the nulls are at the end.
7826
7900
  #
7827
7901
  # @return [Expr]
7828
7902
  #
@@ -7842,12 +7916,16 @@ module Polars
7842
7916
  # # ╞════════╡
7843
7917
  # # │ 3 │
7844
7918
  # # └────────┘
7845
- def set_sorted(descending: false)
7846
- wrap_expr(_rbexpr.set_sorted_flag(descending))
7919
+ def set_sorted(descending: false, nulls_last: false)
7920
+ wrap_expr(_rbexpr.set_sorted_flag(descending, nulls_last))
7847
7921
  end
7848
7922
 
7849
7923
  # Aggregate to list.
7850
7924
  #
7925
+ # @param maintain_order [Boolean]
7926
+ # Whether to preserve the order of elements in the list. Setting this
7927
+ # to `false` can improve performance, especially within `group_by`.
7928
+ #
7851
7929
  # @return [Expr]
7852
7930
  #
7853
7931
  # @example
@@ -7867,8 +7945,8 @@ module Polars
7867
7945
  # # ╞═══════════╪═══════════╡
7868
7946
  # # │ [1, 2, 3] ┆ [4, 5, 6] │
7869
7947
  # # └───────────┴───────────┘
7870
- def implode
7871
- wrap_expr(_rbexpr.implode)
7948
+ def implode(maintain_order: true)
7949
+ wrap_expr(_rbexpr.implode(maintain_order))
7872
7950
  end
7873
7951
 
7874
7952
  # Bin values into buckets and count their occurrences.