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
@@ -242,10 +242,10 @@ module Polars
242
242
  rbexprs = Utils.parse_into_list_of_expressions(*exprs)
243
243
  exprs_wrapped = rbexprs.map { |e| Utils.wrap_expr(e) }
244
244
 
245
- # (Expr): use u32 as that will not cast to float as eagerly
246
- Polars.cum_fold(Polars.lit(0).cast(UInt32), exprs_wrapped) { |a, b| a + b }.alias(
247
- "cum_sum"
248
- )
245
+ Polars.cum_fold(
246
+ Polars.lit(0).cast(Polars.dtype_of(Polars.sum_horizontal(exprs))),
247
+ exprs_wrapped
248
+ ) { |a, b| a + b }.alias("cum_sum")
249
249
  end
250
250
  end
251
251
  end
@@ -81,13 +81,13 @@ module Polars
81
81
  )
82
82
  start_rbexpr = Utils.parse_into_expression(start)
83
83
  end_rbexpr = Utils.parse_into_expression(stop)
84
- unix_epoch = ::Date.new(1970, 1, 1)
84
+ holidays_rbexpr = Utils._holidays_to_expr(holidays)
85
85
  Utils.wrap_expr(
86
86
  Plr.business_day_count(
87
87
  start_rbexpr,
88
88
  end_rbexpr,
89
89
  week_mask,
90
- holidays.map { |holiday| holiday - unix_epoch }
90
+ holidays_rbexpr
91
91
  )
92
92
  )
93
93
  end
@@ -399,6 +399,85 @@ module Polars
399
399
  out
400
400
  end
401
401
 
402
+ # Merge multiple sorted DataFrames or LazyFrames by the sorted key.
403
+ #
404
+ # The output of this operation will also be sorted.
405
+ # It is the callers responsibility that the frames
406
+ # are sorted in ascending order by that key otherwise
407
+ # the output will not make sense.
408
+ #
409
+ # @note
410
+ # This functionality is considered **unstable**. It may be changed
411
+ # at any point without it being considered a breaking change.
412
+ #
413
+ # @param items [Array]
414
+ # DataFrames or LazyFrames to merge.
415
+ # @param key [String]
416
+ # Key that is sorted.
417
+ # @param maintain_order [Boolean]
418
+ # If `true`, the output is guaranteed to have left-biased ordering
419
+ # for equal keys: rows from the left frame appear before rows from
420
+ # the right frame when their keys are equal.
421
+ #
422
+ # @return [Object]
423
+ #
424
+ # @example
425
+ # df0 = Polars::DataFrame.new(
426
+ # {"name" => ["steve", "elise", "bob"], "age" => [42, 44, 18]}
427
+ # ).sort("age")
428
+ # df1 = Polars::DataFrame.new(
429
+ # {"name" => ["anna", "megan", "steve", "thomas"], "age" => [21, 33, 17, 20]}
430
+ # ).sort("age")
431
+ # df2 = Polars::DataFrame.new({"name" => ["ida", "maya"], "age" => [37, 27]}).sort("age")
432
+ # Polars.merge_sorted([df0, df1, df2], "age")
433
+ # # =>
434
+ # # shape: (9, 2)
435
+ # # ┌────────┬─────┐
436
+ # # │ name ┆ age │
437
+ # # │ --- ┆ --- │
438
+ # # │ str ┆ i64 │
439
+ # # ╞════════╪═════╡
440
+ # # │ steve ┆ 17 │
441
+ # # │ bob ┆ 18 │
442
+ # # │ thomas ┆ 20 │
443
+ # # │ anna ┆ 21 │
444
+ # # │ maya ┆ 27 │
445
+ # # │ megan ┆ 33 │
446
+ # # │ ida ┆ 37 │
447
+ # # │ steve ┆ 42 │
448
+ # # │ elise ┆ 44 │
449
+ # # └────────┴─────┘
450
+ def merge_sorted(
451
+ items,
452
+ key,
453
+ maintain_order: false
454
+ )
455
+ elems = items.to_a
456
+
457
+ if elems.empty?
458
+ msg = "cannot merge_sort empty list"
459
+ raise ArgumentError, msg
460
+ end
461
+ if elems.length == 1 && (elems[0].is_a?(DataFrame) || elems[0].is_a?(LazyFrame))
462
+ return elems[0]
463
+ end
464
+
465
+ if !Utils.is_non_empty_sequence_of(elems, DataFrame) && !Utils.is_non_empty_sequence_of(elems, LazyFrame)
466
+ msg = "merge_sorted is not supported for #{elems[0].inspect}"
467
+ raise TypeError, msg
468
+ end
469
+
470
+ frames = elems.map { |df| df.lazy }
471
+
472
+ reduce_fn = lambda do |x, y|
473
+ x.merge_sorted(y, key, maintain_order: maintain_order)
474
+ end
475
+
476
+ lf = Utils.reduce_balanced(reduce_fn, frames)
477
+ eager = elems[0].is_a?(DataFrame)
478
+ eager ? lf.collect : lf
479
+ end
480
+
402
481
  # Align an array of frames using the unique values from one or more columns as a key.
