polars-df 0.15.0 → 0.26.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 (206) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +274 -0
  3. data/Cargo.lock +1465 -867
  4. data/Cargo.toml +3 -0
  5. data/LICENSE.txt +2 -2
  6. data/README.md +87 -37
  7. data/ext/polars/Cargo.toml +47 -16
  8. data/ext/polars/src/c_api/allocator.rs +7 -0
  9. data/ext/polars/src/c_api/mod.rs +1 -0
  10. data/ext/polars/src/catalog/mod.rs +1 -0
  11. data/ext/polars/src/catalog/unity.rs +470 -0
  12. data/ext/polars/src/conversion/any_value.rs +99 -84
  13. data/ext/polars/src/conversion/categorical.rs +30 -0
  14. data/ext/polars/src/conversion/chunked_array.rs +71 -62
  15. data/ext/polars/src/conversion/datetime.rs +63 -0
  16. data/ext/polars/src/conversion/mod.rs +796 -312
  17. data/ext/polars/src/dataframe/construction.rs +6 -18
  18. data/ext/polars/src/dataframe/export.rs +30 -39
  19. data/ext/polars/src/dataframe/general.rs +294 -362
  20. data/ext/polars/src/dataframe/io.rs +33 -150
  21. data/ext/polars/src/dataframe/map.rs +175 -0
  22. data/ext/polars/src/dataframe/mod.rs +37 -5
  23. data/ext/polars/src/dataframe/serde.rs +23 -8
  24. data/ext/polars/src/error.rs +44 -7
  25. data/ext/polars/src/exceptions.rs +21 -8
  26. data/ext/polars/src/expr/array.rs +86 -22
  27. data/ext/polars/src/expr/binary.rs +50 -1
  28. data/ext/polars/src/expr/bitwise.rs +39 -0
  29. data/ext/polars/src/expr/categorical.rs +20 -0
  30. data/ext/polars/src/expr/datatype.rs +51 -0
  31. data/ext/polars/src/expr/datetime.rs +99 -41
  32. data/ext/polars/src/expr/extension.rs +12 -0
  33. data/ext/polars/src/expr/general.rs +252 -128
  34. data/ext/polars/src/expr/list.rs +56 -60
  35. data/ext/polars/src/expr/meta.rs +30 -35
  36. data/ext/polars/src/expr/mod.rs +28 -6
  37. data/ext/polars/src/expr/name.rs +29 -14
  38. data/ext/polars/src/expr/rolling.rs +111 -3
  39. data/ext/polars/src/expr/selector.rs +219 -0
  40. data/ext/polars/src/expr/serde.rs +28 -0
  41. data/ext/polars/src/expr/string.rs +118 -20
  42. data/ext/polars/src/expr/struct.rs +14 -1
  43. data/ext/polars/src/file.rs +194 -86
  44. data/ext/polars/src/functions/aggregation.rs +13 -12
  45. data/ext/polars/src/functions/business.rs +2 -3
  46. data/ext/polars/src/functions/eager.rs +3 -2
  47. data/ext/polars/src/functions/io.rs +90 -18
  48. data/ext/polars/src/functions/lazy.rs +267 -118
  49. data/ext/polars/src/functions/meta.rs +8 -7
  50. data/ext/polars/src/functions/misc.rs +1 -1
  51. data/ext/polars/src/functions/mod.rs +2 -1
  52. data/ext/polars/src/functions/range.rs +88 -31
  53. data/ext/polars/src/functions/strings.rs +6 -0
  54. data/ext/polars/src/functions/utils.rs +8 -0
  55. data/ext/polars/src/interop/arrow/mod.rs +52 -1
  56. data/ext/polars/src/interop/arrow/{to_ruby.rs → to_rb.rs} +37 -7
  57. data/ext/polars/src/interop/arrow/to_rust.rs +43 -0
  58. data/ext/polars/src/interop/numo/to_numo_df.rs +1 -1
  59. data/ext/polars/src/interop/numo/to_numo_series.rs +72 -50
  60. data/ext/polars/src/io/cloud_options.rs +107 -0
  61. data/ext/polars/src/io/mod.rs +4 -0
  62. data/ext/polars/src/io/scan_options.rs +113 -0
  63. data/ext/polars/src/io/sink_options.rs +46 -0
  64. data/ext/polars/src/io/sink_output.rs +21 -0
  65. data/ext/polars/src/lazyframe/exitable.rs +39 -0
  66. data/ext/polars/src/lazyframe/general.rs +846 -368
  67. data/ext/polars/src/lazyframe/mod.rs +58 -5
  68. data/ext/polars/src/lazyframe/optflags.rs +59 -0
  69. data/ext/polars/src/lazyframe/serde.rs +36 -4
  70. data/ext/polars/src/lazyframe/sink.rs +46 -0
  71. data/ext/polars/src/lazygroupby.rs +38 -9
  72. data/ext/polars/src/lib.rs +574 -165
  73. data/ext/polars/src/map/lazy.rs +44 -74
  74. data/ext/polars/src/map/mod.rs +18 -254
  75. data/ext/polars/src/map/series.rs +241 -1087
  76. data/ext/polars/src/on_startup.rs +192 -9
  77. data/ext/polars/src/prelude.rs +1 -0
  78. data/ext/polars/src/rb_modules.rs +10 -57
  79. data/ext/polars/src/ruby/exceptions.rs +26 -0
  80. data/ext/polars/src/ruby/gvl.rs +104 -0
  81. data/ext/polars/src/ruby/lazy.rs +46 -0
  82. data/ext/polars/src/ruby/mod.rs +11 -0
  83. data/ext/polars/src/ruby/numo.rs +52 -0
  84. data/ext/polars/src/ruby/plan_callback.rs +198 -0
  85. data/ext/polars/src/ruby/rb_modules.rs +16 -0
  86. data/ext/polars/src/ruby/ruby_convert_registry.rs +51 -0
  87. data/ext/polars/src/ruby/ruby_function.rs +11 -0
  88. data/ext/polars/src/ruby/ruby_udf.rs +164 -0
  89. data/ext/polars/src/ruby/thread.rs +65 -0
  90. data/ext/polars/src/ruby/utils.rs +39 -0
  91. data/ext/polars/src/series/aggregation.rs +116 -91
  92. data/ext/polars/src/series/arithmetic.rs +16 -22
  93. data/ext/polars/src/series/comparison.rs +101 -222
  94. data/ext/polars/src/series/construction.rs +80 -70
  95. data/ext/polars/src/series/export.rs +98 -56
  96. data/ext/polars/src/series/general.rs +323 -440
  97. data/ext/polars/src/series/import.rs +22 -5
  98. data/ext/polars/src/series/map.rs +103 -0
  99. data/ext/polars/src/series/mod.rs +57 -15
  100. data/ext/polars/src/series/scatter.rs +139 -82
  101. data/ext/polars/src/sql.rs +16 -9
  102. data/ext/polars/src/testing/frame.rs +31 -0
  103. data/ext/polars/src/testing/mod.rs +5 -0
  104. data/ext/polars/src/testing/series.rs +31 -0
  105. data/ext/polars/src/timeout.rs +105 -0
  106. data/ext/polars/src/utils.rs +105 -4
  107. data/lib/polars/array_expr.rb +500 -22
  108. data/lib/polars/array_name_space.rb +384 -10
  109. data/lib/polars/batched_csv_reader.rb +48 -66
  110. data/lib/polars/binary_expr.rb +217 -0
  111. data/lib/polars/binary_name_space.rb +155 -1
  112. data/lib/polars/cat_expr.rb +224 -0
  113. data/lib/polars/cat_name_space.rb +132 -32
  114. data/lib/polars/catalog/unity/catalog_info.rb +20 -0
  115. data/lib/polars/catalog/unity/column_info.rb +31 -0
  116. data/lib/polars/catalog/unity/namespace_info.rb +21 -0
  117. data/lib/polars/catalog/unity/table_info.rb +50 -0
  118. data/lib/polars/catalog.rb +448 -0
  119. data/lib/polars/collect_batches.rb +22 -0
  120. data/lib/polars/config.rb +3 -3
  121. data/lib/polars/convert.rb +201 -36
  122. data/lib/polars/data_frame.rb +2851 -1017
  123. data/lib/polars/data_frame_plot.rb +173 -0
  124. data/lib/polars/data_type_expr.rb +52 -0
  125. data/lib/polars/data_type_group.rb +6 -0
  126. data/lib/polars/data_types.rb +118 -18
  127. data/lib/polars/date_time_expr.rb +426 -84
  128. data/lib/polars/date_time_name_space.rb +384 -111
  129. data/lib/polars/dynamic_group_by.rb +102 -10
  130. data/lib/polars/exceptions.rb +50 -5
  131. data/lib/polars/expr.rb +2159 -915
  132. data/lib/polars/extension_expr.rb +39 -0
  133. data/lib/polars/extension_name_space.rb +39 -0
  134. data/lib/polars/functions/aggregation/horizontal.rb +11 -6
  135. data/lib/polars/functions/aggregation/vertical.rb +2 -3
  136. data/lib/polars/functions/as_datatype.rb +290 -8
  137. data/lib/polars/functions/business.rb +95 -0
  138. data/lib/polars/functions/col.rb +6 -5
  139. data/lib/polars/functions/datatype.rb +62 -0
  140. data/lib/polars/functions/eager.rb +426 -24
  141. data/lib/polars/functions/escape_regex.rb +21 -0
  142. data/lib/polars/functions/lazy.rb +813 -195
  143. data/lib/polars/functions/lit.rb +21 -10
  144. data/lib/polars/functions/range/int_range.rb +74 -2
  145. data/lib/polars/functions/range/linear_space.rb +195 -0
  146. data/lib/polars/functions/range/time_range.rb +1 -1
  147. data/lib/polars/functions/repeat.rb +7 -12
  148. data/lib/polars/functions/whenthen.rb +2 -2
  149. data/lib/polars/group_by.rb +188 -58
  150. data/lib/polars/iceberg_dataset.rb +108 -0
  151. data/lib/polars/in_process_query.rb +37 -0
  152. data/lib/polars/io/cloud.rb +18 -0
  153. data/lib/polars/io/csv.rb +336 -128
  154. data/lib/polars/io/database.rb +19 -4
  155. data/lib/polars/io/delta.rb +134 -0
  156. data/lib/polars/io/iceberg.rb +34 -0
  157. data/lib/polars/io/ipc.rb +63 -63
  158. data/lib/polars/io/json.rb +16 -0
  159. data/lib/polars/io/lines.rb +172 -0
  160. data/lib/polars/io/ndjson.rb +176 -20
  161. data/lib/polars/io/parquet.rb +173 -95
  162. data/lib/polars/io/scan_options.rb +55 -0
  163. data/lib/polars/io/sink_options.rb +27 -0
  164. data/lib/polars/io/utils.rb +17 -0
  165. data/lib/polars/lazy_frame.rb +3017 -622
  166. data/lib/polars/lazy_group_by.rb +436 -2
  167. data/lib/polars/list_expr.rb +551 -59
  168. data/lib/polars/list_name_space.rb +465 -51
  169. data/lib/polars/meta_expr.rb +146 -24
  170. data/lib/polars/name_expr.rb +87 -2
  171. data/lib/polars/query_opt_flags.rb +264 -0
  172. data/lib/polars/rolling_group_by.rb +90 -5
  173. data/lib/polars/scan_cast_options.rb +86 -0
  174. data/lib/polars/schema.rb +128 -0
  175. data/lib/polars/selector.rb +245 -0
  176. data/lib/polars/selectors.rb +1048 -201
  177. data/lib/polars/series.rb +2522 -774
  178. data/lib/polars/series_plot.rb +72 -0
  179. data/lib/polars/slice.rb +1 -1
  180. data/lib/polars/sql_context.rb +13 -6
  181. data/lib/polars/string_cache.rb +19 -72
  182. data/lib/polars/string_expr.rb +561 -107
  183. data/lib/polars/string_name_space.rb +781 -109
  184. data/lib/polars/struct_expr.rb +139 -18
  185. data/lib/polars/struct_name_space.rb +19 -1
  186. data/lib/polars/testing.rb +24 -273
  187. data/lib/polars/utils/constants.rb +2 -0
  188. data/lib/polars/utils/construction/data_frame.rb +410 -0
  189. data/lib/polars/utils/construction/series.rb +350 -0
  190. data/lib/polars/utils/construction/utils.rb +9 -0
  191. data/lib/polars/utils/convert.rb +18 -8
  192. data/lib/polars/utils/deprecation.rb +11 -0
  193. data/lib/polars/utils/parse.rb +62 -9
  194. data/lib/polars/utils/reduce_balanced.rb +43 -0
  195. data/lib/polars/utils/serde.rb +22 -0
  196. data/lib/polars/utils/unstable.rb +19 -0
  197. data/lib/polars/utils/various.rb +86 -1
  198. data/lib/polars/utils.rb +63 -48
  199. data/lib/polars/version.rb +1 -1
  200. data/lib/polars.rb +85 -2
  201. metadata +80 -28
  202. data/ext/polars/src/allocator.rs +0 -13
  203. data/ext/polars/src/batched_csv.rs +0 -138
  204. data/ext/polars/src/functions/string_cache.rs +0 -25
  205. data/ext/polars/src/map/dataframe.rs +0 -338
  206. data/lib/polars/plot.rb +0 -109
