polars-df 0.9.0 → 0.10.0

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/Cargo.lock +90 -45
  4. data/README.md +1 -0
  5. data/ext/polars/Cargo.toml +8 -6
  6. data/ext/polars/src/batched_csv.rs +3 -1
  7. data/ext/polars/src/conversion/anyvalue.rs +3 -2
  8. data/ext/polars/src/conversion/mod.rs +18 -7
  9. data/ext/polars/src/dataframe.rs +40 -14
  10. data/ext/polars/src/expr/array.rs +6 -2
  11. data/ext/polars/src/expr/datetime.rs +7 -2
  12. data/ext/polars/src/expr/general.rs +22 -3
  13. data/ext/polars/src/expr/list.rs +6 -2
  14. data/ext/polars/src/expr/string.rs +3 -3
  15. data/ext/polars/src/file.rs +158 -11
  16. data/ext/polars/src/functions/lazy.rs +18 -3
  17. data/ext/polars/src/functions/whenthen.rs +47 -17
  18. data/ext/polars/src/lazyframe/mod.rs +58 -19
  19. data/ext/polars/src/lib.rs +23 -14
  20. data/ext/polars/src/map/dataframe.rs +17 -9
  21. data/ext/polars/src/series/mod.rs +12 -2
  22. data/lib/polars/array_expr.rb +6 -2
  23. data/lib/polars/batched_csv_reader.rb +4 -2
  24. data/lib/polars/data_frame.rb +148 -74
  25. data/lib/polars/date_time_expr.rb +10 -4
  26. data/lib/polars/date_time_name_space.rb +9 -3
  27. data/lib/polars/expr.rb +37 -34
  28. data/lib/polars/functions/lazy.rb +3 -3
  29. data/lib/polars/functions/whenthen.rb +74 -5
  30. data/lib/polars/io.rb +18 -6
  31. data/lib/polars/lazy_frame.rb +39 -36
  32. data/lib/polars/list_expr.rb +6 -2
  33. data/lib/polars/series.rb +12 -10
  34. data/lib/polars/string_expr.rb +1 -0
  35. data/lib/polars/utils.rb +54 -0
  36. data/lib/polars/version.rb +1 -1
  37. data/lib/polars/whenthen.rb +83 -0
  38. data/lib/polars.rb +1 -2
  39. metadata +4 -5
  40. data/lib/polars/when.rb +0 -16
  41. data/lib/polars/when_then.rb +0 -19
@@ -265,9 +265,9 @@ pub fn apply_lambda_with_rows_output<'a>(
265
265
  // to the row. Before we mutate the row buf again, the reference is dropped.
266
266
  // we only cannot prove it to the compiler.
267
267
  // we still do this because it saves a Vec allocation in a hot loop.
268
- unsafe { &*ptr }
268
+ Ok(unsafe { &*ptr })
269
269
  }
270
- None => &null_row,
270
+ None => Ok(&null_row),
271
271
  }
272
272
  }
273
273
  Err(e) => panic!("ruby function failed {}", e),
@@ -277,22 +277,30 @@ pub fn apply_lambda_with_rows_output<'a>(
277
277
  // first rows for schema inference
278
278
  let mut buf = Vec::with_capacity(inference_size);
279
279
  buf.push(first_value);
280
- buf.extend((&mut row_iter).take(inference_size).cloned());
281
- let schema = rows_to_schema_first_non_null(&buf, Some(50));
280
+ for v in (&mut row_iter).take(inference_size) {
281
+ buf.push(v?.clone());
282
+ }
283
+
284
+ let schema = rows_to_schema_first_non_null(&buf, Some(50))?;
282
285
 