403
482
  #
404
483
  # Frames that do not contain the given key values have rows injected (with nulls
@@ -464,16 +543,10 @@ module Polars
464
543
  def align_frames(
465
544
  *frames,
466
545
  on:,
467
- how: nil,
546
+ how: "full",
468
547
  select: nil,
469
548
  descending: false
470
549
  )
471
- # TODO update
472
- if how.nil?
473
- warn "The default `how` for `align_frames` method will change from `left` to `full` in a future version"
474
- how = "left"
475
- end
476
-
477
550
  if frames.empty?
478
551
  return []
479
552
  elsif frames.map(&:class).uniq.length != 1
@@ -141,6 +141,9 @@ module Polars
141
141
  #
142
142
  # @param columns [Array]
143
143
  # One or more column names.
144
+ # @param maintain_order [Boolean]
145
+ # Whether to preserve the order of elements in the list. Setting this
146
+ # to `false` can improve performance, especially within `group_by`.
144
147
  #
145
148
  # @return [Expr]
146
149
  #
@@ -174,8 +177,8 @@ module Polars
174
177
  # # ╞═══════════╪═══════════════════════╡
175
178
  # # │ [9, 8, 7] ┆ ["foo", "bar", "foo"] │
176
179
  # # └───────────┴───────────────────────┘
177
- def implode(*columns)
178
- col(*columns).implode
180
+ def implode(*columns, maintain_order: true)
181
+ col(*columns).implode(maintain_order: maintain_order)
179
182
  end
180
183
 
181
184
  # Get the standard deviation.
@@ -17,24 +17,48 @@ module Polars
17
17
 
18
18
  table = scan.table
19
19
  snapshot = scan.snapshot
20
- schema = snapshot ? table.schema_by_id(snapshot[:schema_id]) : table.current_schema
20
+ schema = snapshot ? table.schema_by_id(snapshot.is_a?(Hash) ? snapshot[:schema_id] : snapshot.schema_id) : table.current_schema
21
21
 
22
- if files.empty?
23
- # TODO improve
22
+ if files.empty? && !schema.respond_to?(:arrow_c_schema)
23
+ # TODO remove in 0.27.0
24
24
  schema =
25
25
  schema.fields.to_h do |field|
26
26
  dtype =
27
27
  case field[:type]
28
+ when "unknown"
29
+ Unknown
30
+ when "boolean"
31
+ Boolean
28
32
  when "int"
29
- Polars::Int32
33
+ Int32
30
34
  when "long"
31
- Polars::Int64
35
+ Int64
36
+ when "float"
37
+ Float32
32
38
  when "double"
33
- Polars::Float64
39
+ Float64
40
+ when "decimal"
41
+ Decimal.new(field[:precision], field[:scale])
34
42
  when "string"
35
- Polars::String
43
+ String
44
+ when "uuid"
45
+ Binary
46
+ when "fixed"
47
+ Binary
48
+ when "binary"
49
+ Binary
50
+ when "date"
51
+ Date
52
+ when "time"
53
+ Time # ns instead of us since does not support time unit
36
54
  when "timestamp"