@@ -0,0 +1,39 @@
1
+ module Polars
2
+ # Namespace for extension type related expressions.
3
+ class ExtensionExpr
4
+ # @private
5
+ attr_accessor :_rbexpr
6
+
7
+ # @private
8
+ def initialize(expr)
9
+ self._rbexpr = expr._rbexpr
10
+ end
11
+
12
+ # Convert to an extension `dtype`.
13
+ #
14
+ # The input must be of the storage type of the extension dtype.
15
+ #
16
+ # @note
17
+ # This functionality is currently considered **unstable**. It may be
18
+ # changed at any point without it being considered a breaking change.
19
+ #
20
+ # @return [Expr]
21
+ def to(dtype)
22
+ rb_dtype = Utils.parse_into_datatype_expr(dtype)._rbdatatype_expr
23
+ Utils.wrap_expr(_rbexpr.ext_to(rb_dtype))
24
+ end
25
+
26
+ # Get the storage values of an extension data type.
27
+ #
28
+ # If the input does not have an extension data type, it is returned as-is.
29
+ #
30
+ # @note
31
+ # This functionality is currently considered **unstable**. It may be
32
+ # changed at any point without it being considered a breaking change.
33
+ #
34
+ # @return [Expr]
35
+ def storage
36
+ Utils.wrap_expr(_rbexpr.ext_storage)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ module Polars
2
+ # Series.ext namespace.
3
+ class ExtensionNameSpace
4
+ include ExprDispatch
5
+
6
+ self._accessor = "ext"
7
+
8
+ # @private
9
+ def initialize(series)
10
+ self._s = series._s
11
+ end
12
+
13
+ # Create a Series with an extension `dtype`.
14
+ #
15
+ # The input series must have the storage type of the extension dtype.
16
+ #
17
+ # @note
18
+ # This functionality is currently considered **unstable**. It may be
19
+ # changed at any point without it being considered a breaking change.
20
+ #
21
+ # @return [Series]
22
+ def to(dtype)
23
+ Utils.wrap_s(_s.ext_to(dtype))
24
+ end
25
+
26
+ # Get the storage values of a Series with an extension data type.
27
+ #
28
+ # If the input series does not have an extension data type, it is returned as-is.
29
+ #
30
+ # @note
31
+ # This functionality is currently considered **unstable**. It may be
32
+ # changed at any point without it being considered a breaking change.
33
+ #
34
+ # @return [Series]
35
+ def storage
36
+ Utils.wrap_s(_s.ext_storage)
37
+ end
38
+ end
39
+ end
@@ -143,6 +143,9 @@ module Polars
143
143
  # @param exprs [Array]