283
286
  if init_null_count > 0 {
284
287
  // Safety: we know the iterators size
285
288
  let iter = unsafe {
286
289
  (0..init_null_count)
287
- .map(|_| &null_row)
288
- .chain(buf.iter())
290
+ .map(|_| Ok(&null_row))
291
+ .chain(buf.iter().map(Ok))
289
292
  .chain(row_iter)
290
293
  .trust_my_length(df.height())
291
294
  };
292
- DataFrame::from_rows_iter_and_schema(iter, &schema)
295
+ DataFrame::try_from_rows_iter_and_schema(iter, &schema)
293
296
  } else {
294
297
  // Safety: we know the iterators size
295
- let iter = unsafe { buf.iter().chain(row_iter).trust_my_length(df.height()) };
296
- DataFrame::from_rows_iter_and_schema(iter, &schema)
298
+ let iter = unsafe {
299
+ buf.iter()
300
+ .map(Ok)
301
+ .chain(row_iter)
302
+ .trust_my_length(df.height())
303
+ };
304
+ DataFrame::try_from_rows_iter_and_schema(iter, &schema)
297
305
  }
298
306
  }
@@ -233,8 +233,18 @@ impl RbSeries {
233
233
  }
234
234
  }
235
235
 
236
- pub fn sort(&self, descending: bool, nulls_last: bool) -> Self {
237
- (self.series.borrow_mut().sort(descending, nulls_last)).into()
236
+ pub fn sort(&self, descending: bool, nulls_last: bool, multithreaded: bool) -> RbResult<Self> {
237
+ Ok(self
238
+ .series
239
+ .borrow_mut()
240
+ .sort(
241
+ SortOptions::default()
242
+ .with_order_descending(descending)
243
+ .with_nulls_last(nulls_last)
244
+ .with_multithreaded(multithreaded),
245
+ )
246
+ .map_err(RbPolarsErr::from)?
247
+ .into())
238
248
  }
239
249
 
240
250
  pub fn value_counts(&self, sorted: bool) -> RbResult<RbDataFrame> {
@@ -333,6 +333,10 @@ module Polars
333
333
  #
334
334
  # @param index [Integer]
335
335
  # Index to return per sub-array
336
+ # @param null_on_oob [Boolean]
337
+ # Behavior if an index is out of bounds:
338
+ # true -> set as null
339
+ # false -> raise an error
336
340
  #
337
341
  # @return [Expr]
338
342
  #
@@ -353,9 +357,9 @@ module Polars
353
357
  # # │ [4, 5, 6] ┆ -2 ┆ 5 │
354
358
  # # │ [7, 8, 9] ┆ 4 ┆ null │
355
359
  # # └───────────────┴─────┴──────┘
356
- def get(index)
360
+ def get(index, null_on_oob: true)
357
361
  index = Utils.parse_as_expression(index)
358
- Utils.wrap_expr(_rbexpr.arr_get(index))
362
+ Utils.wrap_expr(_rbexpr.arr_get(index, null_on_oob))
359
363
  end
360
364
 
361
365
  # Get the first value of the sub-arrays.
@@ -27,7 +27,8 @@ module Polars
27
27
  row_count_offset: 0,
28
28
  sample_size: 1024,
29
29
  eol_char: "\n",
30
- new_columns: nil
30
+ new_columns: nil,
31
+ truncate_ragged_lines: false
31
32
  )
32
33
  if Utils.pathlike?(file)
33
34
  path = Utils.normalise_filepath(file)
@@ -75,7 +76,8 @@ module Polars
75
76
  skip_rows_after_header,
76
77
  Utils._prepare_row_count_args(row_count_name, row_count_offset),
77
78
  sample_size,
78
- eol_char
79
+ eol_char,
80
+ truncate_ragged_lines
79
81
  )
80
82
  self.new_columns = new_columns
81
83
  end
@@ -91,7 +91,8 @@ module Polars
91
91
  row_count_name: nil,
92
92
  row_count_offset: 0,
93
93
  sample_size: 1024,
94
- eol_char: "\n"
94
+ eol_char: "\n",
95
+ truncate_ragged_lines: false
95
96
  )
96
97
  if Utils.pathlike?(file)
97
98
  path = Utils.normalise_filepath(file)
@@ -147,7 +148,8 @@ module Polars
147
148
  skip_rows_after_header: skip_rows_after_header,
148
149
  row_count_name: row_count_name,
149
150
  row_count_offset: row_count_offset,
150
- eol_char: eol_char
151
+ eol_char: eol_char,
152
+ truncate_ragged_lines: truncate_ragged_lines
151
153
  )
152
154
  if columns.nil?
153
155
  return _from_rbdf(scan.collect._df)