37
- Polars::Datetime
55
+ Datetime.new("us")
56
+ when "timestamp_ns"
57
+ Datetime.new("ns")
58
+ when "timestamptz"
59
+ Datetime.new("us", "+00:00")
60
+ when "timestamptz_ns"
61
+ Datetime.new("ns", "+00:00")
38
62
  else
39
63
  raise Todo
40
64
  end
@@ -44,25 +68,26 @@ module Polars
44
68
 
45
69
  LazyFrame.new(schema: schema)
46
70
  else
47
- sources = files.map { |v| v[:data_file_path] }
71
+ sources = files.map { |v| v.is_a?(Hash) ? v[:data_file_path] : v.file.file_path }
48
72
 
49
73
  column_mapping = [
50
74
  "iceberg-column-mapping",
51
- arrow_schema(schema)
75
+ schema.respond_to?(:arrow_c_schema) ? schema : arrow_schema(schema)
52
76
  ]
53
77
 
54
78
  deletion_files = [
55
79
  "iceberg-position-delete",
56
80
  files.map.with_index
57
- .select { |v, i| v[:deletes].any? }
58
- .to_h { |v, i| [i, v[:deletes].map { |d| d[:file_path] }] }
81
+ .select { |v, i| (v.is_a?(Hash) ? v[:deletes] : v.delete_files).any? }
82
+ .to_h { |v, i| [i, v.is_a?(Hash) ? v[:deletes].map { |d| d[:file_path] } : v.delete_files.map(&:file_path)] }
59
83
  ]
60
84
 
61
85
  scan_options = {
62
- storage_options: @storage_options,
86
+ schema: schema.respond_to?(:arrow_c_schema) ? Schema.new(schema) : nil,
63
87
  cast_options: Polars::ScanCastOptions._default_iceberg,
64
88
  missing_columns: "insert",
65
89
  extra_columns: "ignore",
90
+ storage_options: @storage_options,
66
91
  _column_mapping: column_mapping,
67
92
  _deletion_files: deletion_files
68
93
  }
@@ -73,11 +98,14 @@ module Polars
73
98
 
74
99
  private
75
100
 
101
+ # TODO remove in 0.27.0
76
102
  def arrow_schema(schema)
77
103
  fields =
78
104
  schema.fields.map do |field|
79
105
  type =
80
106
  case field[:type]
107
+ when "unknown"
108
+ "unknown"
81
109
  when "boolean"
82
110
  "boolean"
83
111
  when "int"
@@ -88,11 +116,42 @@ module Polars
88
116
  "float32"
89
117
  when "double"
90
118
  "float64"
119
+ when "decimal"
120
+ "decimal"
121
+ when "string"
122
+ "string"
123
+ when "uuid"
124
+ limit = 16
125
+ "fixed_size_binary"
126
+ when "fixed"
127
+ limit = field[:limit]
128
+ "fixed_size_binary"
129
+ when "binary"
130
+ "large_binary"
131
+ when "date"
132
+ "date32"
133
+ when "time"
134
+ time_unit = "us"
135
+ "time64"
136
+ when "timestamp"
137
+ time_unit = "us"
138
+ "timestamp"
139
+ when "timestamp_ns"
140
+ time_unit = "ns"
141
+ "timestamp"
142
+ when "timestamptz"
143
+ time_unit = "us"
144
+ time_zone = "+00:00"
145
+ "timestamp"
146
+ when "timestamptz_ns"
147
+ time_unit = "ns"
148
+ time_zone = "+00:00"
149
+ "timestamp"
91
150
  else
92
151
  raise Todo
93
152
  end
94
153
 
95
- {
154
+ arrow_field = {
96
155
  name: field[:name],
97
156
  type: type,
98
157
  nullable: !field[:required],
@@ -100,6 +159,14 @@ module Polars
100
159
  "PARQUET:field_id" => field[:id].to_s
101
160
  }
102
161
  }