144
144
  # Column(s) to use in the aggregation. Accepts expression input. Strings are
145
145
  # parsed as column names, other non-expression inputs are parsed as literals.
146
+ # @param ignore_nulls [Boolean]
147
+ # Ignore null values (default).
148
+ # If set to `false`, any null value in the input will lead to a null output.
146
149
  #
147
150
  # @return [Expr]
148
151
  #
@@ -166,9 +169,9 @@ module Polars
166
169
  # # │ 8 ┆ 5 ┆ y ┆ 13 │
167
170
  # # │ 3 ┆ null ┆ z ┆ 3 │
168
171
  # # └─────┴──────┴─────┴─────┘
169
- def sum_horizontal(*exprs)
172
+ def sum_horizontal(*exprs, ignore_nulls: true)
170
173
  rbexprs = Utils.parse_into_list_of_expressions(*exprs)
171
- Utils.wrap_expr(Plr.sum_horizontal(rbexprs))
174
+ Utils.wrap_expr(Plr.sum_horizontal(rbexprs, ignore_nulls))
172
175
  end
173
176
 
174
177
  # Compute the mean of all values horizontally across columns.
@@ -176,6 +179,9 @@ module Polars
176
179
  # @param exprs [Array]
177
180
  # Column(s) to use in the aggregation. Accepts expression input. Strings are