@@ -186,7 +188,8 @@ module Polars
186
188
  skip_rows_after_header,
187
189
  Utils._prepare_row_count_args(row_count_name, row_count_offset),
188
190
  sample_size,
189
- eol_char
191
+ eol_char,
192
+ truncate_ragged_lines
190
193
  )
191
194
  )
192
195
  end
@@ -814,8 +817,6 @@ module Polars
814
817
 
815
818
  # Serialize to JSON representation.
816
819
  #
817
- # @return [nil]
818
- #
819
820
  # @param file [String]
820
821
  # File path to which the result should be written.
821
822
  # @param pretty [Boolean]
@@ -823,17 +824,45 @@ module Polars
823
824
  # @param row_oriented [Boolean]
824
825
  # Write to row oriented json. This is slower, but more common.
825
826
  #
826
- # @see #write_ndjson
827
+ # @return [nil]
828
+ #
829
+ # @example
830
+ # df = Polars::DataFrame.new(
831
+ # {
832
+ # "foo" => [1, 2, 3],
833
+ # "bar" => [6, 7, 8]
834
+ # }
835
+ # )
836
+ # df.write_json
837
+ # # => "{\"columns\":[{\"name\":\"foo\",\"datatype\":\"Int64\",\"bit_settings\":\"\",\"values\":[1,2,3]},{\"name\":\"bar\",\"datatype\":\"Int64\",\"bit_settings\":\"\",\"values\":[6,7,8]}]}"
838
+ #
839
+ # @example
840
+ # df.write_json(row_oriented: true)
841
+ # # => "[{\"foo\":1,\"bar\":6},{\"foo\":2,\"bar\":7},{\"foo\":3,\"bar\":8}]"
827
842
  def write_json(
828
- file,
843
+ file = nil,
829
844
  pretty: false,
830
845
  row_oriented: false
831
846
  )
832
847
  if Utils.pathlike?(file)
833
848
  file = Utils.normalise_filepath(file)
834
849
  end
835
-
836
- _df.write_json(file, pretty, row_oriented)
850
+ to_string_io = !file.nil? && file.is_a?(StringIO)
851
+ if file.nil? || to_string_io
852
+ buf = StringIO.new
853
+ buf.set_encoding(Encoding::BINARY)
854
+ _df.write_json(buf, pretty, row_oriented)
855
+ json_bytes = buf.string
856
+
857
+ json_str = json_bytes.force_encoding(Encoding::UTF_8)
858
+ if to_string_io
859
+ file.write(json_str)
860
+ else
861
+ return json_str
862
+ end
863
+ else
864
+ _df.write_json(file, pretty, row_oriented)
865
+ end
837
866
  nil
838
867
  end
839
868
 
@@ -843,12 +872,36 @@ module Polars
843
872
  # File path to which the result should be written.
844
873
  #
845
874
  # @return [nil]
846
- def write_ndjson(file)
875
+ #
876
+ # @example
877
+ # df = Polars::DataFrame.new(
878
+ # {
879
+ # "foo" => [1, 2, 3],
880
+ # "bar" => [6, 7, 8]
881
+ # }
882
+ # )
883
+ # df.write_ndjson()
884
+ # # => "{\"foo\":1,\"bar\":6}\n{\"foo\":2,\"bar\":7}\n{\"foo\":3,\"bar\":8}\n"
885
+ def write_ndjson(file = nil)
847
886
  if Utils.pathlike?(file)
848
887
  file = Utils.normalise_filepath(file)
849
888
  end
850
-
851
- _df.write_ndjson(file)
889
+ to_string_io = !file.nil? && file.is_a?(StringIO)
890
+ if file.nil? || to_string_io
891
+ buf = StringIO.new
892
+ buf.set_encoding(Encoding::BINARY)
893
+ _df.write_ndjson(buf)
894
+ json_bytes = buf.string
895
+
896
+ json_str = json_bytes.force_encoding(Encoding::UTF_8)
897
+ if to_string_io
898
+ file.write(json_str)
899
+ else
900
+ return json_str
901
+ end
902
+ else
903
+ _df.write_ndjson(file)
904
+ end
852
905
  nil
853
906
  end
854
907
 