162
+ arrow_field[:limit] = limit if limit
163
+ if type == "decimal"
164
+ arrow_field[:precision] = field[:precision]
165
+ arrow_field[:scale] = field[:scale]
166
+ end
167
+ arrow_field[:time_unit] = time_unit if time_unit
168
+ arrow_field[:time_zone] = time_zone if time_zone
169
+ arrow_field
103
170
  end
104
171
 
105
172
  {fields: fields}
data/lib/polars/io/csv.rb CHANGED
@@ -648,6 +648,12 @@ module Polars
648
648
  # (which defaults to 1 hour) if not given.
649
649
  # @param include_file_paths [String]
650
650
  # Include the path of the source file(s) as a column with this name.
651
+ # @param missing_columns ['insert', 'raise']
652
+ # Configuration for behavior when columns defined in the schema are
653
+ # missing from the data:
654
+ #
655
+ # * `"insert"`: Insert the missing columns with NULL values.
656
+ # * `"raise"`: Raise an error.
651
657
  #
652
658
  # @return [LazyFrame]
653
659
  def scan_csv(
@@ -663,7 +669,7 @@ module Polars
663
669
  null_values: nil,
664
670
  missing_utf8_is_empty_string: false,
665
671
  ignore_errors: false,
666
- cache: true,
672
+ cache: nil,
667
673
  with_column_names: nil,
668
674
  infer_schema: true,
669
675
  infer_schema_length: N_INFER_DEFAULT,
@@ -685,7 +691,8 @@ module Polars
685
691
  credential_provider: "auto",
686
692
  retries: nil,
687
693
  file_cache_ttl: nil,
688
- include_file_paths: nil
694
+ include_file_paths: nil,
695
+ missing_columns: nil
689
696
  )
690
697
  if new_columns&.any? && schema_overrides.is_a?(::Array)
691
698
  msg = "expected 'schema_overrides' hash, found #{schema_overrides.inspect}"
@@ -734,6 +741,11 @@ module Polars
734
741
  storage_options["file_cache_ttl"] = file_cache_ttl
735
742
  end
736
743
 
744
+ if !missing_columns.nil?
745
+ msg = "The `missing_columns` parameter of `scan_csv` is considered unstable."
746
+ Utils.issue_unstable_warning(msg)
747
+ end
748
+
737
749
  credential_provider_builder = _init_credential_provider_builder(
738
750
  credential_provider, source, storage_options, "scan_csv"
739
751
  )
@@ -768,7 +780,8 @@ module Polars
768
780
  glob: glob,
769
781
  storage_options: storage_options,
770
782
  credential_provider: credential_provider_builder,
771
- include_file_paths: include_file_paths
783
+ include_file_paths: include_file_paths,
784
+ missing_columns: missing_columns
772
785
  )
773
786
  end
774
787
 
@@ -804,7 +817,8 @@ module Polars
804
817
  glob: true,
805
818
  storage_options: nil,
806
819
  credential_provider: nil,
807
- include_file_paths: nil
820
+ include_file_paths: nil,
821
+ missing_columns: nil
808
822
  )
809
823
  dtype_list = nil
810
824
  if !schema_overrides.nil?
@@ -822,6 +836,13 @@ module Polars
822
836
  sources = []
823
837
  end
824
838
 
839
+ # TODO: This is a hack. We conditionally set `missing_columns` to mimic
840
+ # existing behavior. This should be removed once the workaround is no
841
+ # longer needed.
842
+ if missing_columns.nil? && !schema.nil? && has_header
843
+ missing_columns = "insert"
844
+ end
845
+
825
846
  rblf =
826
847
  RbLazyFrame.new_from_csv(
827
848
  source,
@@ -854,7 +875,8 @@ module Polars
854
875
  schema,
855
876
  storage_options,
856
877
  credential_provider,
857
- include_file_paths
878
+ include_file_paths,
879
+ missing_columns
858
880
  )
859
881
  Utils.wrap_ldf(rblf)
860
882
  end
@@ -25,6 +25,8 @@ module Polars
25
25
  storage_options: nil,
26
26
  delta_table_options: nil
27
27
  )
28
+ require "deltalake-rb"
29
+
28
30
  df =
29
31
  scan_delta(
30
32
  source,
@@ -62,6 +64,8 @@ module Polars
62
64
  delta_table_options: nil,
63
65
  rechunk: nil
64
66
  )