178
181
  # parsed as column names, other non-expression inputs are parsed as literals.
182
+ # @param ignore_nulls [Boolean]
183
+ # Ignore null values (default).
184
+ # If set to `false`, any null value in the input will lead to a null output.
179
185
  #
180
186
  # @return [Expr]
181
187
  #
@@ -199,9 +205,9 @@ module Polars
199
205
  # # │ 8 ┆ 5 ┆ y ┆ 6.5 │
200
206
  # # │ 3 ┆ null ┆ z ┆ 3.0 │
201
207
  # # └─────┴──────┴─────┴──────┘
202
- def mean_horizontal(*exprs)
208
+ def mean_horizontal(*exprs, ignore_nulls: true)
203
209
  rbexprs = Utils.parse_into_list_of_expressions(*exprs)
204
- Utils.wrap_expr(Plr.mean_horizontal(rbexprs))
210
+ Utils.wrap_expr(Plr.mean_horizontal(rbexprs, ignore_nulls))
205
211
  end
206
212
 
207
213
  # Cumulatively sum all values horizontally across columns.
@@ -237,10 +243,9 @@ module Polars
237
243
  exprs_wrapped = rbexprs.map { |e| Utils.wrap_expr(e) }
238
244
 
239
245
  # (Expr): use u32 as that will not cast to float as eagerly