@@ -1010,7 +1063,7 @@ module Polars
1010
1063
 
1011
1064
  # Write to Apache Parquet file.
1012
1065
  #
1013
- # @param file [String]
1066
+ # @param file [String, Pathname, StringIO]
1014
1067
  # File path to which the file should be written.
1015
1068
  # @param compression ["lz4", "uncompressed", "snappy", "gzip", "lzo", "brotli", "zstd"]
1016
1069
  # Choose "zstd" for good compression performance.
@@ -1027,10 +1080,9 @@ module Polars
1027
1080
  # @param statistics [Boolean]
1028
1081
  # Write statistics to the parquet headers. This requires extra compute.
1029
1082
  # @param row_group_size [Integer, nil]
1030
- # Size of the row groups in number of rows.
1031
- # If `nil` (default), the chunks of the DataFrame are
1032
- # used. Writing in smaller chunks may reduce memory pressure and improve
1033
- # writing speeds.
1083
+ # Size of the row groups in number of rows. Defaults to 512^2 rows.
1084
+ # @param data_page_size [Integer, nil]
1085
+ # Size of the data page in bytes. Defaults to 1024^2 bytes.
1034
1086
  #
1035
1087
  # @return [nil]
1036
1088
  def write_parquet(
@@ -1038,7 +1090,8 @@ module Polars
1038
1090
  compression: "zstd",
1039
1091
  compression_level: nil,
1040
1092
  statistics: false,
1041
- row_group_size: nil
1093
+ row_group_size: nil,
1094
+ data_page_size: nil
1042
1095
  )
1043
1096
  if compression.nil?
1044
1097
  compression = "uncompressed"
@@ -1048,7 +1101,7 @@ module Polars
1048
1101
  end
1049
1102
 
1050
1103
  _df.write_parquet(
1051
- file, compression, compression_level, statistics, row_group_size
1104
+ file, compression, compression_level, statistics, row_group_size, data_page_size
1052
1105
  )
1053
1106
  end
1054
1107
 
@@ -1084,7 +1137,7 @@ module Polars
1084
1137
  # df.estimated_size
1085
1138
  # # => 25888898
1086
1139
  # df.estimated_size("mb")
1087
- # # => 26.702880859375
1140
+ # # => 17.0601749420166
1088
1141
  def estimated_size(unit = "b")
1089
1142
  sz = _df.estimated_size
1090
1143
  Utils.scale_bytes(sz, to: unit)
@@ -2161,12 +2214,13 @@ module Polars
2161
2214
  # closed: "right"
2162
2215
  # ).agg(Polars.col("A").alias("A_agg_list"))
2163
2216
  # # =>
2164
- # # shape: (3, 4)
2217
+ # # shape: (4, 4)
2165
2218
  # # ┌─────────────────┬─────────────────┬─────┬─────────────────┐
2166
2219
  # # │ _lower_boundary ┆ _upper_boundary ┆ idx ┆ A_agg_list │
2167
2220
  # # │ --- ┆ --- ┆ --- ┆ --- │
2168
2221
  # # │ i64 ┆ i64 ┆ i64 ┆ list[str] │
2169
2222
  # # ╞═════════════════╪═════════════════╪═════╪═════════════════╡
2223
+ # # │ -2 ┆ 1 ┆ -2 ┆ ["A", "A"] │
2170
2224
  # # │ 0 ┆ 3 ┆ 0 ┆ ["A", "B", "B"] │
2171
2225
  # # │ 2 ┆ 5 ┆ 2 ┆ ["B", "B", "C"] │
2172
2226
  # # │ 4 ┆ 7 ┆ 4 ┆ ["C"] │
@@ -2621,26 +2675,26 @@ module Polars
2621
2675
  # # ┌─────┬─────┬───────────┐
2622
2676
  # # │ a ┆ b ┆ b_squared │
2623
2677
  # # │ --- ┆ --- ┆ --- │
2624
- # # │ i64 ┆ i64 ┆ f64
2678
+ # # │ i64 ┆ i64 ┆ i64
2625
2679
  # # ╞═════╪═════╪═══════════╡