67
+ require "deltalake-rb"
68
+
65
69
  dl_tbl =
66
70
  _get_delta_lake_table(
67
71
  source,
data/lib/polars/io/ipc.rb CHANGED
@@ -218,7 +218,7 @@ module Polars
218
218
  def scan_ipc(
219
219
  source,
220
220
  n_rows: nil,
221
- cache: true,
221
+ cache: nil,
222
222
  rechunk: false,
223
223
  row_index_name: nil,
224
224
  row_index_offset: 0,
@@ -49,7 +49,7 @@ module Polars
49
49
  # # =>
50
50
  # # shape: (2, 1)
51
51
  # # ┌───────┐
52
- # # │ lines
52
+ # # │ line
53
53
  # # │ --- │
54
54
  # # │ str │
55
55
  # # ╞═══════╡
@@ -58,7 +58,7 @@ module Polars
58
58
  # # └───────┘
59
59
  def read_lines(
60
60
  source,
61
- name: "lines",
61
+ name: "line",
62
62
  n_rows: nil,
63
63
  row_index_name: nil,
64
64
  row_index_offset: 0,
@@ -129,7 +129,7 @@ module Polars
129
129
  # # =>
130
130
  # # shape: (2, 1)
131
131
  # # ┌───────┐
132
- # # │ lines
132
+ # # │ line
133
133
  # # │ --- │
134
134
  # # │ str │
135
135
  # # ╞═══════╡
@@ -138,7 +138,7 @@ module Polars
138
138
  # # └───────┘
139
139
  def scan_lines(
140
140
  source,
141
- name: "lines",
141
+ name: "line",
142
142
  n_rows: nil,
143
143
  row_index_name: nil,
144
144
  row_index_offset: 0,
@@ -2,20 +2,22 @@ module Polars
2
2
  module IO
3
3
  # @private
4
4
  class SinkOptions
5
- attr_reader :mkdir, :maintain_order, :sync_on_close, :storage_options, :credential_provider
5
+ attr_reader :mkdir, :maintain_order, :sync_on_close, :storage_options, :credential_provider, :sinked_paths_callback
6
6
 
7
7
  def initialize(
8
8
  mkdir: nil,
9
9
  maintain_order: nil,
10
10
  sync_on_close: nil,
11
11
  storage_options: nil,
12
- credential_provider: nil
12
+ credential_provider: nil,
13
+ sinked_paths_callback: nil
13
14
  )
14
15
  @mkdir = mkdir
15
16
  @maintain_order = maintain_order
16
17
  @sync_on_close = sync_on_close
17
18
  @storage_options = storage_options
18
19
  @credential_provider = credential_provider
20
+ @sinked_paths_callback = sinked_paths_callback
19
21
  end
20
22
  end
21
23
  end
@@ -1162,7 +1162,8 @@ module Polars
1162
1162
  lazy: false,
1163
1163
  arrow_schema: nil,
1164
1164
  engine: "auto",
1165
- optimizations: DEFAULT_QUERY_OPT_FLAGS
1165
+ optimizations: DEFAULT_QUERY_OPT_FLAGS,
1166
+ _sinked_paths_callback: nil
1166
1167
  )
1167
1168
  engine = _select_engine(engine)
1168
1169
 
@@ -1204,7 +1205,8 @@ module Polars
1204
1205
  maintain_order: maintain_order,
1205
1206
  sync_on_close: sync_on_close,
1206
1207
  storage_options: storage_options,
1207
- credential_provider: credential_provider_builder
1208
+ credential_provider: credential_provider_builder,
1209
+ sinked_paths_callback: _sinked_paths_callback
1208
1210
  )
1209
1211
 
1210
1212
  ldf_rb = _ldf.sink_parquet(
@@ -3479,6 +3481,78 @@ module Polars
3479
3481
  )
3480
3482
  end
3481
3483
 
3484
+ # Selects rows from this LazyFrame at the given indices.
3485
+ #
3486
+ # @note
3487
+ # This functionality is experimental. It may be
3488
+ # changed at any point without it being considered a breaking change.
3489
+ #
3490
+ # @param indices [Object]
3491
+ # The indices of the rows to select.
3492
+ #
3493
+ # Due to the lack of a `LazySeries` it's permitted to pass a single-width
3494
+ # `LazyFrame` as indices as well.
3495
+ # @param null_on_oob [Boolean]
3496
+ # If true when an index is out-of-bounds a null row will be generated
3497
+ # instead of raising an error.
3498
+ #
3499
+ # @return [LazyFrame]
3500
+ #
3501
+ # @example
3502
+ # lf = Polars::LazyFrame.new({"x" => [2, 1, 0], "s" => ["foo", "bar", "baz"]})
3503
+ # lf.gather([2, 0, 0]).collect
3504
+ # # =>
3505
+ # # shape: (3, 2)
3506
+ # # ┌─────┬─────┐
3507
+ # # │ x ┆ s │
3508
+ # # │ --- ┆ --- │
3509
+ # # │ i64 ┆ str │
3510
+ # # ╞═════╪═════╡
3511
+ # # │ 0 ┆ baz │
3512
+ # # │ 2 ┆ foo │
3513
+ # # │ 2 ┆ foo │
3514
+ # # └─────┴─────┘
3515
+ #
3516
+ # @example
3517
+ # lf.gather([0, 10, 1], null_on_oob: true).collect
3518
+ # # =>
3519
+ # # shape: (3, 2)
3520
+ # # ┌──────┬──────┐
3521
+ # # │ x ┆ s │
3522
+ # # │ --- ┆ --- │
3523
+ # # │ i64 ┆ str │
3524
+ # # ╞══════╪══════╡
3525
+ # # │ 2 ┆ foo │
3526
+ # # │ null ┆ null │
3527
+ # # │ 1 ┆ bar │
3528
+ # # └──────┴──────┘
3529
+ #
3530
+ # @example
3531
+ # idxs = Polars::LazyFrame.new({"i" => [1, 10, 0], "b" => [true, false, true]})
3532
+ # lf.gather(idxs.filter(Polars.col("b")).select(Polars.col("i"))).collect
3533
+ # # =>
3534
+ # # shape: (2, 2)
3535
+ # # ┌─────┬─────┐
3536
+ # # │ x ┆ s │
3537
+ # # │ --- ┆ --- │
3538
+ # # │ i64 ┆ str │
3539
+ # # ╞═════╪═════╡
3540
+ # # │ 1 ┆ bar │
3541
+ # # │ 2 ┆ foo │
3542
+ # # └─────┴─────┘
3543
+ def gather(indices, null_on_oob: false)
3544
+ if !indices.is_a?(LazyFrame)
3545
+ if indices.is_a?(::Array)
3546
+ indices_expr = F.lit(Series.new("", indices, dtype: Int64))
3547
+ else
3548
+ indices_expr = wrap_expr(Utils.parse_into_expression(indices))
3549
+ end
3550
+ indices = select(indices_expr)
3551
+ end
3552
+
3553
+ _from_rbldf(_ldf.gather(indices._ldf, null_on_oob))
3554
+ end
3555
+
3482
3556
  # Add or overwrite multiple columns in a DataFrame.
3483
3557
  #
3484
3558
  # @param exprs [Object]
@@ -4643,6 +4717,8 @@ module Polars
4643
4717
  # @param separator [String]
4644
4718
  # Used as separator/delimiter in generated column names in case of multiple
4645
4719
  # `values` columns.
4720
+ # @param column_naming ['auto', 'combine']
4721
+ # How resulting column names will be constructed.
4646
4722
  #
4647
4723
  # @return [LazyFrame]
4648
4724
  #
@@ -4682,7 +4758,8 @@ module Polars
4682
4758
  values: nil,
4683
4759
  aggregate_function: nil,
4684
4760
  maintain_order: false,
4685
- separator: "_"
4761
+ separator: "_",
4762
+ column_naming: "auto"
4686
4763
  )
4687
4764
  if index.nil? && values.nil?
4688
4765
  msg = "`pivot` needs either `index or `values` needs to be specified"
@@ -4756,7 +4833,8 @@ module Polars
4756
4833
  values_selector._rbselector,
4757
4834
  agg._rbexpr,
4758
4835
  maintain_order,
4759
- separator
4836
+ separator,
4837
+ column_naming
4760
4838
  )