240
- Polars.cum_fold(Polars.lit(0).cast(UInt32), ->(a, b) { a + b }, exprs_wrapped).alias(
246
+ Polars.cum_fold(Polars.lit(0).cast(UInt32), exprs_wrapped) { |a, b| a + b }.alias(
241
247
  "cum_sum"
242
248
  )
243
249
  end
244
- alias_method :cumsum_horizontal, :cum_sum_horizontal
245
250
  end
246
251
  end
@@ -46,7 +46,7 @@ module Polars
46
46
  return col("*")
47
47
  end
48
48
 
49
- col(*names).all(drop_nulls: ignore_nulls)
49
+ col(*names).all(ignore_nulls: ignore_nulls)
50
50
  end
51
51
 
52
52
  # Evaluate a bitwise OR operation.
@@ -78,7 +78,7 @@ module Polars
78
78
  # # │ true │
79
79
  # # └──────┘
80
80
  def any(*names, ignore_nulls: true)
81
- col(*names).any(drop_nulls: ignore_nulls)
81
+ col(*names).any(ignore_nulls: ignore_nulls)
82
82
  end
83
83
 
84
84
  # Get the maximum value.
@@ -277,6 +277,5 @@ module Polars
277
277
  def cum_sum(*names)
278
278
  col(*names).cum_sum
279
279
  end
280
- alias_method :cumsum, :cum_sum
281
280
  end
282
281
  end
@@ -1,5 +1,233 @@
1
1
  module Polars
2
2
  module Functions
3
+ # Create a Polars literal expression of type Datetime.
4
+ #
5
+ # @param year [Object]
6
+ # Column or literal.
7
+ # @param month [Object]
8
+ # Column or literal, ranging from 1-12.
9
+ # @param day [Object]
10
+ # Column or literal, ranging from 1-31.
11
+ # @param hour [Object]
12
+ # Column or literal, ranging from 0-23.
13
+ # @param minute [Object]
14
+ # Column or literal, ranging from 0-59.
15
+ # @param second [Object]
16
+ # Column or literal, ranging from 0-59.
17
+ # @param microsecond [Object]
18
+ # Column or literal, ranging from 0-999999.
19
+ # @param time_unit ['us', 'ms', 'ns']
20
+ # Time unit of the resulting expression.
21
+ # @param time_zone [Object]
22
+ # Time zone of the resulting expression.
23
+ # @param ambiguous ['raise', 'earliest', 'latest', 'null']
24
+ # Determine how to deal with ambiguous datetimes:
25
+ #
26
+ # - `'raise'` (default): raise
27
+ # - `'earliest'`: use the earliest datetime
28
+ # - `'latest'`: use the latest datetime
29
+ # - `'null'`: set to null
30
+ #
31
+ # @return [Expr]
32
+ #
33
+ # @example
34
+ # df = Polars::DataFrame.new(
35
+ # {
36
+ # "month" => [1, 2, 3],
37
+ # "day" => [4, 5, 6],
38
+ # "hour" => [12, 13, 14],
39
+ # "minute" => [15, 30, 45]
40
+ # }
41
+ # )
42
+ # df.with_columns(
43
+ # Polars.datetime(
44
+ # 2024,
45
+ # Polars.col("month"),
46
+ # Polars.col("day"),
47
+ # Polars.col("hour"),
48
+ # Polars.col("minute"),
49
+ # time_zone: "Australia/Sydney"
50
+ # )
51
+ # )
52
+ # # =>
53
+ # # shape: (3, 5)
54
+ # # ┌───────┬─────┬──────┬────────┬────────────────────────────────┐
55
+ # # │ month ┆ day ┆ hour ┆ minute ┆ datetime │
56
+ # # │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
57
+ # # │ i64 ┆ i64 ┆ i64 ┆ i64 ┆ datetime[μs, Australia/Sydney] │
58
+ # # ╞═══════╪═════╪══════╪════════╪════════════════════════════════╡
59
+ # # │ 1 ┆ 4 ┆ 12 ┆ 15 ┆ 2024-01-04 12:15:00 AEDT │
60
+ # # │ 2 ┆ 5 ┆ 13 ┆ 30 ┆ 2024-02-05 13:30:00 AEDT │
61
+ # # │ 3 ┆ 6 ┆ 14 ┆ 45 ┆ 2024-03-06 14:45:00 AEDT │
62
+ # # └───────┴─────┴──────┴────────┴────────────────────────────────┘
63
+ #
64
+ # @example We can also use `Polars.datetime` for filtering:
65
+ # df = Polars::DataFrame.new(
66
+ # {
67
+ # "start" => [
68
+ # DateTime.new(2024, 1, 1, 0, 0, 0),
69
+ # DateTime.new(2024, 1, 1, 0, 0, 0),
70
+ # DateTime.new(2024, 1, 1, 0, 0, 0)
71
+ # ],
72
+ # "end" => [
73
+ # DateTime.new(2024, 5, 1, 20, 15, 10),
74
+ # DateTime.new(2024, 7, 1, 21, 25, 20),
75
+ # DateTime.new(2024, 9, 1, 22, 35, 30)
76
+ # ]
77
+ # }
78
+ # )
79
+ # df.filter(Polars.col("end") > Polars.datetime(2024, 6, 1))
80
+ # # =>
81
+ # # shape: (2, 2)
82
+ # # ┌─────────────────────┬─────────────────────┐
83
+ # # │ start ┆ end │
84
+ # # │ --- ┆ --- │
85
+ # # │ datetime[ns] ┆ datetime[ns] │
86
+ # # ╞═════════════════════╪═════════════════════╡
87
+ # # │ 2024-01-01 00:00:00 ┆ 2024-07-01 21:25:20 │
88
+ # # │ 2024-01-01 00:00:00 ┆ 2024-09-01 22:35:30 │
89
+ # # └─────────────────────┴─────────────────────┘
90
+ def datetime(
91
+ year,
92
+ month,
93
+ day,
94
+ hour = nil,
95
+ minute = nil,
96
+ second = nil,
97
+ microsecond = nil,
98
+ time_unit: "us",
99
+ time_zone: nil,
100
+ ambiguous: "raise"
101
+ )
102
+ ambiguous_expr = Utils.parse_into_expression(ambiguous, str_as_lit: true)
103
+ year_expr = Utils.parse_into_expression(year)
104
+ month_expr = Utils.parse_into_expression(month)
105
+ day_expr = Utils.parse_into_expression(day)
106
+
107
+ hour_expr = !hour.nil? ? Utils.parse_into_expression(hour) : nil
108
+ minute_expr = !minute.nil? ? Utils.parse_into_expression(minute) : nil
109
+ second_expr = !second.nil? ? Utils.parse_into_expression(second) : nil
110
+ microsecond_expr = (
111
+ !microsecond.nil? ? Utils.parse_into_expression(microsecond) : nil
112
+ )
113
+
114
+ Utils.wrap_expr(
115
+ Plr.datetime(
116
+ year_expr,
117
+ month_expr,
118
+ day_expr,
119
+ hour_expr,
120
+ minute_expr,
121
+ second_expr,
122
+ microsecond_expr,
123
+ time_unit,
124
+ time_zone,
125
+ ambiguous_expr
126
+ )
127
+ )
128
+ end
129
+
130
+ # Create a Polars literal expression of type Date.
131
+ #
132
+ # @param year [Object]
133
+ # column or literal.
134
+ # @param month [Object]
135
+ # column or literal, ranging from 1-12.
136
+ # @param day [Object]
137
+ # column or literal, ranging from 1-31.
138
+ #
139
+ # @return [Expr]
140
+ #
141
+ # @example
142
+ # df = Polars::DataFrame.new(
143
+ # {
144
+ # "month" => [1, 2, 3],
145
+ # "day" => [4, 5, 6]
146
+ # }
147
+ # )
148
+ # df.with_columns(Polars.date(2024, Polars.col("month"), Polars.col("day")))
149
+ # # =>
150
+ # # shape: (3, 3)
151
+ # # ┌───────┬─────┬────────────┐
152
+ # # │ month ┆ day ┆ date │
153
+ # # │ --- ┆ --- ┆ --- │
154
+ # # │ i64 ┆ i64 ┆ date │
155
+ # # ╞═══════╪═════╪════════════╡
156
+ # # │ 1 ┆ 4 ┆ 2024-01-04 │
157
+ # # │ 2 ┆ 5 ┆ 2024-02-05 │
158
+ # # │ 3 ┆ 6 ┆ 2024-03-06 │
159
+ # # └───────┴─────┴────────────┘
160
+ #
161
+ # @example We can also use `pl.date` for filtering:
162
+ # df = Polars::DataFrame.new(
163
+ # {
164
+ # "start" => [Date.new(2024, 1, 1), Date.new(2024, 1, 1), Date.new(2024, 1, 1)],
165
+ # "end" => [Date.new(2024, 5, 1), Date.new(2024, 7, 1), Date.new(2024, 9, 1)]
166
+ # }
167
+ # )
168
+ # df.filter(Polars.col("end") > Polars.date(2024, 6, 1))
169
+ # # =>
170
+ # # shape: (2, 2)
171
+ # # ┌────────────┬────────────┐
172
+ # # │ start ┆ end │
173
+ # # │ --- ┆ --- │
174
+ # # │ date ┆ date │
175
+ # # ╞════════════╪════════════╡
176
+ # # │ 2024-01-01 ┆ 2024-07-01 │
177
+ # # │ 2024-01-01 ┆ 2024-09-01 │
178
+ # # └────────────┴────────────┘
179
+ def date(
180
+ year,
181
+ month,
182
+ day
183
+ )
184
+ datetime(year, month, day).cast(Date).alias("date")
185
+ end
186
+
187
+ # Create a Polars literal expression of type Time.
188
+ #
189
+ # @param hour [Object]
190
+ # column or literal, ranging from 0-23.
191
+ # @param minute [Object]
192
+ # column or literal, ranging from 0-59.
193
+ # @param second [Object]
194
+ # column or literal, ranging from 0-59.
195
+ # @param microsecond [Object]
196
+ # column or literal, ranging from 0-999999.
197
+ #
198
+ # @return [Expr]
199
+ #
200
+ # @example
201
+ # df = Polars::DataFrame.new(
202
+ # {
203
+ # "hour" => [12, 13, 14],
204
+ # "minute" => [15, 30, 45]
205
+ # }
206
+ # )
207
+ # df.with_columns(Polars.time(Polars.col("hour"), Polars.col("minute")))
208
+ # # =>
209
+ # # shape: (3, 3)
210
+ # # ┌──────┬────────┬──────────┐
211
+ # # │ hour ┆ minute ┆ time │
212
+ # # │ --- ┆ --- ┆ --- │
213
+ # # │ i64 ┆ i64 ┆ time │
214
+ # # ╞══════╪════════╪══════════╡
215
+ # # │ 12 ┆ 15 ┆ 12:15:00 │
216
+ # # │ 13 ┆ 30 ┆ 13:30:00 │
217
+ # # │ 14 ┆ 45 ┆ 14:45:00 │
218
+ # # └──────┴────────┴──────────┘
219
+ def time(
220
+ hour = nil,
221
+ minute = nil,
222
+ second = nil,
223
+ microsecond = nil
224
+ )
225
+ epoch_start = [1970, 1, 1]
226
+ datetime(*epoch_start, hour, minute, second, microsecond)
227
+ .cast(Time)
228
+ .alias("time")
229
+ end
230
+
3
231
  # Create polars `Duration` from distinct time components.
4
232
  #
5
233
  # @return [Expr]
@@ -41,8 +269,12 @@ module Polars
41
269
  milliseconds: nil,
42
270
  microseconds: nil,
43
271
  nanoseconds: nil,
44
- time_unit: "us"
272
+ time_unit: nil
45
273
  )