2626
- # # │ 1 ┆ 2 ┆ 4.0
2627
- # # │ 3 ┆ 4 ┆ 16.0
2628
- # # │ 5 ┆ 6 ┆ 36.0
2680
+ # # │ 1 ┆ 2 ┆ 4
2681
+ # # │ 3 ┆ 4 ┆ 16
2682
+ # # │ 5 ┆ 6 ┆ 36
2629
2683
  # # └─────┴─────┴───────────┘
2630
2684
  #
2631
2685
  # @example Replaced
2632
2686
  # df.with_column(Polars.col("a") ** 2)
2633
2687
  # # =>
2634
2688
  # # shape: (3, 2)
2635
- # # ┌──────┬─────┐
2636
- # # │ a ┆ b │
2637
- # # │ --- ┆ --- │
2638
- # # │ f64 ┆ i64 │
2639
- # # ╞══════╪═════╡
2640
- # # │ 1.0 ┆ 2 │
2641
- # # │ 9.0 ┆ 4 │
2642
- # # │ 25.0 ┆ 6 │
2643
- # # └──────┴─────┘
2689
+ # # ┌─────┬─────┐
2690
+ # # │ a ┆ b │
2691
+ # # │ --- ┆ --- │
2692
+ # # │ i64 ┆ i64 │
2693
+ # # ╞═════╪═════╡
2694
+ # # │ 1 ┆ 2 │
2695
+ # # │ 9 ┆ 4 │
2696
+ # # │ 25 ┆ 6 │
2697
+ # # └─────┴─────┘
2644
2698
  def with_column(column)
2645
2699
  lazy
2646
2700
  .with_column(column)
@@ -2807,16 +2861,36 @@ module Polars
2807
2861
  # # │ 2 ┆ 7.0 │
2808
2862
  # # │ 3 ┆ 8.0 │
2809
2863
  # # └─────┴─────┘
2810
- def drop(columns)
2811
- if columns.is_a?(::Array)
2812
- df = clone
2813
- columns.each do |n|
2814
- df._df.drop_in_place(n)
2815
- end
2816
- df
2817
- else
2818
- _from_rbdf(_df.drop(columns))
2819
- end
2864
+ #
2865
+ # @example Drop multiple columns by passing a list of column names.
2866
+ # df.drop(["bar", "ham"])
2867
+ # # =>
2868
+ # # shape: (3, 1)
2869
+ # # ┌─────┐
2870
+ # # │ foo │
2871
+ # # │ --- │
2872
+ # # │ i64 │
2873
+ # # ╞═════╡
2874
+ # # │ 1 │
2875
+ # # │ 2 │
2876
+ # # │ 3 │
2877
+ # # └─────┘
2878
+ #
2879
+ # @example Use positional arguments to drop multiple columns.
2880
+ # df.drop("foo", "ham")
2881
+ # # =>
2882
+ # # shape: (3, 1)
2883
+ # # ┌─────┐
2884
+ # # │ bar │
2885
+ # # │ --- │
2886
+ # # │ f64 │
2887
+ # # ╞═════╡
2888
+ # # │ 6.0 │
2889
+ # # │ 7.0 │
2890
+ # # │ 8.0 │
2891
+ # # └─────┘
2892
+ def drop(*columns)
2893
+ lazy.drop(*columns).collect(_eager: true)
2820
2894
  end
2821
2895
 
2822
2896
  # Drop in place.
@@ -3735,16 +3809,16 @@ module Polars
3735
3809
  # df.with_columns((Polars.col("a") ** 2).alias("a^2"))
3736
3810
  # # =>
3737
3811
  # # shape: (4, 4)