4761
4839
  )
4762
4840
  end
@@ -4772,7 +4850,8 @@ module Polars
4772
4850
  #
4773
4851
  # @param on [Object]
4774
4852
  # Column(s) or selector(s) to use as values variables; if `on`
4775
- # is empty all columns that are not in `index` will be used.
4853
+ # is empty no columns will be used. If set to `nil` (default)
4854
+ # all columns that are not in `index` will be used.
4776
4855
  # @param index [Object]
4777
4856
  # Column(s) or selector(s) to use as identifier variables.
4778
4857
  # @param variable_name [String]
@@ -4820,12 +4899,12 @@ module Polars
4820
4899
  warn "The `streamable` parameter for `LazyFrame.unpivot` is deprecated"
4821
4900
  end
4822
4901
 
4823
- selector_on = on.nil? ? Selectors.empty : Utils.parse_list_into_selector(on)
4902
+ selector_on = on.nil? ? nil : Utils.parse_list_into_selector(on)._rbselector
4824
4903
  selector_index = index.nil? ? Selectors.empty : Utils.parse_list_into_selector(index)
4825
4904
 
4826
4905
  _from_rbldf(
4827
4906
  _ldf.unpivot(
4828
- selector_on._rbselector,
4907
+ selector_on,
4829
4908
  selector_index._rbselector,
4830
4909
  value_name,
4831
4910
  variable_name
@@ -4905,10 +4984,10 @@ module Polars
4905
4984
  # # │ -2 ┆ 199998 │
4906
4985
  # # └─────────┴────────┘
4907
4986
  def map_batches(
4908
- predicate_pushdown: true,
4909
- projection_pushdown: true,
4910
- slice_pushdown: true,
4911
- no_optimizations: false,
4987
+ predicate_pushdown: false,
4988
+ projection_pushdown: false,
4989
+ slice_pushdown: false,
4990
+ no_optimizations: nil,
4912
4991
  schema: nil,
4913
4992
  validate_output_schema: true,
4914
4993
  streamable: false,
@@ -5020,7 +5099,7 @@ module Polars
5020
5099
  # # │ foo ┆ 1 ┆ a ┆ true ┆ [1, 2] ┆ baz │
5021
5100
  # # │ bar ┆ 2 ┆ b ┆ null ┆ [3] ┆ womp │
5022
5101
  # # └────────┴─────┴─────┴──────┴───────────┴───────┘
5023
- def unnest(columns, *more_columns, separator: nil)
5102
+ def unnest(columns = nil, *more_columns, separator: nil)
5024
5103
  subset = Utils.parse_list_into_selector(columns) | Utils.parse_list_into_selector(
5025
5104
  more_columns
5026
5105
  )
@@ -5039,6 +5118,10 @@ module Polars
5039
5118
  # Other DataFrame that must be merged
5040
5119
  # @param key [String]
5041
5120
  # Key that is sorted.
5121
+ # @param maintain_order [Boolean]
5122
+ # If `true`, the output is guaranteed to have left-biased ordering
5123
+ # for equal keys: rows from the left frame appear before rows from
5124
+ # the right frame when their keys are equal.
5042
5125
  #
5043
5126
  # @return [LazyFrame]
5044
5127
  #
@@ -5065,8 +5148,8 @@ module Polars
5065
5148
  # # │ steve ┆ 42 │
5066
5149
  # # │ elise ┆ 44 │
5067
5150
  # # └────────┴─────┘
5068
- def merge_sorted(other, key)
5069
- _from_rbldf(_ldf.merge_sorted(other._ldf, key))
5151
+ def merge_sorted(other, key, maintain_order: false)
5152
+ _from_rbldf(_ldf.merge_sorted(other._ldf, key, maintain_order))
5070
5153
  end
5071
5154
 
5072
5155
  # Flag a column as sorted.