274
+ if !nanoseconds.nil? && time_unit.nil?
275
+ time_unit = "ns"
276
+ end
277
+
46
278
  if !weeks.nil?
47
279
  weeks = Utils.parse_into_expression(weeks, str_as_lit: false)
48
280
  end
@@ -68,6 +300,10 @@ module Polars
68
300
  nanoseconds = Utils.parse_into_expression(nanoseconds, str_as_lit: false)
69
301
  end
70
302
 
303
+ if time_unit.nil?
304
+ time_unit = "us"
305
+ end
306
+
71
307
  Utils.wrap_expr(
72
308
  Plr.duration(
73
309
  weeks,
@@ -140,6 +376,49 @@ module Polars
140
376
  Utils.wrap_expr(Plr.concat_list(exprs))
141
377
  end
142
378
 
379
+ # Horizontally concatenate columns into a single array column.
380
+ #
381
+ # Non-array columns are reshaped to a unit-width array. All columns must have
382
+ # a dtype of either `Polars::Array.new(<DataType>, width)` or `Polars::<DataType>`.
383
+ #
384
+ # @note
385
+ # This functionality is considered **unstable**. It may be changed
386
+ # at any point without it being considered a breaking change.
387
+ #
388
+ # @param exprs [Object]
389
+ # Columns to concatenate into a single array column. Accepts expression input.
390
+ # Strings are parsed as column names, other non-expression inputs are parsed as
391
+ # literals.
392
+ # @param more_exprs [Array]
393
+ # Additional columns to concatenate into a single array column, specified as
394
+ # positional arguments.
395
+ #
396
+ # @return [Expr]
397
+ #
398
+ # @example Concatenate 2 array columns:
399
+ # Polars.select(
400
+ # a: Polars::Series.new([[1], [3], nil], dtype: Polars::Array.new(Polars::Int64, 1)),
401
+ # b: Polars::Series.new([[3], [nil], [5]], dtype: Polars::Array.new(Polars::Int64, 1))
402
+ # ).with_columns(
403
+ # Polars.concat_arr("a", "b").alias("concat_arr(a, b)"),
404
+ # Polars.concat_arr("a", Polars.first("b")).alias("concat_arr(a, first(b))")
405
+ # )
406
+ # # =>
407
+ # # shape: (3, 4)
408
+ # # ┌───────────────┬───────────────┬──────────────────┬─────────────────────────┐
409
+ # # │ a ┆ b ┆ concat_arr(a, b) ┆ concat_arr(a, first(b)) │
410
+ # # │ --- ┆ --- ┆ --- ┆ --- │
411
+ # # │ array[i64, 1] ┆ array[i64, 1] ┆ array[i64, 2] ┆ array[i64, 2] │
412
+ # # ╞═══════════════╪═══════════════╪══════════════════╪═════════════════════════╡
413
+ # # │ [1] ┆ [3] ┆ [1, 3] ┆ [1, 3] │
414
+ # # │ [3] ┆ [null] ┆ [3, null] ┆ [3, 3] │
415
+ # # │ null ┆ [5] ┆ null ┆ null │
416
+ # # └───────────────┴───────────────┴──────────────────┴─────────────────────────┘
417
+ def concat_arr(exprs, *more_exprs)
418
+ exprs = Utils.parse_into_list_of_expressions(exprs, *more_exprs)
419
+ Utils.wrap_expr(Plr.concat_arr(exprs))
420
+ end
421
+
143
422
  # Collect several columns into a Series of dtype Struct.
144
423
  #
145
424
  # @param exprs [Array]
@@ -194,7 +473,7 @@ module Polars
194
473
  #
195
474
  # @example Use keyword arguments to easily name each struct field.
196
475
  # df.select(Polars.struct(p: "int", q: "bool").alias("my_struct")).schema
197
- # # => {"my_struct"=>Polars::Struct({"p"=>Polars::Int64, "q"=>Polars::Boolean})}
476
+ # # => Polars::Schema({"my_struct"=>Polars::Struct({"p"=>Polars::Int64, "q"=>Polars::Boolean})})
198
477
  def struct(*exprs, schema: nil, eager: false, **named_exprs)
199
478
  rbexprs = Utils.parse_into_list_of_expressions(*exprs, **named_exprs)
200
479
  expr = Utils.wrap_expr(Plr.as_struct(rbexprs))
@@ -221,7 +500,10 @@ module Polars
221
500
  #
222
501
  # @param exprs [Object]
223
502
  # Columns to concat into a Utf8 Series.
224
- # @param sep [String]
503
+ # @param more_exprs [Array]
504
+ # Additional columns to concatenate into a single string column, specified as
505
+ # positional arguments.
506
+ # @param separator [String]
225
507
  # String value that will be used to separate the values.
226
508
  # @param ignore_nulls [Boolean]
227
509
  # Ignore null values (default).
@@ -244,7 +526,7 @@ module Polars
244
526
  # Polars.col("b"),
245
527
  # Polars.col("c")
246
528
  # ],
247
- # sep: " "
529
+ # separator: " "
248
530
  # ).alias("full_sentence")
249
531
  # ]
250
532
  # )
@@ -259,9 +541,9 @@ module Polars
259
541
  # # │ 2 ┆ cats ┆ swim ┆ 4 cats swim │
260
542
  # # │ 3 ┆ null ┆ walk ┆ null │
261
543
  # # └─────┴──────┴──────┴───────────────┘
262
- def concat_str(exprs, sep: "", ignore_nulls: false)
263
- exprs = Utils.parse_into_list_of_expressions(exprs)
264
- Utils.wrap_expr(Plr.concat_str(exprs, sep, ignore_nulls))
544
+ def concat_str(exprs, *more_exprs, separator: "", ignore_nulls: false)
545
+ exprs = Utils.parse_into_list_of_expressions(exprs, *more_exprs)
546
+ Utils.wrap_expr(Plr.concat_str(exprs, separator, ignore_nulls))
265
547
  end
266
548
 
267
549
  # Format expressions as a string.
@@ -314,7 +596,7 @@ module Polars
314
596
  end
315
597
  end
316
598
 
317
- concat_str(exprs, sep: "")
599
+ concat_str(exprs, separator: "")
318
600
  end
319
601
  end
320
602
  end
@@ -0,0 +1,95 @@
1
+ module Polars
2
+ module Functions
3
+ # Count the number of business days between `start` and `end` (not including `end`).
4
+ #
5
+ # @note
6
+ # This functionality is considered **unstable**. It may be changed
7
+ # at any point without it being considered a breaking change.
8
+ #
9
+ # @param start [Object]
10
+ # Start dates.
11
+ # @param stop [Object]
12
+ # End dates.
13
+ # @param week_mask [Array]
14
+ # Which days of the week to count. The default is Monday to Friday.
15
+ # If you wanted to count only Monday to Thursday, you would pass
16
+ # `[true, true, true, true, false, false, false]`.
17
+ # @param holidays [Array]
18
+ # Holidays to exclude from the count.
19
+ #
20
+ # @return [Expr]
21
+ #
22
+ # @example
23
+ # df = Polars::DataFrame.new(
24
+ # {
25
+ # "start" => [Date.new(2020, 1, 1), Date.new(2020, 1, 2)],
26
+ # "end" => [Date.new(2020, 1, 2), Date.new(2020, 1, 10)]
27
+ # }
28
+ # )
29
+ # df.with_columns(
30
+ # business_day_count: Polars.business_day_count("start", "end")
31
+ # )
32
+ # # =>
33
+ # # shape: (2, 3)
34
+ # # ┌────────────┬────────────┬────────────────────┐
35
+ # # │ start ┆ end ┆ business_day_count │
36
+ # # │ --- ┆ --- ┆ --- │
37
+ # # │ date ┆ date ┆ i32 │
38
+ # # ╞════════════╪════════════╪════════════════════╡
39
+ # # │ 2020-01-01 ┆ 2020-01-02 ┆ 1 │
40
+ # # │ 2020-01-02 ┆ 2020-01-10 ┆ 6 │
41
+ # # └────────────┴────────────┴────────────────────┘
42
+ #
43
+ # @example You can pass a custom weekend - for example, if you only take Sunday off:
44
+ # week_mask = [true, true, true, true, true, true, false]
45
+ # df.with_columns(
46
+ # business_day_count: Polars.business_day_count(
47
+ # "start", "end", week_mask: week_mask
48
+ # )
49
+ # )
50
+ # # =>
51
+ # # shape: (2, 3)
52
+ # # ┌────────────┬────────────┬────────────────────┐
53
+ # # │ start ┆ end ┆ business_day_count │
54
+ # # │ --- ┆ --- ┆ --- │
55
+ # # │ date ┆ date ┆ i32 │
56
+ # # ╞════════════╪════════════╪════════════════════╡
57
+ # # │ 2020-01-01 ┆ 2020-01-02 ┆ 1 │
58
+ # # │ 2020-01-02 ┆ 2020-01-10 ┆ 7 │
59
+ # # └────────────┴────────────┴────────────────────┘
60
+ #
61
+ # @example You can also pass a list of holidays to exclude from the count:
62
+ # holidays = [Date.new(2020, 1, 1), Date.new(2020, 1, 2)]
63
+ # df.with_columns(
64
+ # business_day_count: Polars.business_day_count("start", "end", holidays: holidays)
65
+ # )
66
+ # # =>
67
+ # # shape: (2, 3)
68
+ # # ┌────────────┬────────────┬────────────────────┐
69
+ # # │ start ┆ end ┆ business_day_count │
70
+ # # │ --- ┆ --- ┆ --- │
71
+ # # │ date ┆ date ┆ i32 │
72
+ # # ╞════════════╪════════════╪════════════════════╡
73
+ # # │ 2020-01-01 ┆ 2020-01-02 ┆ 0 │
74
+ # # │ 2020-01-02 ┆ 2020-01-10 ┆ 5 │
75
+ # # └────────────┴────────────┴────────────────────┘
76
+ def business_day_count(
77
+ start,
78
+ stop,
79
+ week_mask: [true, true, true, true, true, false, false],
80
+ holidays: []
81
+ )
82
+ start_rbexpr = Utils.parse_into_expression(start)
83
+ end_rbexpr = Utils.parse_into_expression(stop)
84
+ holidays_rbexpr = Utils._holidays_to_expr(holidays)
85
+ Utils.wrap_expr(
86
+ Plr.business_day_count(
87
+ start_rbexpr,
88
+ end_rbexpr,
89
+ week_mask,
90
+ holidays_rbexpr
91
+ )
92
+ )
93
+ end
94
+ end
95
+ end
@@ -8,11 +8,11 @@ module Polars
8
8
  if Utils.strlike?(name)
9
9
  names_str = [name]
10
10
  names_str.concat(more_names)
11
- return Utils.wrap_expr(Plr.cols(names_str.map(&:to_s)))
11
+ return Selector._by_name(names_str.map(&:to_s), strict: true, expand_patterns: true).as_expr
12
12
  elsif Utils.is_polars_dtype(name)
13
13
  dtypes = [name]
14
14
  dtypes.concat(more_names)
15
- return Utils.wrap_expr(Plr.dtype_cols(dtypes))
15
+ return Selector._by_type(dtypes).as_expr
16
16
  else
17
17
  msg = "invalid input for `col`\n\nExpected `str` or `DataType`, got #{name.class.name}."
18
18
  raise TypeError, msg
@@ -22,7 +22,8 @@ module Polars
22
22
  if Utils.strlike?(name)
23
23
  Utils.wrap_expr(Plr.col(name.to_s))
24
24
  elsif Utils.is_polars_dtype(name)
25
- Utils.wrap_expr(Plr.dtype_cols([name]))
25
+ dtypes = [name]
26
+ Selector._by_dtype(dtypes).as_expr
26
27
  elsif name.is_a?(::Array) || name.is_a?(::Set)
27
28
  names = Array(name)
28
29
  if names.empty?
@@ -31,9 +32,9 @@ module Polars
31
32
 
32
33
  item = names[0]
33
34
  if Utils.strlike?(item)
34
- Utils.wrap_expr(Plr.cols(names.map(&:to_s)))
35
+ Selector._by_name(names.map(&:to_s), strict: true, expand_patterns: true).as_expr
35
36
  elsif Utils.is_polars_dtype(item)
36
- Utils.wrap_expr(Plr.dtype_cols(names))
37
+ Selector._by_dtype(names).as_expr
37
38
  else
38
39
  msg = "invalid input for `col`\n\nExpected iterable of type `str` or `DataType`, got iterable of type #{item.class.name}."
39
40
  raise TypeError, msg