3738
- # # ┌─────┬──────┬───────┬──────┐
3739
- # # │ a ┆ b ┆ c ┆ a^2
3740
- # # │ --- ┆ --- ┆ --- ┆ ---
3741
- # # │ i64 ┆ f64 ┆ bool ┆ f64
3742
- # # ╞═════╪══════╪═══════╪══════╡
3743
- # # │ 1 ┆ 0.5 ┆ true ┆ 1.0
3744
- # # │ 2 ┆ 4.0 ┆ true ┆ 4.0
3745
- # # │ 3 ┆ 10.0 ┆ false ┆ 9.0
3746
- # # │ 4 ┆ 13.0 ┆ true ┆ 16.0
3747
- # # └─────┴──────┴───────┴──────┘
3812
+ # # ┌─────┬──────┬───────┬─────┐
3813
+ # # │ a ┆ b ┆ c ┆ a^2
3814
+ # # │ --- ┆ --- ┆ --- ┆ ---
3815
+ # # │ i64 ┆ f64 ┆ bool ┆ i64
3816
+ # # ╞═════╪══════╪═══════╪═════╡
3817
+ # # │ 1 ┆ 0.5 ┆ true ┆ 1
3818
+ # # │ 2 ┆ 4.0 ┆ true ┆ 4
3819
+ # # │ 3 ┆ 10.0 ┆ false ┆ 9
3820
+ # # │ 4 ┆ 13.0 ┆ true ┆ 16
3821
+ # # └─────┴──────┴───────┴─────┘
3748
3822
  #
3749
3823
  # @example Added columns will replace existing columns with the same name.
3750
3824
  # df.with_columns(Polars.col("a").cast(Polars::Float64))
@@ -3771,16 +3845,16 @@ module Polars
3771
3845
  # )
3772
3846
  # # =>
3773
3847
  # # shape: (4, 6)
3774
- # # ┌─────┬──────┬───────┬──────┬──────┬───────┐
3775
- # # │ a ┆ b ┆ c ┆ a^2 ┆ b/2 ┆ not c │
3776
- # # │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
3777
- # # │ i64 ┆ f64 ┆ bool ┆ f64 ┆ f64 ┆ bool │
3778
- # # ╞═════╪══════╪═══════╪══════╪══════╪═══════╡
3779
- # # │ 1 ┆ 0.5 ┆ true ┆ 1.0 ┆ 0.25 ┆ false │
3780
- # # │ 2 ┆ 4.0 ┆ true ┆ 4.0 ┆ 2.0 ┆ false │
3781
- # # │ 3 ┆ 10.0 ┆ false ┆ 9.0 ┆ 5.0 ┆ true │
3782
- # # │ 4 ┆ 13.0 ┆ true ┆ 16.0 ┆ 6.5 ┆ false │
3783
- # # └─────┴──────┴───────┴──────┴──────┴───────┘
3848
+ # # ┌─────┬──────┬───────┬─────┬──────┬───────┐
3849
+ # # │ a ┆ b ┆ c ┆ a^2 ┆ b/2 ┆ not c │
3850
+ # # │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
3851
+ # # │ i64 ┆ f64 ┆ bool ┆ i64 ┆ f64 ┆ bool │
3852
+ # # ╞═════╪══════╪═══════╪═════╪══════╪═══════╡
3853
+ # # │ 1 ┆ 0.5 ┆ true ┆ 1 ┆ 0.25 ┆ false │
3854
+ # # │ 2 ┆ 4.0 ┆ true ┆ 4 ┆ 2.0 ┆ false │
3855
+ # # │ 3 ┆ 10.0 ┆ false ┆ 9 ┆ 5.0 ┆ true │
3856
+ # # │ 4 ┆ 13.0 ┆ true ┆ 16 ┆ 6.5 ┆ false │
3857
+ # # └─────┴──────┴───────┴─────┴──────┴───────┘
3784
3858
  #
3785
3859
  # @example Multiple columns also can be added using positional arguments instead of a list.
3786
3860
  # df.with_columns(
@@ -3790,16 +3864,16 @@ module Polars
3790
3864
  # )
3791
3865
  # # =>
3792
3866
  # # shape: (4, 6)
3793
- # # ┌─────┬──────┬───────┬──────┬──────┬───────┐
3794
- # # │ a ┆ b ┆ c ┆ a^2 ┆ b/2 ┆ not c │
3795
- # # │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
3796
- # # │ i64 ┆ f64 ┆ bool ┆ f64 ┆ f64 ┆ bool │
3797
- # # ╞═════╪══════╪═══════╪══════╪══════╪═══════╡
3798
- # # │ 1 ┆ 0.5 ┆ true ┆ 1.0 ┆ 0.25 ┆ false │
3799
- # # │ 2 ┆ 4.0 ┆ true ┆ 4.0 ┆ 2.0 ┆ false │
3800
- # # │ 3 ┆ 10.0 ┆ false ┆ 9.0 ┆ 5.0 ┆ true │
3801
- # # │ 4 ┆ 13.0 ┆ true ┆ 16.0 ┆ 6.5 ┆ false │
3802
- # # └─────┴──────┴───────┴──────┴──────┴───────┘
3867
+ # # ┌─────┬──────┬───────┬─────┬──────┬───────┐
3868
+ # # │ a ┆ b ┆ c ┆ a^2 ┆ b/2 ┆ not c │
3869
+ # # │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
3870
+ # # │ i64 ┆ f64 ┆ bool ┆ i64 ┆ f64 ┆ bool │
3871
+ # # ╞═════╪══════╪═══════╪═════╪══════╪═══════╡
3872
+ # # │ 1 ┆ 0.5 ┆ true ┆ 1 ┆ 0.25 ┆ false │
3873
+ # # │ 2 ┆ 4.0 ┆ true ┆ 4 ┆ 2.0 ┆ false │
3874
+ # # │ 3 ┆ 10.0 ┆ false ┆ 9 ┆ 5.0 ┆ true │
3875
+ # # │ 4 ┆ 13.0 ┆ true ┆ 16 ┆ 6.5 ┆ false │
3876
+ # # └─────┴──────┴───────┴─────┴──────┴───────┘
3803
3877
  #
3804
3878
  # @example Use keyword arguments to easily name your expression inputs.
3805
3879
  # df.with_columns(
@@ -1027,14 +1027,20 @@ module Polars
1027
1027
  # Different from `convert_time_zone`, this will also modify
1028
1028
  # the underlying timestamp,
1029
1029
  #
1030
- # @param tz [String]
1031
- # Time zone for the `Datetime` Series.
1030
+ # @param time_zone [String]
1031
+ # Time zone for the `Datetime` Series. Pass `nil` to unset time zone.
1032
+ # @param use_earliest [Boolean]
1033
+ # Determine how to deal with ambiguous datetimes.
1034
+ # @param ambiguous [String]
1035
+ # Determine how to deal with ambiguous datetimes.
1036
+ # @param non_existent [String]
1037
+ # Determine how to deal with non-existent datetimes.
1032
1038
  #
1033
1039
  # @return [Expr]
1034
- def replace_time_zone(tz, use_earliest: nil, ambiguous: "raise")
1040
+ def replace_time_zone(time_zone, use_earliest: nil, ambiguous: "raise", non_existent: "raise")
1035
1041
  ambiguous = Utils.rename_use_earliest_to_ambiguous(use_earliest, ambiguous)
1036
1042
  ambiguous = Polars.lit(ambiguous) unless ambiguous.is_a?(Expr)
1037
- Utils.wrap_expr(_rbexpr.dt_replace_time_zone(tz, ambiguous._rbexpr))
1043
+ Utils.wrap_expr(_rbexpr.dt_replace_time_zone(time_zone, ambiguous._rbexpr, non_existent))
1038
1044
  end
1039
1045
 
1040
1046
  # Extract the days from a Duration type.
@@ -910,8 +910,14 @@ module Polars
910
910
  # Different from `with_time_zone`, this will also modify
911
911
  # the underlying timestamp.
912
912
  #
913
- # @param tz [String]
914
- # Time zone for the `Datetime` Series.
913
+ # @param time_zone [String]
914
+ # Time zone for the `Datetime` Series. Pass `nil` to unset time zone.
915
+ # @param use_earliest [Boolean]
916
+ # Determine how to deal with ambiguous datetimes.
917
+ # @param ambiguous [String]
918
+ # Determine how to deal with ambiguous datetimes.
919
+ # @param non_existent [String]
920
+ # Determine how to deal with non-existent datetimes.
915
921
  #
916
922
  # @return [Series]
917
923
  #
@@ -982,7 +988,7 @@ module Polars
982
988
  # # 1585717200
983
989
  # # 1588309200
984
990
  # # ]
985
- def replace_time_zone(tz)
991
+ def replace_time_zone(time_zone, use_earliest: nil, ambiguous: "raise", non_existent: "raise")
986
992
  super
987
993
  end
988
994