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
data/lib/polars/expr.rb CHANGED
@@ -1,9 +1,6 @@
1
1
  module Polars
2
2
  # Expressions that can be used in various contexts.
3
3
  class Expr
4
- # @private
5
- NO_DEFAULT = Object.new
6
-
7
4
  # @private
8
5
  attr_accessor :_rbexpr
9
6
 
@@ -26,56 +23,59 @@ module Polars
26
23
  #
27
24
  # @return [Expr]
28
25
  def ^(other)
29
- _from_rbexpr(_rbexpr._xor(_to_rbexpr(other)))
26
+ other = Utils.parse_into_expression(other)
27
+ wrap_expr(_rbexpr.xor_(other))
30
28
  end
31
29
 
32
30
  # Bitwise AND.
33
31
  #
34
32
  # @return [Expr]
35
33
  def &(other)
36
- _from_rbexpr(_rbexpr._and(_to_rbexpr(other)))
34
+ other = Utils.parse_into_expression(other)
35
+ wrap_expr(_rbexpr.and_(other))
37
36
  end
38
37
 
39
38
  # Bitwise OR.
40
39
  #
41
40
  # @return [Expr]
42
41
  def |(other)
43
- _from_rbexpr(_rbexpr._or(_to_rbexpr(other)))
42
+ other = Utils.parse_into_expression(other)
43
+ wrap_expr(_rbexpr.or_(other))
44
44
  end
45
45
 
46
46
  # Performs addition.
47
47
  #
48
48
  # @return [Expr]
49
49
  def +(other)
50
- _from_rbexpr(_rbexpr + _to_rbexpr(other))
50
+ wrap_expr(_rbexpr + _to_rbexpr(other))
51
51
  end
52
52
 
53
53
  # Performs subtraction.
54
54
  #
55
55
  # @return [Expr]
56
56
  def -(other)
57
- _from_rbexpr(_rbexpr - _to_rbexpr(other))
57
+ wrap_expr(_rbexpr - _to_rbexpr(other))
58
58
  end
59
59
 
60
60
  # Performs multiplication.
61
61
  #
62
62
  # @return [Expr]
63
63
  def *(other)
64
- _from_rbexpr(_rbexpr * _to_rbexpr(other))
64
+ wrap_expr(_rbexpr * _to_rbexpr(other))
65
65
  end
66
66
 
67
67
  # Performs division.
68
68
  #
69
69
  # @return [Expr]
70
70
  def /(other)
71
- _from_rbexpr(_rbexpr / _to_rbexpr(other))
71
+ wrap_expr(_rbexpr / _to_rbexpr(other))
72
72
  end
73
73
 
74
74
  # Returns the modulo.
75
75
  #
76
76
  # @return [Expr]
77
77
  def %(other)
78
- _from_rbexpr(_rbexpr % _to_rbexpr(other))
78
+ wrap_expr(_rbexpr % _to_rbexpr(other))
79
79
  end
80
80
 
81
81
  # Raises to the power of exponent.
@@ -83,49 +83,49 @@ module Polars
83
83
  # @return [Expr]
84
84
  def **(power)
85
85
  exponent = Utils.parse_into_expression(power)
86
- _from_rbexpr(_rbexpr.pow(exponent))
86
+ wrap_expr(_rbexpr.pow(exponent))
87
87
  end
88
88
 
89
89
  # Greater than or equal.
90
90
  #
91
91
  # @return [Expr]
92
92
  def >=(other)
93
- _from_rbexpr(_rbexpr.gt_eq(_to_expr(other)._rbexpr))
93
+ wrap_expr(_rbexpr.gt_eq(_to_expr(other)._rbexpr))
94
94
  end
95
95
 
96
96
  # Less than or equal.
97
97
  #
98
98
  # @return [Expr]
99
99
  def <=(other)
100
- _from_rbexpr(_rbexpr.lt_eq(_to_expr(other)._rbexpr))
100
+ wrap_expr(_rbexpr.lt_eq(_to_expr(other)._rbexpr))
101
101
  end
102
102
 
103
103
  # Equal.
104
104
  #
105
105
  # @return [Expr]
106
106
  def ==(other)
107
- _from_rbexpr(_rbexpr.eq(_to_expr(other)._rbexpr))
107
+ wrap_expr(_rbexpr.eq(_to_expr(other)._rbexpr))
108
108
  end
109
109
 
110
110
  # Not equal.
111
111
  #
112
112
  # @return [Expr]
113
113
  def !=(other)
114
- _from_rbexpr(_rbexpr.neq(_to_expr(other)._rbexpr))
114
+ wrap_expr(_rbexpr.neq(_to_expr(other)._rbexpr))
115
115
  end
116
116
 
117
117
  # Less than.
118
118
  #
119
119
  # @return [Expr]
120
120
  def <(other)
121
- _from_rbexpr(_rbexpr.lt(_to_expr(other)._rbexpr))
121
+ wrap_expr(_rbexpr.lt(_to_expr(other)._rbexpr))
122
122
  end
123
123
 
124
124
  # Greater than.
125
125
  #
126
126
  # @return [Expr]
127
127
  def >(other)
128
- _from_rbexpr(_rbexpr.gt(_to_expr(other)._rbexpr))
128
+ wrap_expr(_rbexpr.gt(_to_expr(other)._rbexpr))
129
129
  end
130
130
 
131
131
  # Performs boolean not.
@@ -134,31 +134,59 @@ module Polars
134
134
  def !
135
135
  is_not
136
136
  end
137
+ alias_method :~, :!
137
138
 
138
139
  # Performs negation.
139
140
  #
140
141
  # @return [Expr]
141
142
  def -@
142
- _from_rbexpr(_rbexpr.neg)
143
+ wrap_expr(_rbexpr.neg)
143
144
  end
144
145
 
145
- # Cast to physical representation of the logical dtype.
146
+ # Read a serialized expression from a file.
147
+ #
148
+ # @param source [Object]
149
+ # Path to a file or a file-like object (by file-like object, we refer to
150
+ # objects that have a `read` method, such as a file handler or `StringIO`).
151
+ #
152
+ # @return [Expr]
153
+ #
154
+ # @note
155
+ # This function uses marshaling if the logical plan contains Ruby UDFs,
156
+ # and as such inherits the security implications. Deserializing can execute
157
+ # arbitrary code, so it should only be attempted on trusted data.
158
+ #
159
+ # @note
160
+ # Serialization is not stable across Polars versions: a LazyFrame serialized
161
+ # in one Polars version may not be deserializable in another Polars version.
146
162
  #
147
- # - `:date` -> `:i32`
148
- # - `:datetime` -> `:i64`
149
- # - `:time` -> `:i64`
150
- # - `:duration` -> `:i64`
151
- # - `:cat` -> `:u32`
152
- # - Other data types will be left unchanged.
163
+ # @example
164
+ # expr = Polars.col("foo").sum.over("bar")
165
+ # bytes = expr.meta.serialize
166
+ # Polars::Expr.deserialize(StringIO.new(bytes))
167
+ # # => col("foo").sum().over([col("bar")])
168
+ def self.deserialize(source)
169
+ raise Todo unless RbExpr.respond_to?(:deserialize_binary)
170
+
171
+ if Utils.pathlike?(source)
172
+ source = Utils.normalize_filepath(source)
173
+ end
174
+
175
+ deserializer = RbExpr.method(:deserialize_binary)
176
+
177
+ _from_rbexpr(deserializer.(source))
178
+ end
179
+
180
+ # Cast to physical representation of the logical dtype.
153
181
  #
154
182
  # @return [Expr]
155
183
  #
156
184
  # @example
157
185
  # Polars::DataFrame.new({"vals" => ["a", "x", nil, "a"]}).with_columns(
158
186
  # [
159
- # Polars.col("vals").cast(:cat),
187
+ # Polars.col("vals").cast(Polars::Categorical),
160
188
  # Polars.col("vals")
161
- # .cast(:cat)
189
+ # .cast(Polars::Categorical)
162
190
  # .to_physical
163
191
  # .alias("vals_physical")
164
192
  # ]
@@ -176,7 +204,7 @@ module Polars
176
204
  # # │ a ┆ 0 │
177
205
  # # └──────┴───────────────┘
178
206
  def to_physical
179
- _from_rbexpr(_rbexpr.to_physical)
207
+ wrap_expr(_rbexpr.to_physical)
180
208
  end
181
209
 
182
210
  # Check if any boolean value in a Boolean column is `true`.
@@ -195,8 +223,8 @@ module Polars
195
223
  # # ╞══════╪═══════╡
196
224
  # # │ true ┆ false │
197
225
  # # └──────┴───────┘
198
- def any(drop_nulls: true)
199
- _from_rbexpr(_rbexpr.any(drop_nulls))
226
+ def any(ignore_nulls: true)
227
+ wrap_expr(_rbexpr.any(ignore_nulls))
200
228
  end
201
229
 
202
230
  # Check if all boolean values in a Boolean column are `true`.
@@ -220,8 +248,66 @@ module Polars
220
248
  # # ╞══════╪═══════╪═══════╡
221
249
  # # │ true ┆ false ┆ false │
222
250
  # # └──────┴───────┴───────┘
223
- def all(drop_nulls: true)
224
- _from_rbexpr(_rbexpr.all(drop_nulls))
251
+ def all(ignore_nulls: true)
252
+ wrap_expr(_rbexpr.all(ignore_nulls))
253
+ end
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
+
287
+ # Return indices where expression evaluates `true`.
288
+ #
289
+ # @return [Expr]
290
+ #
291
+ # @note
292
+ # Modifies number of rows returned, so will fail in combination with other
293
+ # expressions. Use as only expression in `select` / `with_columns`.
294
+ #
295
+ # @example
296
+ # df = Polars::DataFrame.new({"a" => [1, 1, 2, 1]})
297
+ # df.select((Polars.col("a") == 1).arg_true)
298
+ # # =>
299
+ # # shape: (3, 1)
300
+ # # ┌─────┐
301
+ # # │ a │
302
+ # # │ --- │
303
+ # # │ u32 │
304
+ # # ╞═════╡
305
+ # # │ 0 │
306
+ # # │ 1 │
307
+ # # │ 3 │
308
+ # # └─────┘
309
+ def arg_true
310
+ wrap_expr(Plr.arg_where(_rbexpr))
225
311
  end
226
312
 
227
313
  # Compute the square root of the elements.
@@ -243,7 +329,29 @@ module Polars
243
329
  # # │ 2.0 │
244
330
  # # └──────────┘
245
331
  def sqrt
246
- self**0.5
332
+ wrap_expr(_rbexpr.sqrt)
333
+ end
334
+
335
+ # Compute the cube root of the elements.
336
+ #
337
+ # @return [Expr]
338
+ #
339
+ # @example
340
+ # df = Polars::DataFrame.new({"values" => [1.0, 2.0, 4.0]})
341
+ # df.select(Polars.col("values").cbrt)
342
+ # # =>
343
+ # # shape: (3, 1)
344
+ # # ┌──────────┐
345
+ # # │ values │
346
+ # # │ --- │
347
+ # # │ f64 │
348
+ # # ╞══════════╡
349
+ # # │ 1.0 │
350
+ # # │ 1.259921 │
351
+ # # │ 1.587401 │
352
+ # # └──────────┘
353
+ def cbrt
354
+ wrap_expr(_rbexpr.cbrt)
247
355
  end
248
356
 
249
357
  # Compute the base 10 logarithm of the input array, element-wise.
@@ -287,7 +395,7 @@ module Polars
287
395
  # # │ 54.59815 │
288
396
  # # └──────────┘
289
397
  def exp
290
- _from_rbexpr(_rbexpr.exp)
398
+ wrap_expr(_rbexpr.exp)
291
399
  end
292
400
 
293
401
  # Rename the output of an expression.
@@ -322,23 +430,20 @@ module Polars
322
430
  # # │ 3 ┆ null │
323
431
  # # └─────┴──────┘
324
432
  def alias(name)
325
- _from_rbexpr(_rbexpr._alias(name))
433
+ wrap_expr(_rbexpr.alias(name))
326
434
  end
327
435
 
328
- # TODO support symbols for exclude
329
-
330
436
  # Exclude certain columns from a wildcard/regex selection.
331
437
  #
332
438
  # You may also use regexes in the exclude list. They must start with `^` and end
333
439
  # with `$`.
334
440
  #
335
441
  # @param columns [Object]
336
- # Column(s) to exclude from selection.
337
- # This can be:
338
- #
339
- # - a column name, or multiple column names
340
- # - a regular expression starting with `^` and ending with `$`
341
- # - a dtype or multiple dtypes
442
+ # The name or datatype of the column(s) to exclude. Accepts regular expression
443
+ # input. Regular expressions should start with `^` and end with `$`.
444
+ # @param more_columns [Array]
445
+ # Additional names or datatypes of columns to exclude, specified as positional
446
+ # arguments.
342
447
  #
343
448
  # @return [Expr]
344
449
  #
@@ -362,132 +467,56 @@ module Polars
362
467
  # # │ 2 ┆ 2.5 │
363
468
  # # │ 3 ┆ 1.5 │
364
469
  # # └─────┴──────┘
365
- def exclude(columns)
366
- if columns.is_a?(::String)
367
- columns = [columns]
368
- return _from_rbexpr(_rbexpr.exclude(columns))
369
- elsif !columns.is_a?(::Array)
370
- columns = [columns]
371
- return _from_rbexpr(_rbexpr.exclude_dtype(columns))
372
- end
373
-
374
- if !columns.all? { |a| a.is_a?(::String) } || !columns.all? { |a| Utils.is_polars_dtype(a) }
375
- raise ArgumentError, "input should be all string or all DataType"
376
- end
377
-
378
- if columns[0].is_a?(::String)
379
- _from_rbexpr(_rbexpr.exclude(columns))
380
- else
381
- _from_rbexpr(_rbexpr.exclude_dtype(columns))
382
- end
383
- end
384
-
385
- # Keep the original root name of the expression.
386
- #
387
- # @return [Expr]
388
- #
389
- # @example
390
- # df = Polars::DataFrame.new(
391
- # {
392
- # "a" => [1, 2],
393
- # "b" => [3, 4]
394
- # }
395
- # )
396
- # df.with_columns([(Polars.col("a") * 9).alias("c").keep_name])
397
- # # =>
398
- # # shape: (2, 2)
399
- # # ┌─────┬─────┐
400
- # # │ a ┆ b │
401
- # # │ --- ┆ --- │
402
- # # │ i64 ┆ i64 │
403
- # # ╞═════╪═════╡
404
- # # │ 9 ┆ 3 │
405
- # # │ 18 ┆ 4 │
406
- # # └─────┴─────┘
407
- def keep_name
408
- name.keep
470
+ def exclude(columns, *more_columns)
471
+ meta.as_selector.exclude(columns, *more_columns).as_expr
409
472
  end
410
473
 
411
- # Add a prefix to the root column name of the expression.
412
- #
413
- # @return [Expr]
474
+ # Offers a structured way to apply a sequence of user-defined functions (UDFs).
414
475
  #
415
- # @example
416
- # df = Polars::DataFrame.new(
417
- # {
418
- # "a" => [1, 2, 3],
419
- # "b" => ["x", "y", "z"]
420
- # }
421
- # )
422
- # df.with_columns(Polars.all.reverse.name.prefix("reverse_"))
423
- # # =>
424
- # # shape: (3, 4)
425
- # # ┌─────┬─────┬───────────┬───────────┐
426
- # # │ a ┆ b ┆ reverse_a ┆ reverse_b │
427
- # # │ --- ┆ --- ┆ --- ┆ --- │
428
- # # │ i64 ┆ str ┆ i64 ┆ str │
429
- # # ╞═════╪═════╪═══════════╪═══════════╡
430
- # # │ 1 ┆ x ┆ 3 ┆ z │
431
- # # │ 2 ┆ y ┆ 2 ┆ y │
432
- # # │ 3 ┆ z ┆ 1 ┆ x │
433
- # # └─────┴─────┴───────────┴───────────┘
434
- def prefix(prefix)
435
- name.prefix(prefix)
436
- end
437
-
438
- # Add a suffix to the root column name of the expression.
476
+ # @param function [Object]
477
+ # Callable; will receive the expression as the first parameter,
478
+ # followed by any given args/kwargs.
479
+ # @param args [Array]
480
+ # Arguments to pass to the UDF.
481
+ # @param kwargs [Hash]
482
+ # Keyword arguments to pass to the UDF.
439
483
  #
440
- # @return [Expr]
484
+ # @return [Object]
441
485
  #
442
486
  # @example
443
- # df = Polars::DataFrame.new(
444
- # {
445
- # "a" => [1, 2, 3],
446
- # "b" => ["x", "y", "z"]
447
- # }
448
- # )
449
- # df.with_columns(Polars.all.reverse.name.suffix("_reverse"))
450
- # # =>
451
- # # shape: (3, 4)
452
- # # ┌─────┬─────┬───────────┬───────────┐
453
- # # │ a ┆ b ┆ a_reverse ┆ b_reverse │
454
- # # │ --- ┆ --- ┆ --- ┆ --- │
455
- # # │ i64 ┆ str ┆ i64 ┆ str │
456
- # # ╞═════╪═════╪═══════════╪═══════════╡
457
- # # │ 1 ┆ x ┆ 3 ┆ z │
458
- # # │ 2 ┆ y ┆ 2 ┆ y │
459
- # # │ 3 ┆ z ┆ 1 ┆ x │
460
- # # └─────┴─────┴───────────┴───────────┘
461
- def suffix(suffix)
462
- name.suffix(suffix)
463
- end
464
-
465
- # Rename the output of an expression by mapping a function over the root name.
487
+ # extract_number = lambda do |expr|
488
+ # # Extract the digits from a string.
489
+ # expr.str.extract('\d+', group_index: 0).cast(Polars::Int64)
490
+ # end
466
491
  #
467
- # @return [Expr]
492
+ # scale_negative_even = lambda do |expr, n: 1|
493
+ # # Set even numbers negative, and scale by a user-supplied value.
494
+ # expr = Polars.when(expr % 2 == 0).then(-expr).otherwise(expr)
495
+ # expr * n
496
+ # end
468
497
  #
469
- # @example
470
- # df = Polars::DataFrame.new(
471
- # {
472
- # "A" => [1, 2],
473
- # "B" => [3, 4]
474
- # }
475
- # )
476
- # df.select(
477
- # Polars.all.reverse.map_alias { |colName| colName + "_reverse" }
498
+ # df = Polars::DataFrame.new({"val" => ["a: 1", "b: 2", "c: 3", "d: 4"]})
499
+ # df.with_columns(
500
+ # udfs: Polars.col("val").pipe(extract_number).pipe(scale_negative_even, n: 5)
478
501
  # )
479
502
  # # =>
480
- # # shape: (2, 2)
481
- # # ┌───────────┬───────────┐
482
- # # │ A_reverse B_reverse
483
- # # │ --- ┆ ---
484
- # # │ i64 ┆ i64
485
- # # ╞═══════════╪═══════════╡
486
- # # │ 2 4
487
- # # │ 1 3
488
- # # └───────────┴───────────┘
489
- def map_alias(&f)
490
- name.map(&f)
503
+ # # shape: (4, 2)
504
+ # # ┌──────┬──────┐
505
+ # # │ val udfs
506
+ # # │ --- ┆ ---
507
+ # # │ str ┆ i64
508
+ # # ╞══════╪══════╡
509
+ # # │ a: 1 5
510
+ # # │ b: 2 -10
511
+ # # │ c: 3 ┆ 15 │
512
+ # # │ d: 4 ┆ -20 │
513
+ # # └──────┴──────┘
514
+ def pipe(
515
+ function,
516
+ *args,
517
+ **kwargs
518
+ )
519
+ function.(self, *args, **kwargs)
491
520
  end
492
521
 
493
522
  # Negate a boolean expression.
@@ -527,7 +556,7 @@ module Polars
527
556
  # # │ true │
528
557
  # # └───────┘
529
558
  def is_not
530
- _from_rbexpr(_rbexpr.not_)
559
+ wrap_expr(_rbexpr.not_)
531
560
  end
532
561
  alias_method :not_, :is_not
533
562
 
@@ -542,7 +571,7 @@ module Polars
542
571
  # "b" => [1.0, 2.0, Float::NAN, 1.0, 5.0]
543
572
  # }
544
573
  # )
545
- # df.with_column(Polars.all.is_null.suffix("_isnull"))
574
+ # df.with_columns(Polars.all.is_null.name.suffix("_isnull"))
546
575
  # # =>
547
576
  # # shape: (5, 4)
548
577
  # # ┌──────┬─────┬──────────┬──────────┐
@@ -557,7 +586,7 @@ module Polars
557
586
  # # │ 5 ┆ 5.0 ┆ false ┆ false │
558
587
  # # └──────┴─────┴──────────┴──────────┘
559
588
  def is_null
560
- _from_rbexpr(_rbexpr.is_null)
589
+ wrap_expr(_rbexpr.is_null)
561
590
  end
562
591
 
563
592
  # Returns a boolean Series indicating which values are not null.
@@ -571,7 +600,7 @@ module Polars
571
600
  # "b" => [1.0, 2.0, Float::NAN, 1.0, 5.0]
572
601
  # }
573
602
  # )
574
- # df.with_column(Polars.all.is_not_null.suffix("_not_null"))
603
+ # df.with_columns(Polars.all.is_not_null.name.suffix("_not_null"))
575
604
  # # =>
576
605
  # # shape: (5, 4)
577
606
  # # ┌──────┬─────┬────────────┬────────────┐
@@ -586,7 +615,7 @@ module Polars
586
615
  # # │ 5 ┆ 5.0 ┆ true ┆ true │
587
616
  # # └──────┴─────┴────────────┴────────────┘
588
617
  def is_not_null
589
- _from_rbexpr(_rbexpr.is_not_null)
618
+ wrap_expr(_rbexpr.is_not_null)
590
619
  end
591
620
 
592
621
  # Returns a boolean Series indicating which values are finite.
@@ -612,7 +641,7 @@ module Polars
612
641
  # # │ true ┆ false │
613
642
  # # └──────┴───────┘
614
643
  def is_finite
615
- _from_rbexpr(_rbexpr.is_finite)
644
+ wrap_expr(_rbexpr.is_finite)
616
645
  end
617
646
 
618
647
  # Returns a boolean Series indicating which values are infinite.
@@ -638,7 +667,7 @@ module Polars
638
667
  # # │ false ┆ true │
639
668
  # # └───────┴───────┘
640
669
  def is_infinite
641
- _from_rbexpr(_rbexpr.is_infinite)
670
+ wrap_expr(_rbexpr.is_infinite)
642
671
  end
643
672
 
644
673
  # Returns a boolean Series indicating which values are NaN.
@@ -656,7 +685,7 @@ module Polars
656
685
  # "b" => [1.0, 2.0, Float::NAN, 1.0, 5.0]
657
686
  # }
658
687
  # )
659
- # df.with_column(Polars.col(Polars::Float64).is_nan.suffix("_isnan"))
688
+ # df.with_columns(Polars.col(Polars::Float64).is_nan.name.suffix("_isnan"))
660
689
  # # =>
661
690
  # # shape: (5, 3)
662
691
  # # ┌──────┬─────┬─────────┐
@@ -671,7 +700,7 @@ module Polars
671
700
  # # │ 5 ┆ 5.0 ┆ false │
672
701
  # # └──────┴─────┴─────────┘
673
702
  def is_nan
674
- _from_rbexpr(_rbexpr.is_nan)
703
+ wrap_expr(_rbexpr.is_nan)
675
704
  end
676
705
 
677
706
  # Returns a boolean Series indicating which values are not NaN.
@@ -689,7 +718,7 @@ module Polars
689
718
  # "b" => [1.0, 2.0, Float::NAN, 1.0, 5.0]
690
719
  # }
691
720
  # )
692
- # df.with_column(Polars.col(Polars::Float64).is_not_nan.suffix("_is_not_nan"))
721
+ # df.with_columns(Polars.col(Polars::Float64).is_not_nan.name.suffix("_is_not_nan"))
693
722
  # # =>
694
723
  # # shape: (5, 3)
695
724
  # # ┌──────┬─────┬──────────────┐
@@ -704,7 +733,7 @@ module Polars
704
733
  # # │ 5 ┆ 5.0 ┆ true │
705
734
  # # └──────┴─────┴──────────────┘
706
735
  def is_not_nan
707
- _from_rbexpr(_rbexpr.is_not_nan)
736
+ wrap_expr(_rbexpr.is_not_nan)
708
737
  end
709
738
 
710
739
  # Get the group indexes of the group by operation.
@@ -739,7 +768,7 @@ module Polars
739
768
  # # │ two ┆ [3, 4, 5] │
740
769
  # # └───────┴───────────┘
741
770
  def agg_groups
742
- _from_rbexpr(_rbexpr.agg_groups)
771
+ wrap_expr(_rbexpr.agg_groups)
743
772
  end
744
773
 
745
774
  # Count the number of values in this expression.
@@ -759,7 +788,7 @@ module Polars
759
788
  # # │ 3 ┆ 2 │
760
789
  # # └─────┴─────┘
761
790
  def count
762
- _from_rbexpr(_rbexpr.count)
791
+ wrap_expr(_rbexpr.count)
763
792
  end
764
793
 
765
794
  # Count the number of values in this expression.
@@ -779,7 +808,7 @@ module Polars
779
808
  # # │ 3 ┆ 3 │
780
809
  # # └─────┴─────┘
781
810
  def len
782
- _from_rbexpr(_rbexpr.len)
811
+ wrap_expr(_rbexpr.len)
783
812
  end
784
813
  alias_method :length, :len
785
814
 
@@ -818,7 +847,7 @@ module Polars
818
847
  if !length.is_a?(Expr)
819
848
  length = Polars.lit(length)
820
849
  end
821
- _from_rbexpr(_rbexpr.slice(offset._rbexpr, length._rbexpr))
850
+ wrap_expr(_rbexpr.slice(offset._rbexpr, length._rbexpr))
822
851
  end
823
852
 
824
853
  # Append expressions.
@@ -852,7 +881,7 @@ module Polars
852
881
  # # └─────┴──────┘
853
882
  def append(other, upcast: true)
854
883
  other = Utils.parse_into_expression(other)
855
- _from_rbexpr(_rbexpr.append(other, upcast))
884
+ wrap_expr(_rbexpr.append(other, upcast))
856
885
  end
857
886
 
858
887
  # Create a single chunk of memory for this Series.
@@ -877,7 +906,7 @@ module Polars
877
906
  # # │ 2 │
878
907
  # # └────────┘
879
908
  def rechunk
880
- _from_rbexpr(_rbexpr.rechunk)
909
+ wrap_expr(_rbexpr.rechunk)
881
910
  end
882
911
 
883
912
  # Drop null values.
@@ -904,7 +933,7 @@ module Polars
904
933
  # # │ NaN │
905
934
  # # └─────┘
906
935
  def drop_nulls
907
- _from_rbexpr(_rbexpr.drop_nulls)
936
+ wrap_expr(_rbexpr.drop_nulls)
908
937
  end
909
938
 
910
939
  # Drop floating point NaN values.
@@ -931,7 +960,7 @@ module Polars
931
960
  # # │ 4.0 │
932
961
  # # └──────┘
933
962
  def drop_nans
934
- _from_rbexpr(_rbexpr.drop_nans)
963
+ wrap_expr(_rbexpr.drop_nans)
935
964
  end
936
965
 
937
966
  # Get an array with the cumulative sum computed at every element.
@@ -942,8 +971,8 @@ module Polars
942
971
  # @return [Expr]
943
972
  #
944
973
  # @note
945
- # Dtypes in `:i8`, `:u8`, `:i16`, and `:u16` are cast to
946
- # `:i64` before summing to prevent overflow issues.
974
+ # Dtypes in \\\\{Int8, UInt8, Int16, UInt16} are cast to
975
+ # Int64 before summing to prevent overflow issues.
947
976
  #
948
977
  # @example
949
978
  # df = Polars::DataFrame.new({"a" => [1, 2, 3, 4]})
@@ -966,9 +995,8 @@ module Polars
966
995
  # # │ 10 ┆ 4 │
967
996
  # # └─────┴───────────┘
968
997
  def cum_sum(reverse: false)
969
- _from_rbexpr(_rbexpr.cum_sum(reverse))
998
+ wrap_expr(_rbexpr.cum_sum(reverse))
970
999
  end
971
- alias_method :cumsum, :cum_sum
972
1000
 
973
1001
  # Get an array with the cumulative product computed at every element.
974
1002
  #
@@ -978,8 +1006,8 @@ module Polars
978
1006
  # @return [Expr]
979
1007
  #
980
1008
  # @note
981
- # Dtypes in `:i8`, `:u8`, `:i16`, and `:u16` are cast to
982
- # `:i64` before summing to prevent overflow issues.
1009
+ # Dtypes in \\\\{Int8, UInt8, Int16, UInt16} are cast to
1010
+ # Int64 before summing to prevent overflow issues.
983
1011
  #
984
1012
  # @example
985
1013
  # df = Polars::DataFrame.new({"a" => [1, 2, 3, 4]})
@@ -1002,9 +1030,8 @@ module Polars
1002
1030
  # # │ 24 ┆ 4 │
1003
1031
  # # └─────┴───────────┘
1004
1032
  def cum_prod(reverse: false)
1005
- _from_rbexpr(_rbexpr.cum_prod(reverse))
1033
+ wrap_expr(_rbexpr.cum_prod(reverse))
1006
1034
  end
1007
- alias_method :cumprod, :cum_prod
1008
1035
 
1009
1036
  # Get an array with the cumulative min computed at every element.
1010
1037
  #
@@ -1034,9 +1061,8 @@ module Polars
1034
1061
  # # │ 1 ┆ 4 │
1035
1062
  # # └─────┴───────────┘
1036
1063
  def cum_min(reverse: false)
1037
- _from_rbexpr(_rbexpr.cum_min(reverse))
1064
+ wrap_expr(_rbexpr.cum_min(reverse))
1038
1065
  end
1039
- alias_method :cummin, :cum_min
1040
1066
 
1041
1067
  # Get an array with the cumulative max computed at every element.
1042
1068
  #
@@ -1066,9 +1092,8 @@ module Polars
1066
1092
  # # │ 4 ┆ 4 │
1067
1093
  # # └─────┴───────────┘
1068
1094
  def cum_max(reverse: false)
1069
- _from_rbexpr(_rbexpr.cum_max(reverse))
1095
+ wrap_expr(_rbexpr.cum_max(reverse))
1070
1096
  end
1071
- alias_method :cummax, :cum_max
1072
1097
 
1073
1098
  # Get an array with the cumulative count computed at every element.
1074
1099
  #
@@ -1100,9 +1125,8 @@ module Polars
1100
1125
  # # │ d ┆ 3 ┆ 1 │
1101
1126
  # # └──────┴───────────┴───────────────────┘
1102
1127
  def cum_count(reverse: false)
1103
- _from_rbexpr(_rbexpr.cum_count(reverse))
1128
+ wrap_expr(_rbexpr.cum_count(reverse))
1104
1129
  end
1105
- alias_method :cumcount, :cum_count
1106
1130
 
1107
1131
  # Rounds down to the nearest integer value.
1108
1132
  #
@@ -1126,7 +1150,7 @@ module Polars
1126
1150
  # # │ 1.0 │
1127
1151
  # # └─────┘
1128
1152
  def floor
1129
- _from_rbexpr(_rbexpr.floor)
1153
+ wrap_expr(_rbexpr.floor)
1130
1154
  end
1131
1155
 
1132
1156
  # Rounds up to the nearest integer value.
@@ -1151,13 +1175,20 @@ module Polars
1151
1175
  # # │ 2.0 │
1152
1176
  # # └─────┘
1153
1177
  def ceil
1154
- _from_rbexpr(_rbexpr.ceil)
1178
+ wrap_expr(_rbexpr.ceil)
1155
1179
  end
1156
1180
 
1157
1181
  # Round underlying floating point data by `decimals` digits.
1158
1182
  #
1159
1183
  # @param decimals [Integer]
1160
1184
  # Number of decimals to round by.
1185
+ # @param mode ['half_to_even', 'half_away_from_zero']
1186
+ # RoundMode.
1187
+ #
1188
+ # * *half_to_even*
1189
+ # round to the nearest even number
1190
+ # * *half_away_from_zero*
1191
+ # round to the nearest number away from zero
1161
1192
  #
1162
1193
  # @return [Expr]
1163
1194
  #
@@ -1176,8 +1207,76 @@ module Polars
1176
1207
  # # │ 1.0 │
1177
1208
  # # │ 1.2 │
1178
1209
  # # └─────┘
1179
- def round(decimals = 0)
1180
- _from_rbexpr(_rbexpr.round(decimals))
1210
+ def round(decimals = 0, mode: "half_to_even")
1211
+ wrap_expr(_rbexpr.round(decimals, mode))
1212
+ end
1213
+
1214
+ # Round to a number of significant figures.
1215
+ #
1216
+ # @param digits [Integer]
1217
+ # Number of significant figures to round to.
1218
+ #
1219
+ # @return [Expr]
1220
+ #
1221
+ # @example
1222
+ # df = Polars::DataFrame.new({"a" => [0.01234, 3.333, 1234.0]})
1223
+ # df.with_columns(Polars.col("a").round_sig_figs(2).alias("round_sig_figs"))
1224
+ # # =>
1225
+ # # shape: (3, 2)
1226
+ # # ┌─────────┬────────────────┐
1227
+ # # │ a ┆ round_sig_figs │
1228
+ # # │ --- ┆ --- │
1229
+ # # │ f64 ┆ f64 │
1230
+ # # ╞═════════╪════════════════╡
1231
+ # # │ 0.01234 ┆ 0.012 │
1232
+ # # │ 3.333 ┆ 3.3 │
1233
+ # # │ 1234.0 ┆ 1200.0 │
1234
+ # # └─────────┴────────────────┘
1235
+ def round_sig_figs(digits)
1236
+ wrap_expr(_rbexpr.round_sig_figs(digits))
1237
+ end
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))
1181
1280
  end
1182
1281
 
1183
1282
  # Compute the dot/inner product between two Expressions.
@@ -1206,13 +1305,16 @@ module Polars
1206
1305
  # # └─────┘
1207
1306
  def dot(other)
1208
1307
  other = Utils.parse_into_expression(other, str_as_lit: false)
1209
- _from_rbexpr(_rbexpr.dot(other))
1308
+ wrap_expr(_rbexpr.dot(other))
1210
1309
  end
1211
1310
 
1212
1311
  # Compute the most occurring value(s).
1213
1312
  #
1214
1313
  # Can return multiple Values.
1215
1314
  #
1315
+ # @param maintain_order [Boolean]
1316
+ # Maintain order of data. This requires more work.
1317
+ #
1216
1318
  # @return [Expr]
1217
1319
  #
1218
1320
  # @example
@@ -1233,17 +1335,20 @@ module Polars
1233
1335
  # # │ 1 ┆ 1 │
1234
1336
  # # │ 1 ┆ 2 │
1235
1337
  # # └─────┴─────┘
1236
- def mode
1237
- _from_rbexpr(_rbexpr.mode)
1338
+ def mode(maintain_order: false)
1339
+ wrap_expr(_rbexpr.mode(maintain_order))
1238
1340
  end
1239
1341
 
1240
1342
  # Cast between data types.
1241
1343
  #
1242
- # @param dtype [Symbol]
1344
+ # @param dtype [Object]
1243
1345
  # DataType to cast to.
1244
1346
  # @param strict [Boolean]
1245
1347
  # Throw an error if a cast could not be done.
1246
1348
  # For instance, due to an overflow.
1349
+ # @param wrap_numerical [Boolean]
1350
+ # If true numeric casts wrap overflowing values instead of
1351
+ # marking the cast as invalid.
1247
1352
  #
1248
1353
  # @return [Expr]
1249
1354
  #
@@ -1256,8 +1361,8 @@ module Polars
1256
1361
  # )
1257
1362
  # df.with_columns(
1258
1363
  # [
1259
- # Polars.col("a").cast(:f64),
1260
- # Polars.col("b").cast(:i32)
1364
+ # Polars.col("a").cast(Polars::Float64),
1365
+ # Polars.col("b").cast(Polars::Int32)
1261
1366
  # ]
1262
1367
  # )
1263
1368
  # # =>
@@ -1271,16 +1376,16 @@ module Polars
1271
1376
  # # │ 2.0 ┆ 5 │
1272
1377
  # # │ 3.0 ┆ 6 │
1273
1378
  # # └─────┴─────┘
1274
- def cast(dtype, strict: true)
1275
- dtype = Utils.rb_type_to_dtype(dtype)
1276
- _from_rbexpr(_rbexpr.cast(dtype, strict))
1379
+ def cast(dtype, strict: true, wrap_numerical: false)
1380
+ dtype = Utils.parse_into_datatype_expr(dtype)
1381
+ wrap_expr(_rbexpr.cast(dtype._rbdatatype_expr, strict, wrap_numerical))
1277
1382
  end
1278
1383
 
1279
1384
  # Sort this column. In projection/ selection context the whole column is sorted.
1280
1385
  #
1281
1386
  # If used in a group by context, the groups are sorted.
1282
1387
  #
1283
- # @param reverse [Boolean]
1388
+ # @param descending [Boolean]
1284
1389
  # false -> order from small to large.
1285
1390
  # true -> order from large to small.
1286
1391
  # @param nulls_last [Boolean]
@@ -1347,8 +1452,8 @@ module Polars
1347
1452
  # # │ two ┆ [3, 4, 99] │
1348
1453
  # # │ one ┆ [1, 2, 98] │
1349
1454
  # # └───────┴────────────┘
1350
- def sort(reverse: false, nulls_last: false)
1351
- _from_rbexpr(_rbexpr.sort_with(reverse, nulls_last))
1455
+ def sort(descending: false, nulls_last: false)
1456
+ wrap_expr(_rbexpr.sort_with(descending, nulls_last))
1352
1457
  end
1353
1458
 
1354
1459
  # Return the `k` largest elements.
@@ -1387,7 +1492,113 @@ module Polars
1387
1492
  # # └───────┴──────────┘
1388
1493
  def top_k(k: 5)
1389
1494
  k = Utils.parse_into_expression(k)
1390
- _from_rbexpr(_rbexpr.top_k(k))
1495
+ wrap_expr(_rbexpr.top_k(k))
1496
+ end
1497
+
1498
+ # Return the elements corresponding to the `k` largest elements of the `by` column(s).
1499
+ #
1500
+ # Non-null elements are always preferred over null elements, regardless of
1501
+ # the value of `reverse`. The output is not guaranteed to be in any
1502
+ # particular order, call :func:`sort` after this function if you wish the
1503
+ # output to be sorted.
1504
+ #
1505
+ # @param by [Object]
1506
+ # Column(s) used to determine the largest elements.
1507
+ # Accepts expression input. Strings are parsed as column names.
1508
+ # @param k [Integer]
1509
+ # Number of elements to return.
1510
+ # @param reverse [Object]
1511
+ # Consider the `k` smallest elements of the `by` column(s) (instead of the `k`
1512
+ # largest). This can be specified per column by passing an array of
1513
+ # booleans.
1514
+ #
1515
+ # @return [Expr]
1516
+ #
1517
+ # @example
1518
+ # df = Polars::DataFrame.new(
1519
+ # {
1520
+ # "a" => [1, 2, 3, 4, 5, 6],
1521
+ # "b" => [6, 5, 4, 3, 2, 1],
1522
+ # "c" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
1523
+ # }
1524
+ # )
1525
+ # # =>
1526
+ # # shape: (6, 3)
1527
+ # # ┌─────┬─────┬────────┐
1528
+ # # │ a ┆ b ┆ c │
1529
+ # # │ --- ┆ --- ┆ --- │
1530
+ # # │ i64 ┆ i64 ┆ str │
1531
+ # # ╞═════╪═════╪════════╡
1532
+ # # │ 1 ┆ 6 ┆ Apple │
1533
+ # # │ 2 ┆ 5 ┆ Orange │
1534
+ # # │ 3 ┆ 4 ┆ Apple │
1535
+ # # │ 4 ┆ 3 ┆ Apple │
1536
+ # # │ 5 ┆ 2 ┆ Banana │
1537
+ # # │ 6 ┆ 1 ┆ Banana │
1538
+ # # └─────┴─────┴────────┘
1539
+ #
1540
+ # @example Get the top 2 rows by column `a` or `b`.
1541
+ # df.select(
1542
+ # Polars.all.top_k_by("a", k: 2).name.suffix("_top_by_a"),
1543
+ # Polars.all.top_k_by("b", k: 2).name.suffix("_top_by_b")
1544
+ # )
1545
+ # # =>
1546
+ # # shape: (2, 6)
1547
+ # # ┌────────────┬────────────┬────────────┬────────────┬────────────┬────────────┐
1548
+ # # │ a_top_by_a ┆ b_top_by_a ┆ c_top_by_a ┆ a_top_by_b ┆ b_top_by_b ┆ c_top_by_b │
1549
+ # # │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
1550
+ # # │ i64 ┆ i64 ┆ str ┆ i64 ┆ i64 ┆ str │
1551
+ # # ╞════════════╪════════════╪════════════╪════════════╪════════════╪════════════╡
1552
+ # # │ 6 ┆ 1 ┆ Banana ┆ 1 ┆ 6 ┆ Apple │
1553
+ # # │ 5 ┆ 2 ┆ Banana ┆ 2 ┆ 5 ┆ Orange │
1554
+ # # └────────────┴────────────┴────────────┴────────────┴────────────┴────────────┘
1555
+ #
1556
+ # @example Get the top 2 rows by multiple columns with given order.
1557
+ # df.select(
1558
+ # Polars.all
1559
+ # .top_k_by(["c", "a"], k: 2, reverse: [false, true])
1560
+ # .name.suffix("_by_ca"),
1561
+ # Polars.all
1562
+ # .top_k_by(["c", "b"], k: 2, reverse: [false, true])
1563
+ # .name.suffix("_by_cb")
1564
+ # )
1565
+ # # =>
1566
+ # # shape: (2, 6)
1567
+ # # ┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
1568
+ # # │ a_by_ca ┆ b_by_ca ┆ c_by_ca ┆ a_by_cb ┆ b_by_cb ┆ c_by_cb │
1569
+ # # │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
1570
+ # # │ i64 ┆ i64 ┆ str ┆ i64 ┆ i64 ┆ str │
1571
+ # # ╞═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
1572
+ # # │ 2 ┆ 5 ┆ Orange ┆ 2 ┆ 5 ┆ Orange │
1573
+ # # │ 5 ┆ 2 ┆ Banana ┆ 6 ┆ 1 ┆ Banana │
1574
+ # # └─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
1575
+ #
1576
+ # @example Get the top 2 rows by column `a` in each group.
1577
+ # df.group_by("c", maintain_order: true)
1578
+ # .agg(Polars.all.top_k_by("a", k: 2))
1579
+ # .explode(Polars.all.exclude("c"))
1580
+ # # =>
1581
+ # # shape: (5, 3)
1582
+ # # ┌────────┬─────┬─────┐
1583
+ # # │ c ┆ a ┆ b │
1584
+ # # │ --- ┆ --- ┆ --- │
1585
+ # # │ str ┆ i64 ┆ i64 │
1586
+ # # ╞════════╪═════╪═════╡
1587
+ # # │ Apple ┆ 4 ┆ 3 │
1588
+ # # │ Apple ┆ 3 ┆ 4 │
1589
+ # # │ Orange ┆ 2 ┆ 5 │
1590
+ # # │ Banana ┆ 6 ┆ 1 │
1591
+ # # │ Banana ┆ 5 ┆ 2 │
1592
+ # # └────────┴─────┴─────┘
1593
+ def top_k_by(
1594
+ by,
1595
+ k: 5,
1596
+ reverse: false
1597
+ )
1598
+ k = Utils.parse_into_expression(k)
1599
+ by = Utils.parse_into_list_of_expressions(by)
1600
+ reverse = Utils.extend_bool(reverse, by.length, "reverse", "by")
1601
+ wrap_expr(_rbexpr.top_k_by(by, k, reverse))
1391
1602
  end
1392
1603
 
1393
1604
  # Return the `k` smallest elements.
@@ -1426,12 +1637,118 @@ module Polars
1426
1637
  # # └───────┴──────────┘
1427
1638
  def bottom_k(k: 5)
1428
1639
  k = Utils.parse_into_expression(k)
1429
- _from_rbexpr(_rbexpr.bottom_k(k))
1640
+ wrap_expr(_rbexpr.bottom_k(k))
1641
+ end
1642
+
1643
+ # Return the elements corresponding to the `k` smallest elements of the `by` column(s).
1644
+ #
1645
+ # Non-null elements are always preferred over null elements, regardless of
1646
+ # the value of `reverse`. The output is not guaranteed to be in any
1647
+ # particular order, call :func:`sort` after this function if you wish the
1648
+ # output to be sorted.
1649
+ #
1650
+ # @param by [Object]
1651
+ # Column(s) used to determine the smallest elements.
1652
+ # Accepts expression input. Strings are parsed as column names.
1653
+ # @param k [Integer]
1654
+ # Number of elements to return.
1655
+ # @param reverse [Object]
1656
+ # Consider the `k` largest elements of the `by` column(s) (instead of the `k`
1657
+ # smallest). This can be specified per column by passing an array of
1658
+ # booleans.
1659
+ #
1660
+ # @return [Expr]
1661
+ #
1662
+ # @example
1663
+ # df = Polars::DataFrame.new(
1664
+ # {
1665
+ # "a" => [1, 2, 3, 4, 5, 6],
1666
+ # "b" => [6, 5, 4, 3, 2, 1],
1667
+ # "c" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"],
1668
+ # }
1669
+ # )
1670
+ # # =>
1671
+ # # shape: (6, 3)
1672
+ # # ┌─────┬─────┬────────┐
1673
+ # # │ a ┆ b ┆ c │
1674
+ # # │ --- ┆ --- ┆ --- │
1675
+ # # │ i64 ┆ i64 ┆ str │
1676
+ # # ╞═════╪═════╪════════╡
1677
+ # # │ 1 ┆ 6 ┆ Apple │
1678
+ # # │ 2 ┆ 5 ┆ Orange │
1679
+ # # │ 3 ┆ 4 ┆ Apple │
1680
+ # # │ 4 ┆ 3 ┆ Apple │
1681
+ # # │ 5 ┆ 2 ┆ Banana │
1682
+ # # │ 6 ┆ 1 ┆ Banana │
1683
+ # # └─────┴─────┴────────┘
1684
+ #
1685
+ # @example Get the bottom 2 rows by column `a` or `b`.
1686
+ # df.select(
1687
+ # Polars.all.bottom_k_by("a", k: 2).name.suffix("_btm_by_a"),
1688
+ # Polars.all.bottom_k_by("b", k: 2).name.suffix("_btm_by_b")
1689
+ # )
1690
+ # # =>
1691
+ # # shape: (2, 6)
1692
+ # # ┌────────────┬────────────┬────────────┬────────────┬────────────┬────────────┐
1693
+ # # │ a_btm_by_a ┆ b_btm_by_a ┆ c_btm_by_a ┆ a_btm_by_b ┆ b_btm_by_b ┆ c_btm_by_b │
1694
+ # # │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
1695
+ # # │ i64 ┆ i64 ┆ str ┆ i64 ┆ i64 ┆ str │
1696
+ # # ╞════════════╪════════════╪════════════╪════════════╪════════════╪════════════╡
1697
+ # # │ 1 ┆ 6 ┆ Apple ┆ 6 ┆ 1 ┆ Banana │
1698
+ # # │ 2 ┆ 5 ┆ Orange ┆ 5 ┆ 2 ┆ Banana │
1699
+ # # └────────────┴────────────┴────────────┴────────────┴────────────┴────────────┘
1700
+ #
1701
+ # @example Get the bottom 2 rows by multiple columns with given order.
1702
+ # df.select(
1703
+ # Polars.all
1704
+ # .bottom_k_by(["c", "a"], k: 2, reverse: [false, true])
1705
+ # .name.suffix("_by_ca"),
1706
+ # Polars.all
1707
+ # .bottom_k_by(["c", "b"], k: 2, reverse: [false, true])
1708
+ # .name.suffix("_by_cb"),
1709
+ # )
1710
+ # # =>
1711
+ # # shape: (2, 6)
1712
+ # # ┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
1713
+ # # │ a_by_ca ┆ b_by_ca ┆ c_by_ca ┆ a_by_cb ┆ b_by_cb ┆ c_by_cb │
1714
+ # # │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
1715
+ # # │ i64 ┆ i64 ┆ str ┆ i64 ┆ i64 ┆ str │
1716
+ # # ╞═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
1717
+ # # │ 4 ┆ 3 ┆ Apple ┆ 1 ┆ 6 ┆ Apple │
1718
+ # # │ 3 ┆ 4 ┆ Apple ┆ 3 ┆ 4 ┆ Apple │
1719
+ # # └─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
1720
+ #
1721
+ # @example Get the bottom 2 rows by column `a` in each group.
1722
+ # df.group_by("c", maintain_order: true)
1723
+ # .agg(Polars.all.bottom_k_by("a", k: 2))
1724
+ # .explode(Polars.all.exclude("c"))
1725
+ # # =>
1726
+ # # shape: (5, 3)
1727
+ # # ┌────────┬─────┬─────┐
1728
+ # # │ c ┆ a ┆ b │
1729
+ # # │ --- ┆ --- ┆ --- │
1730
+ # # │ str ┆ i64 ┆ i64 │
1731
+ # # ╞════════╪═════╪═════╡
1732
+ # # │ Apple ┆ 1 ┆ 6 │
1733
+ # # │ Apple ┆ 3 ┆ 4 │
1734
+ # # │ Orange ┆ 2 ┆ 5 │
1735
+ # # │ Banana ┆ 5 ┆ 2 │
1736
+ # # │ Banana ┆ 6 ┆ 1 │
1737
+ # # └────────┴─────┴─────┘
1738
+ def bottom_k_by(
1739
+ by,
1740
+ k: 5,
1741
+ reverse: false
1742
+ )
1743
+ k = Utils.parse_into_expression(k)
1744
+ by = Utils.parse_into_list_of_expressions(by)
1745
+ reverse = Utils.extend_bool(reverse, by.length, "reverse", "by")
1746
+ wrap_expr(_rbexpr.bottom_k_by(by, k, reverse))
1430
1747
  end
1431
1748
 
1432
1749
  # Get the index values that would sort this column.
1433
1750
  #
1434
- # @param reverse [Boolean]
1751
+ # @param descending [Boolean]
1435
1752
  # Sort in reverse (descending) order.
1436
1753
  # @param nulls_last [Boolean]
1437
1754
  # Place null values last instead of first.
@@ -1456,8 +1773,8 @@ module Polars
1456
1773
  # # │ 0 │
1457
1774
  # # │ 2 │
1458
1775
  # # └─────┘
1459
- def arg_sort(reverse: false, nulls_last: false)
1460
- _from_rbexpr(_rbexpr.arg_sort(reverse, nulls_last))
1776
+ def arg_sort(descending: false, nulls_last: false)
1777
+ wrap_expr(_rbexpr.arg_sort(descending, nulls_last))
1461
1778
  end
1462
1779
 
1463
1780
  # Get the index of the maximal value.
@@ -1481,7 +1798,7 @@ module Polars
1481
1798
  # # │ 2 │
1482
1799
  # # └─────┘
1483
1800
  def arg_max
1484
- _from_rbexpr(_rbexpr.arg_max)
1801
+ wrap_expr(_rbexpr.arg_max)
1485
1802
  end
1486
1803
 
1487
1804
  # Get the index of the minimal value.
@@ -1505,13 +1822,50 @@ module Polars
1505
1822
  # # │ 1 │
1506
1823
  # # └─────┘
1507
1824
  def arg_min
1508
- _from_rbexpr(_rbexpr.arg_min)
1825
+ wrap_expr(_rbexpr.arg_min)
1826
+ end
1827
+
1828
+ # Get the index of the first occurrence of a value, or `nil` if it's not found.
1829
+ #
1830
+ # @param element [Object]
1831
+ # Value to find.
1832
+ #
1833
+ # @return [Expr]
1834
+ #
1835
+ # @example
1836
+ # df = Polars::DataFrame.new({"a" => [1, nil, 17]})
1837
+ # df.select(
1838
+ # [
1839
+ # Polars.col("a").index_of(17).alias("seventeen"),
1840
+ # Polars.col("a").index_of(nil).alias("null"),
1841
+ # Polars.col("a").index_of(55).alias("fiftyfive")
1842
+ # ]
1843
+ # )
1844
+ # # =>
1845
+ # # shape: (1, 3)
1846
+ # # ┌───────────┬──────┬───────────┐
1847
+ # # │ seventeen ┆ null ┆ fiftyfive │
1848
+ # # │ --- ┆ --- ┆ --- │
1849
+ # # │ u32 ┆ u32 ┆ u32 │
1850
+ # # ╞═══════════╪══════╪═══════════╡
1851
+ # # │ 2 ┆ 1 ┆ null │
1852
+ # # └───────────┴──────┴───────────┘
1853
+ def index_of(element)
1854
+ element = Utils.parse_into_expression(element, str_as_lit: true)
1855
+ wrap_expr(_rbexpr.index_of(element))
1509
1856
  end
1510
1857
 
1511
1858
  # Find indices where elements should be inserted to maintain order.
1512
1859
  #
1513
1860
  # @param element [Object]
1514
1861
  # Expression or scalar value.
1862
+ # @param side ['any', 'left', 'right']
1863
+ # If 'any', the index of the first suitable location found is given.
1864
+ # If 'left', the index of the leftmost suitable location found is given.
1865
+ # If 'right', return the rightmost suitable location found is given.
1866
+ # @param descending [Boolean]
1867
+ # Boolean indicating whether the values are descending or not (they
1868
+ # are required to be sorted either way).
1515
1869
  #
1516
1870
  # @return [Expr]
1517
1871
  #
@@ -1537,9 +1891,9 @@ module Polars
1537
1891
  # # ╞══════╪═══════╪═════╡
1538
1892
  # # │ 0 ┆ 2 ┆ 4 │
1539
1893
  # # └──────┴───────┴─────┘
1540
- def search_sorted(element, side: "any")
1894
+ def search_sorted(element, side: "any", descending: false)
1541
1895
  element = Utils.parse_into_expression(element, str_as_lit: false)
1542
- _from_rbexpr(_rbexpr.search_sorted(element, side))
1896
+ wrap_expr(_rbexpr.search_sorted(element, side, descending))
1543
1897
  end
1544
1898
 
1545
1899
  # Sort this column by the ordering of another column, or multiple other columns.
@@ -1549,9 +1903,18 @@ module Polars
1549
1903
  #
1550
1904
  # @param by [Object]
1551
1905
  # The column(s) used for sorting.
1552
- # @param reverse [Boolean]
1906
+ # @param more_by [Array]
1907
+ # Additional columns to sort by, specified as positional arguments.
1908
+ # @param descending [Boolean]
1553
1909
  # false -> order from small to large.
1554
1910
  # true -> order from large to small.
1911
+ # @param nulls_last [Boolean]
1912
+ # Place null values last; can specify a single boolean applying to all columns
1913
+ # or an array of booleans for per-column control.
1914
+ # @param multithreaded [Boolean]
1915
+ # Sort using multiple threads.
1916
+ # @param maintain_order [Boolean]
1917
+ # Whether the order should be maintained if elements are equal.
1555
1918
  #
1556
1919
  # @return [Expr]
1557
1920
  #
@@ -1584,13 +1947,13 @@ module Polars
1584
1947
  # # │ one │
1585
1948
  # # │ two │
1586
1949
  # # └───────┘
1587
- def sort_by(by, *more_by, reverse: false, nulls_last: false, multithreaded: true, maintain_order: false)
1950
+ def sort_by(by, *more_by, descending: false, nulls_last: false, multithreaded: true, maintain_order: false)
1588
1951
  by = Utils.parse_into_list_of_expressions(by, *more_by)
1589
- reverse = Utils.extend_bool(reverse, by.length, "reverse", "by")
1952
+ descending = Utils.extend_bool(descending, by.length, "descending", "by")
1590
1953
  nulls_last = Utils.extend_bool(nulls_last, by.length, "nulls_last", "by")
1591
- _from_rbexpr(
1954
+ wrap_expr(
1592
1955
  _rbexpr.sort_by(
1593
- by, reverse, nulls_last, multithreaded, maintain_order
1956
+ by, descending, nulls_last, multithreaded, maintain_order
1594
1957
  )
1595
1958
  )
1596
1959
  end
@@ -1599,6 +1962,11 @@ module Polars
1599
1962
  #
1600
1963
  # @param indices [Expr]
1601
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
1602
1970
  #
1603
1971
  # @return [Expr]
1604
1972
  #
@@ -1616,7 +1984,7 @@ module Polars
1616
1984
  # "value" => [1, 98, 2, 3, 99, 4]
1617
1985
  # }
1618
1986
  # )
1619
- # df.group_by("group", maintain_order: true).agg(Polars.col("value").take([2, 1]))
1987
+ # df.group_by("group", maintain_order: true).agg(Polars.col("value").gather([2, 1]))
1620
1988
  # # =>
1621
1989
  # # shape: (2, 2)
1622
1990
  # # ┌───────┬───────────┐
@@ -1627,20 +1995,24 @@ module Polars
1627
1995
  # # │ one ┆ [2, 98] │
1628
1996
  # # │ two ┆ [4, 99] │
1629
1997
  # # └───────┴───────────┘
1630
- def gather(indices)
1998
+ def gather(indices, null_on_oob: false)
1631
1999
  if indices.is_a?(::Array)
1632
- indices_lit = Polars.lit(Series.new("", indices, dtype: :u32))._rbexpr
2000
+ indices_lit_rbexpr = Polars.lit(Series.new("", indices, dtype: Int64))._rbexpr
1633
2001
  else
1634
- indices_lit = Utils.parse_into_expression(indices, str_as_lit: false)
2002
+ indices_lit_rbexpr = Utils.parse_into_expression(indices)
1635
2003
  end
1636
- _from_rbexpr(_rbexpr.gather(indices_lit))
2004
+ wrap_expr(_rbexpr.gather(indices_lit_rbexpr, null_on_oob))
1637
2005
  end
1638
- alias_method :take, :gather
1639
2006
 
1640
2007
  # Return a single value by index.
1641
2008
  #
1642
2009
  # @param index [Object]
1643
2010
  # An expression that leads to a UInt32 index.
2011
+ # @param null_on_oob [Boolean]
2012
+ # Behavior if an index is out of bounds:
2013
+ #
2014
+ # - true -> set the result to null
2015
+ # - false -> raise an error
1644
2016
  #
1645
2017
  # @return [Expr]
1646
2018
  #
@@ -1669,9 +2041,9 @@ module Polars
1669
2041
  # # │ one ┆ 98 │
1670
2042
  # # │ two ┆ 99 │
1671
2043
  # # └───────┴───────┘
1672
- def get(index)
2044
+ def get(index, null_on_oob: false)
1673
2045
  index_lit = Utils.parse_into_expression(index)
1674
- _from_rbexpr(_rbexpr.get(index_lit))
2046
+ wrap_expr(_rbexpr.get(index_lit, null_on_oob))
1675
2047
  end
1676
2048
 
1677
2049
  # Shift the values by a given period.
@@ -1703,38 +2075,10 @@ module Polars
1703
2075
  fill_value = Utils.parse_into_expression(fill_value, str_as_lit: true)
1704
2076
  end
1705
2077
  n = Utils.parse_into_expression(n)
1706
- _from_rbexpr(_rbexpr.shift(n, fill_value))
2078
+ wrap_expr(_rbexpr.shift(n, fill_value))
1707
2079
  end
1708
2080
 
1709
- # Shift the values by a given period and fill the resulting null values.
1710
- #
1711
- # @param periods [Integer]
1712
- # Number of places to shift (may be negative).
1713
- # @param fill_value [Object]
1714
- # Fill nil values with the result of this expression.
1715
- #
1716
- # @return [Expr]
1717
- #
1718
- # @example
1719
- # df = Polars::DataFrame.new({"foo" => [1, 2, 3, 4]})
1720
- # df.select(Polars.col("foo").shift_and_fill(1, "a"))
1721
- # # =>
1722
- # # shape: (4, 1)
1723
- # # ┌─────┐
1724
- # # │ foo │
1725
- # # │ --- │
1726
- # # │ str │
1727
- # # ╞═════╡
1728
- # # │ a │
1729
- # # │ 1 │
1730
- # # │ 2 │
1731
- # # │ 3 │
1732
- # # └─────┘
1733
- def shift_and_fill(periods, fill_value)
1734
- shift(periods, fill_value: fill_value)
1735
- end
1736
-
1737
- # Fill null values using the specified value or strategy.
2081
+ # Fill null values using the specified value or strategy.
1738
2082
  #
1739
2083
  # To interpolate over null values see interpolate.
1740
2084
  #
@@ -1806,9 +2150,9 @@ module Polars
1806
2150
 
1807
2151
  if !value.nil?
1808
2152
  value = Utils.parse_into_expression(value, str_as_lit: true)
1809
- _from_rbexpr(_rbexpr.fill_null(value))
2153
+ wrap_expr(_rbexpr.fill_null(value))
1810
2154
  else
1811
- _from_rbexpr(_rbexpr.fill_null_with_strategy(strategy, limit))
2155
+ wrap_expr(_rbexpr.fill_null_with_strategy(strategy, limit))
1812
2156
  end
1813
2157
  end
1814
2158
 
@@ -1835,9 +2179,9 @@ module Polars
1835
2179
  # # │ null ┆ zero │
1836
2180
  # # │ zero ┆ 6.0 │
1837
2181
  # # └──────┴──────┘
1838
- def fill_nan(fill_value)
1839
- fill_value = Utils.parse_into_expression(fill_value, str_as_lit: true)
1840
- _from_rbexpr(_rbexpr.fill_nan(fill_value))
2182
+ def fill_nan(value)
2183
+ fill_value_rbexpr = Utils.parse_into_expression(value, str_as_lit: true)
2184
+ wrap_expr(_rbexpr.fill_nan(fill_value_rbexpr))
1841
2185
  end
1842
2186
 
1843
2187
  # Fill missing values with the latest seen values.
@@ -1867,7 +2211,7 @@ module Polars
1867
2211
  # # │ 2 ┆ 6 │
1868
2212
  # # └─────┴─────┘
1869
2213
  def forward_fill(limit: nil)
1870
- _from_rbexpr(_rbexpr.forward_fill(limit))
2214
+ fill_null(strategy: "forward", limit: limit)
1871
2215
  end
1872
2216
 
1873
2217
  # Fill missing values with the next to be seen values.
@@ -1897,7 +2241,7 @@ module Polars
1897
2241
  # # │ null ┆ 6 │
1898
2242
  # # └──────┴─────┘
1899
2243
  def backward_fill(limit: nil)
1900
- _from_rbexpr(_rbexpr.backward_fill(limit))
2244
+ fill_null(strategy: "backward", limit: limit)
1901
2245
  end
1902
2246
 
1903
2247
  # Reverse the selection.
@@ -1933,7 +2277,7 @@ module Polars
1933
2277
  # # │ 5 ┆ banana ┆ 1 ┆ beetle ┆ 1 ┆ banana ┆ 5 ┆ beetle │
1934
2278
  # # └─────┴────────┴─────┴────────┴───────────┴────────────────┴───────────┴──────────────┘
1935
2279
  def reverse
1936
- _from_rbexpr(_rbexpr.reverse)
2280
+ wrap_expr(_rbexpr.reverse)
1937
2281
  end
1938
2282
 
1939
2283
  # Get standard deviation.
@@ -1956,7 +2300,7 @@ module Polars
1956
2300
  # # │ 1.0 │
1957
2301
  # # └─────┘
1958
2302
  def std(ddof: 1)
1959
- _from_rbexpr(_rbexpr.std(ddof))
2303
+ wrap_expr(_rbexpr.std(ddof))
1960
2304
  end
1961
2305
 
1962
2306
  # Get variance.
@@ -1979,7 +2323,7 @@ module Polars
1979
2323
  # # │ 1.0 │
1980
2324
  # # └─────┘
1981
2325
  def var(ddof: 1)
1982
- _from_rbexpr(_rbexpr.var(ddof))
2326
+ wrap_expr(_rbexpr.var(ddof))
1983
2327
  end
1984
2328
 
1985
2329
  # Get maximum value.
@@ -1999,7 +2343,39 @@ module Polars
1999
2343
  # # │ 1.0 │
2000
2344
  # # └─────┘
2001
2345
  def max
2002
- _from_rbexpr(_rbexpr.max)
2346
+ wrap_expr(_rbexpr.max)
2347
+ end
2348
+
2349
+ # Get maximum value, ordered by another expression.
2350
+ #
2351
+ # If the by expression has multiple values equal to the maximum it is not
2352
+ # defined which value will be chosen.
2353
+ #
2354
+ # @note
2355
+ # This functionality is considered **unstable**. It may be changed
2356
+ # at any point without it being considered a breaking change.
2357
+ #
2358
+ # @param by [Object]
2359
+ # Column used to determine the largest element.
2360
+ # Accepts expression input. Strings are parsed as column names.
2361
+ #
2362
+ # @return [Expr]
2363
+ #
2364
+ # @example
2365
+ # df = Polars::DataFrame.new({"a" => [-1.0, Float::NAN, 1.0], "b" => ["x", "y", "z"]})
2366
+ # df.select(Polars.col("b").max_by("a"))
2367
+ # # =>
2368
+ # # shape: (1, 1)
2369
+ # # ┌─────┐
2370
+ # # │ b │
2371
+ # # │ --- │
2372
+ # # │ str │
2373
+ # # ╞═════╡
2374
+ # # │ z │
2375
+ # # └─────┘
2376
+ def max_by(by)
2377
+ by_rbexpr = Utils.parse_into_expression(by)
2378
+ wrap_expr(_rbexpr.max_by(by_rbexpr))
2003
2379
  end
2004
2380
 
2005
2381
  # Get minimum value.
@@ -2019,7 +2395,39 @@ module Polars
2019
2395
  # # │ -1.0 │
2020
2396
  # # └──────┘
2021
2397
  def min
2022
- _from_rbexpr(_rbexpr.min)
2398
+ wrap_expr(_rbexpr.min)
2399
+ end
2400
+
2401
+ # Get minimum value, ordered by another expression.
2402
+ #
2403
+ # If the by expression has multiple values equal to the minimum it is not
2404
+ # defined which value will be chosen.
2405
+ #
2406
+ # @note
2407
+ # This functionality is considered **unstable**. It may be changed
2408
+ # at any point without it being considered a breaking change.
2409
+ #
2410
+ # @param by [Object]
2411
+ # Column used to determine the smallest element.
2412
+ # Accepts expression input. Strings are parsed as column names.
2413
+ #
2414
+ # @return [Expr]
2415
+ #
2416
+ # @example
2417
+ # df = Polars::DataFrame.new({"a" => [-1.0, Float::NAN, 1.0], "b" => ["x", "y", "z"]})
2418
+ # df.select(Polars.col("b").min_by("a"))
2419
+ # # =>
2420
+ # # shape: (1, 1)
2421
+ # # ┌─────┐
2422
+ # # │ b │
2423
+ # # │ --- │
2424
+ # # │ str │
2425
+ # # ╞═════╡
2426
+ # # │ x │
2427
+ # # └─────┘
2428
+ def min_by(by)
2429
+ by_rbexpr = Utils.parse_into_expression(by)
2430
+ wrap_expr(_rbexpr.min_by(by_rbexpr))
2023
2431
  end
2024
2432
 
2025
2433
  # Get maximum value, but propagate/poison encountered NaN values.
@@ -2039,7 +2447,7 @@ module Polars
2039
2447
  # # │ NaN │
2040
2448
  # # └─────┘
2041
2449
  def nan_max
2042
- _from_rbexpr(_rbexpr.nan_max)
2450
+ wrap_expr(_rbexpr.nan_max)
2043
2451
  end
2044
2452
 
2045
2453
  # Get minimum value, but propagate/poison encountered NaN values.
@@ -2059,7 +2467,7 @@ module Polars
2059
2467
  # # │ NaN │
2060
2468
  # # └─────┘
2061
2469
  def nan_min
2062
- _from_rbexpr(_rbexpr.nan_min)
2470
+ wrap_expr(_rbexpr.nan_min)
2063
2471
  end
2064
2472
 
2065
2473
  # Get sum value.
@@ -2067,8 +2475,8 @@ module Polars
2067
2475
  # @return [Expr]
2068
2476
  #
2069
2477
  # @note
2070
- # Dtypes in `:i8`, `:u8`, `:i16`, and `:u16` are cast to
2071
- # `:i64` before summing to prevent overflow issues.
2478
+ # Dtypes in \\\\{Int8, UInt8, Int16, UInt16} are cast to
2479
+ # Int64 before summing to prevent overflow issues.
2072
2480
  #
2073
2481
  # @example
2074
2482
  # df = Polars::DataFrame.new({"a" => [-1, 0, 1]})
@@ -2083,7 +2491,7 @@ module Polars
2083
2491
  # # │ 0 │
2084
2492
  # # └─────┘
2085
2493
  def sum
2086
- _from_rbexpr(_rbexpr.sum)
2494
+ wrap_expr(_rbexpr.sum)
2087
2495
  end
2088
2496
 
2089
2497
  # Get mean value.
@@ -2103,7 +2511,7 @@ module Polars
2103
2511
  # # │ 0.0 │
2104
2512
  # # └─────┘
2105
2513
  def mean
2106
- _from_rbexpr(_rbexpr.mean)
2514
+ wrap_expr(_rbexpr.mean)
2107
2515
  end
2108
2516
 
2109
2517
  # Get median value using linear interpolation.
@@ -2123,7 +2531,7 @@ module Polars
2123
2531
  # # │ 0.0 │
2124
2532
  # # └─────┘
2125
2533
  def median
2126
- _from_rbexpr(_rbexpr.median)
2534
+ wrap_expr(_rbexpr.median)
2127
2535
  end
2128
2536
 
2129
2537
  # Compute the product of an expression.
@@ -2143,7 +2551,7 @@ module Polars
2143
2551
  # # │ 6 │
2144
2552
  # # └─────┘
2145
2553
  def product
2146
- _from_rbexpr(_rbexpr.product)
2554
+ wrap_expr(_rbexpr.product)
2147
2555
  end
2148
2556
 
2149
2557
  # Count unique values.
@@ -2163,7 +2571,7 @@ module Polars
2163
2571
  # # │ 2 │
2164
2572
  # # └─────┘
2165
2573
  def n_unique
2166
- _from_rbexpr(_rbexpr.n_unique)
2574
+ wrap_expr(_rbexpr.n_unique)
2167
2575
  end
2168
2576
 
2169
2577
  # Approx count unique values.
@@ -2185,9 +2593,8 @@ module Polars
2185
2593
  # # │ 2 │
2186
2594
  # # └─────┘
2187
2595
  def approx_n_unique
2188
- _from_rbexpr(_rbexpr.approx_n_unique)
2596
+ wrap_expr(_rbexpr.approx_n_unique)
2189
2597
  end
2190
- alias_method :approx_unique, :approx_n_unique
2191
2598
 
2192
2599
  # Count null values.
2193
2600
  #
@@ -2211,7 +2618,33 @@ module Polars
2211
2618
  # # │ 2 ┆ 0 │
2212
2619
  # # └─────┴─────┘
2213
2620
  def null_count
2214
- _from_rbexpr(_rbexpr.null_count)
2621
+ wrap_expr(_rbexpr.null_count)
2622
+ end
2623
+
2624
+ # Check whether the expression contains one or more null values.
2625
+ #
2626
+ # @return [Expr]
2627
+ #
2628
+ # @example
2629
+ # df = Polars::DataFrame.new(
2630
+ # {
2631
+ # "a" => [nil, 1, nil],
2632
+ # "b" => [10, nil, 300],
2633
+ # "c" => [350, 650, 850]
2634
+ # }
2635
+ # )
2636
+ # df.select(Polars.all.has_nulls)
2637
+ # # =>
2638
+ # # shape: (1, 3)
2639
+ # # ┌──────┬──────┬───────┐
2640
+ # # │ a ┆ b ┆ c │
2641
+ # # │ --- ┆ --- ┆ --- │
2642
+ # # │ bool ┆ bool ┆ bool │
2643
+ # # ╞══════╪══════╪═══════╡
2644
+ # # │ true ┆ true ┆ false │
2645
+ # # └──────┴──────┴───────┘
2646
+ def has_nulls
2647
+ null_count > 0
2215
2648
  end
2216
2649
 
2217
2650
  # Get index of first unique value.
@@ -2251,7 +2684,7 @@ module Polars
2251
2684
  # # │ 1 │
2252
2685
  # # └─────┘
2253
2686
  def arg_unique
2254
- _from_rbexpr(_rbexpr.arg_unique)
2687
+ wrap_expr(_rbexpr.arg_unique)
2255
2688
  end
2256
2689
 
2257
2690
  # Get unique values of this expression.
@@ -2276,14 +2709,19 @@ module Polars
2276
2709
  # # └─────┘
2277
2710
  def unique(maintain_order: false)
2278
2711
  if maintain_order
2279
- _from_rbexpr(_rbexpr.unique_stable)
2712
+ wrap_expr(_rbexpr.unique_stable)
2280
2713
  else
2281
- _from_rbexpr(_rbexpr.unique)
2714
+ wrap_expr(_rbexpr.unique)
2282
2715
  end
2283
2716
  end
2284
2717
 
2285
2718
  # Get the first value.
2286
2719
  #
2720
+ # @param ignore_nulls [Boolean]
2721
+ # Ignore null values (default `false`).
2722
+ # If set to `true`, the first non-null value is returned, otherwise `nil` is
2723
+ # returned if no non-null value exists.
2724
+ #
2287
2725
  # @return [Expr]
2288
2726
  #
2289
2727
  # @example
@@ -2298,12 +2736,17 @@ module Polars
2298
2736
  # # ╞═════╡
2299
2737
  # # │ 1 │
2300
2738
  # # └─────┘
2301
- def first
2302
- _from_rbexpr(_rbexpr.first)
2739
+ def first(ignore_nulls: false)
2740
+ wrap_expr(_rbexpr.first(ignore_nulls))
2303
2741
  end
2304
2742
 
2305
2743
  # Get the last value.
2306
2744
  #
2745
+ # @param ignore_nulls [Boolean]
2746
+ # Ignore null values (default `false`).
2747
+ # If set to `true`, the last non-null value is returned, otherwise `nil` is
2748
+ # returned if no non-null value exists.
2749
+ #
2307
2750
  # @return [Expr]
2308
2751
  #
2309
2752
  # @example
@@ -2318,8 +2761,45 @@ module Polars
2318
2761
  # # ╞═════╡
2319
2762
  # # │ 2 │
2320
2763
  # # └─────┘
2321
- def last
2322
- _from_rbexpr(_rbexpr.last)
2764
+ def last(ignore_nulls: false)
2765
+ wrap_expr(_rbexpr.last(ignore_nulls))
2766
+ end
2767
+
2768
+ # Get the single value.
2769
+ #
2770
+ # This raises an error if there is not exactly one value.
2771
+ #
2772
+ # @param allow_empty [Boolean]
2773
+ # Allow having no values to return `null`.
2774
+ #
2775
+ # @return [Expr]
2776
+ #
2777
+ # @example
2778
+ # df = Polars::DataFrame.new({"a" => [1]})
2779
+ # df.select(Polars.col("a").item)
2780
+ # # =>
2781
+ # # shape: (1, 1)
2782
+ # # ┌─────┐
2783
+ # # │ a │
2784
+ # # │ --- │
2785
+ # # │ i64 │
2786
+ # # ╞═════╡
2787
+ # # │ 1 │
2788
+ # # └─────┘
2789
+ #
2790
+ # @example
2791
+ # df.head(0).select(Polars.col("a").item(allow_empty: true))
2792
+ # # =>
2793
+ # # shape: (1, 1)
2794
+ # # ┌──────┐
2795
+ # # │ a │
2796
+ # # │ --- │
2797
+ # # │ i64 │
2798
+ # # ╞══════╡
2799
+ # # │ null │
2800
+ # # └──────┘
2801
+ def item(allow_empty: false)
2802
+ Utils.wrap_expr(_rbexpr.item(allow_empty))
2323
2803
  end
2324
2804
 
2325
2805
  # Apply window function over a subgroup.
@@ -2327,8 +2807,40 @@ module Polars
2327
2807
  # This is similar to a group by + aggregation + self join.
2328
2808
  # Or similar to [window functions in Postgres](https://www.postgresql.org/docs/current/tutorial-window.html).
2329
2809
  #
2330
- # @param expr [Object]
2331
- # Column(s) to group by.
2810
+ # @param partition_by [Object]
2811
+ # Column(s) to group by. Accepts expression input. Strings are parsed as
2812
+ # column names.
2813
+ # @param more_exprs [Array]
2814
+ # Additional columns to group by, specified as positional arguments.
2815
+ # @param order_by [Object]
2816
+ # Order the window functions/aggregations with the partitioned groups by
2817
+ # the result of the expression passed to `order_by`.
2818
+ # @param descending [Boolean]
2819
+ # In case 'order_by' is given, indicate whether to order in ascending or
2820
+ # descending order.
2821
+ # @param nulls_last [Boolean]
2822
+ # In case 'order_by' is given, indicate whether to order
2823
+ # the nulls in last position.
2824
+ # @param mapping_strategy ['group_to_rows', 'join', 'explode']
2825
+ # - group_to_rows
2826
+ # If the aggregation results in multiple values per group, map them back
2827
+ # to their row position in the DataFrame. This can only be done if each
2828
+ # group yields the same elements before aggregation as after. If the
2829
+ # aggregation results in one scalar value per group, this value will be
2830
+ # mapped to every row.
2831
+ # - join
2832
+ # If the aggregation may result in multiple values per group, join the
2833
+ # values as 'List<group_dtype>' to each row position. Warning: this can be
2834
+ # memory intensive. If the aggregation always results in one scalar value
2835
+ # per group, join this value as '<group_dtype>' to each row position.
2836
+ # - explode
2837
+ # If the aggregation may result in multiple values per group, map each
2838
+ # value to a new row, similar to the results of `group_by` + `agg` +
2839
+ # `explode`. If the aggregation always results in one scalar value per
2840
+ # group, map this value to one row position. Sorting of the given groups
2841
+ # is required if the groups are not part of the window operation for the
2842
+ # operation, otherwise the result would not make sense. This operation
2843
+ # changes the number of rows.
2332
2844
  #
2333
2845
  # @return [Expr]
2334
2846
  #
@@ -2339,7 +2851,7 @@ module Polars
2339
2851
  # "values" => [1, 2, 3]
2340
2852
  # }
2341
2853
  # )
2342
- # df.with_column(
2854
+ # df.with_columns(
2343
2855
  # Polars.col("values").max.over("groups").alias("max_by_group")
2344
2856
  # )
2345
2857
  # # =>
@@ -2381,9 +2893,145 @@ module Polars
2381
2893
  # # │ 6 │
2382
2894
  # # │ 4 │
2383
2895
  # # └────────┘
2384
- def over(expr)
2385
- rbexprs = Utils.parse_into_list_of_expressions(expr)
2386
- _from_rbexpr(_rbexpr.over(rbexprs))
2896
+ #
2897
+ # @example
2898
+ # df = Polars::DataFrame.new(
2899
+ # {
2900
+ # "store_id" => ["a", "a", "b", "b"],
2901
+ # "date" => [Date.new(2024, 9, 18), Date.new(2024, 9, 17), Date.new(2024, 9, 18), Date.new(2024, 9, 16)],
2902
+ # "sales" => [7, 9, 8, 10]
2903
+ # }
2904
+ # )
2905
+ # df.with_columns(
2906
+ # cumulative_sales: Polars.col("sales").cum_sum.over("store_id", order_by: "date")
2907
+ # )
2908
+ # # =>
2909
+ # # shape: (4, 4)
2910
+ # # ┌──────────┬────────────┬───────┬──────────────────┐
2911
+ # # │ store_id ┆ date ┆ sales ┆ cumulative_sales │
2912
+ # # │ --- ┆ --- ┆ --- ┆ --- │
2913
+ # # │ str ┆ date ┆ i64 ┆ i64 │
2914
+ # # ╞══════════╪════════════╪═══════╪══════════════════╡
2915
+ # # │ a ┆ 2024-09-18 ┆ 7 ┆ 16 │
2916
+ # # │ a ┆ 2024-09-17 ┆ 9 ┆ 9 │
2917
+ # # │ b ┆ 2024-09-18 ┆ 8 ┆ 18 │
2918
+ # # │ b ┆ 2024-09-16 ┆ 10 ┆ 10 │
2919
+ # # └──────────┴────────────┴───────┴──────────────────┘
2920
+ def over(partition_by = nil, *more_exprs, order_by: nil, descending: false, nulls_last: false, mapping_strategy: "group_to_rows")
2921
+ partition_by_rbexprs =
2922
+ if !partition_by.nil?
2923
+ Utils.parse_into_list_of_expressions(partition_by, *more_exprs)
2924
+ else
2925
+ nil
2926
+ end
2927
+
2928
+ order_by_rbexprs = !order_by.nil? ? Utils.parse_into_list_of_expressions(order_by) : nil
2929
+
2930
+ wrap_expr(_rbexpr.over(partition_by_rbexprs, order_by_rbexprs, descending, nulls_last, mapping_strategy))
2931
+ end
2932
+
2933
+ # Create rolling groups based on a temporal or integer column.
2934
+ #
2935
+ # If you have a time series `<t_0, t_1, ..., t_n>`, then by default the
2936
+ # windows created will be
2937
+ #
2938
+ # * (t_0 - period, t_0]
2939
+ # * (t_1 - period, t_1]
2940
+ # * ...
2941
+ # * (t_n - period, t_n]
2942
+ #
2943
+ # whereas if you pass a non-default `offset`, then the windows will be
2944
+ #
2945
+ # * (t_0 + offset, t_0 + offset + period]
2946
+ # * (t_1 + offset, t_1 + offset + period]
2947
+ # * ...
2948
+ # * (t_n + offset, t_n + offset + period]
2949
+ #
2950
+ # The `period` and `offset` arguments are created either from a timedelta, or
2951
+ # by using the following string language:
2952
+ #
2953
+ # - 1ns (1 nanosecond)
2954
+ # - 1us (1 microsecond)
2955
+ # - 1ms (1 millisecond)
2956
+ # - 1s (1 second)
2957
+ # - 1m (1 minute)
2958
+ # - 1h (1 hour)
2959
+ # - 1d (1 calendar day)
2960
+ # - 1w (1 calendar week)
2961
+ # - 1mo (1 calendar month)
2962
+ # - 1q (1 calendar quarter)
2963
+ # - 1y (1 calendar year)
2964
+ # - 1i (1 index count)
2965
+ #
2966
+ # Or combine them:
2967
+ # "3d12h4m25s" # 3 days, 12 hours, 4 minutes, and 25 seconds
2968
+ #
2969
+ # By "calendar day", we mean the corresponding time on the next day (which may
2970
+ # not be 24 hours, due to daylight savings). Similarly for "calendar week",
2971
+ # "calendar month", "calendar quarter", and "calendar year".
2972
+ #
2973
+ # @param index_column [Object]
2974
+ # Column used to group based on the time window.
2975
+ # Often of type Date/Datetime.
2976
+ # This column must be sorted in ascending order.
2977
+ # In case of a rolling group by on indices, dtype needs to be one of
2978
+ # \\\\{UInt32, UInt64, Int32, Int64}. Note that the first three get temporarily
2979
+ # cast to Int64, so if performance matters use an Int64 column.
2980
+ # @param period [Object]
2981
+ # Length of the window - must be non-negative.
2982
+ # @param offset [Object]
2983
+ # Offset of the window. Default is `-period`.
2984
+ # @param closed ['right', 'left', 'both', 'none']
2985
+ # Define which sides of the temporal interval are closed (inclusive).
2986
+ #
2987
+ # @return [Expr]
2988
+ #
2989
+ # @example
2990
+ # dates = [
2991
+ # "2020-01-01 13:45:48",
2992
+ # "2020-01-01 16:42:13",
2993
+ # "2020-01-01 16:45:09",
2994
+ # "2020-01-02 18:12:48",
2995
+ # "2020-01-03 19:45:32",
2996
+ # "2020-01-08 23:16:43"
2997
+ # ]
2998
+ # df = Polars::DataFrame.new({"dt" => dates, "a": [3, 7, 5, 9, 2, 1]}).with_columns(
2999
+ # Polars.col("dt").str.strptime(Polars::Datetime).set_sorted
3000
+ # )
3001
+ # df.with_columns(
3002
+ # sum_a: Polars.sum("a").rolling(index_column: "dt", period: "2d"),
3003
+ # min_a: Polars.min("a").rolling(index_column: "dt", period: "2d"),
3004
+ # max_a: Polars.max("a").rolling(index_column: "dt", period: "2d")
3005
+ # )
3006
+ # # =>
3007
+ # # shape: (6, 5)
3008
+ # # ┌─────────────────────┬─────┬───────┬───────┬───────┐
3009
+ # # │ dt ┆ a ┆ sum_a ┆ min_a ┆ max_a │
3010
+ # # │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
3011
+ # # │ datetime[μs] ┆ i64 ┆ i64 ┆ i64 ┆ i64 │
3012
+ # # ╞═════════════════════╪═════╪═══════╪═══════╪═══════╡
3013
+ # # │ 2020-01-01 13:45:48 ┆ 3 ┆ 3 ┆ 3 ┆ 3 │
3014
+ # # │ 2020-01-01 16:42:13 ┆ 7 ┆ 10 ┆ 3 ┆ 7 │
3015
+ # # │ 2020-01-01 16:45:09 ┆ 5 ┆ 15 ┆ 3 ┆ 7 │
3016
+ # # │ 2020-01-02 18:12:48 ┆ 9 ┆ 24 ┆ 3 ┆ 9 │
3017
+ # # │ 2020-01-03 19:45:32 ┆ 2 ┆ 11 ┆ 2 ┆ 9 │
3018
+ # # │ 2020-01-08 23:16:43 ┆ 1 ┆ 1 ┆ 1 ┆ 1 │
3019
+ # # └─────────────────────┴─────┴───────┴───────┴───────┘
3020
+ def rolling(
3021
+ index_column:,
3022
+ period:,
3023
+ offset: nil,
3024
+ closed: "right"
3025
+ )
3026
+ index_column_rbexpr = Utils.parse_into_expression(index_column)
3027
+ if offset.nil?
3028
+ offset = Utils.negate_duration_string(Utils.parse_as_duration_string(period))
3029
+ end
3030
+
3031
+ period = Utils.parse_as_duration_string(period)
3032
+ offset = Utils.parse_as_duration_string(offset)
3033
+
3034
+ wrap_expr(_rbexpr.rolling(index_column_rbexpr, period, offset, closed))
2387
3035
  end
2388
3036
 
2389
3037
  # Get mask of unique values.
@@ -2405,7 +3053,7 @@ module Polars
2405
3053
  # # │ true │
2406
3054
  # # └───────┘
2407
3055
  def is_unique
2408
- _from_rbexpr(_rbexpr.is_unique)
3056
+ wrap_expr(_rbexpr.is_unique)
2409
3057
  end
2410
3058
 
2411
3059
  # Get a mask of the first unique value.
@@ -2418,7 +3066,7 @@ module Polars
2418
3066
  # "num" => [1, 2, 3, 1, 5]
2419
3067
  # }
2420
3068
  # )
2421
- # df.with_column(Polars.col("num").is_first.alias("is_first"))
3069
+ # df.with_columns(Polars.col("num").is_first_distinct.alias("is_first"))
2422
3070
  # # =>
2423
3071
  # # shape: (5, 2)
2424
3072
  # # ┌─────┬──────────┐
@@ -2433,9 +3081,32 @@ module Polars
2433
3081
  # # │ 5 ┆ true │
2434
3082
  # # └─────┴──────────┘
2435
3083
  def is_first_distinct
2436
- _from_rbexpr(_rbexpr.is_first_distinct)
3084
+ wrap_expr(_rbexpr.is_first_distinct)
3085
+ end
3086
+
3087
+ # Return a boolean mask indicating the last occurrence of each distinct value.
3088
+ #
3089
+ # @return [Expr]
3090
+ #
3091
+ # @example
3092
+ # df = Polars::DataFrame.new({"a" => [1, 1, 2, 3, 2]})
3093
+ # df.with_columns(Polars.col("a").is_last_distinct.alias("last"))
3094
+ # # =>
3095
+ # # shape: (5, 2)
3096
+ # # ┌─────┬───────┐
3097
+ # # │ a ┆ last │
3098
+ # # │ --- ┆ --- │
3099
+ # # │ i64 ┆ bool │
3100
+ # # ╞═════╪═══════╡
3101
+ # # │ 1 ┆ false │
3102
+ # # │ 1 ┆ true │
3103
+ # # │ 2 ┆ false │
3104
+ # # │ 3 ┆ true │
3105
+ # # │ 2 ┆ true │
3106
+ # # └─────┴───────┘
3107
+ def is_last_distinct
3108
+ wrap_expr(_rbexpr.is_last_distinct)
2437
3109
  end
2438
- alias_method :is_first, :is_first_distinct
2439
3110
 
2440
3111
  # Get mask of duplicated values.
2441
3112
  #
@@ -2456,7 +3127,7 @@ module Polars
2456
3127
  # # │ false │
2457
3128
  # # └───────┘
2458
3129
  def is_duplicated
2459
- _from_rbexpr(_rbexpr.is_duplicated)
3130
+ wrap_expr(_rbexpr.is_duplicated)
2460
3131
  end
2461
3132
 
2462
3133
  # Get a boolean mask of the local maximum peaks.
@@ -2480,7 +3151,7 @@ module Polars
2480
3151
  # # │ true │
2481
3152
  # # └───────┘
2482
3153
  def peak_max
2483
- _from_rbexpr(_rbexpr.peak_max)
3154
+ wrap_expr(_rbexpr.peak_max)
2484
3155
  end
2485
3156
 
2486
3157
  # Get a boolean mask of the local minimum peaks.
@@ -2504,7 +3175,7 @@ module Polars
2504
3175
  # # │ false │
2505
3176
  # # └───────┘
2506
3177
  def peak_min
2507
- _from_rbexpr(_rbexpr.peak_min)
3178
+ wrap_expr(_rbexpr.peak_min)
2508
3179
  end
2509
3180
 
2510
3181
  # Get quantile value.
@@ -2578,7 +3249,7 @@ module Polars
2578
3249
  # # └─────┘
2579
3250
  def quantile(quantile, interpolation: "nearest")
2580
3251
  quantile = Utils.parse_into_expression(quantile, str_as_lit: false)
2581
- _from_rbexpr(_rbexpr.quantile(quantile, interpolation))
3252
+ wrap_expr(_rbexpr.quantile(quantile, interpolation))
2582
3253
  end
2583
3254
 
2584
3255
  # Bin continuous values into discrete categories.
@@ -2604,17 +3275,17 @@ module Polars
2604
3275
  # )
2605
3276
  # # =>
2606
3277
  # # shape: (5, 2)
2607
- # # ┌─────┬─────┐
2608
- # # │ foo ┆ cut
2609
- # # │ --- ┆ ---
2610
- # # │ i64 ┆ cat
2611
- # # ╞═════╪═════╡
2612
- # # │ -2 ┆ a
2613
- # # │ -1 ┆ a
2614
- # # │ 0 ┆ b
2615
- # # │ 1 ┆ b
2616
- # # │ 2 ┆ c
2617
- # # └─────┴─────┘
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
+ # # └─────┴──────┘
2618
3289
  #
2619
3290
  # @example Add both the category and the breakpoint.
2620
3291
  # df.with_columns(
@@ -2625,7 +3296,7 @@ module Polars
2625
3296
  # # ┌─────┬────────────┬────────────┐
2626
3297
  # # │ foo ┆ breakpoint ┆ category │
2627
3298
  # # │ --- ┆ --- ┆ --- │
2628
- # # │ i64 ┆ f64 ┆ cat
3299
+ # # │ i64 ┆ f64 ┆ enum
2629
3300
  # # ╞═════╪════════════╪════════════╡
2630
3301
  # # │ -2 ┆ -1.0 ┆ (-inf, -1] │
2631
3302
  # # │ -1 ┆ -1.0 ┆ (-inf, -1] │
@@ -2634,7 +3305,7 @@ module Polars
2634
3305
  # # │ 2 ┆ inf ┆ (1, inf] │
2635
3306
  # # └─────┴────────────┴────────────┘
2636
3307
  def cut(breaks, labels: nil, left_closed: false, include_breaks: false)
2637
- _from_rbexpr(_rbexpr.cut(breaks, labels, left_closed, include_breaks))
3308
+ wrap_expr(_rbexpr.cut(breaks, labels, left_closed, include_breaks))
2638
3309
  end
2639
3310
 
2640
3311
  # Bin continuous values into discrete categories based on their quantiles.
@@ -2725,7 +3396,7 @@ module Polars
2725
3396
  )
2726
3397
  end
2727
3398
 
2728
- _from_rbexpr(rbexpr)
3399
+ wrap_expr(rbexpr)
2729
3400
  end
2730
3401
 
2731
3402
  # Get the lengths of runs of identical values.
@@ -2750,7 +3421,7 @@ module Polars
2750
3421
  # # │ 2 ┆ 3 │
2751
3422
  # # └─────┴───────┘
2752
3423
  def rle
2753
- _from_rbexpr(_rbexpr.rle)
3424
+ wrap_expr(_rbexpr.rle)
2754
3425
  end
2755
3426
 
2756
3427
  # Map values to run IDs.
@@ -2778,7 +3449,7 @@ module Polars
2778
3449
  # # │ 1 ┆ y ┆ 2 ┆ 3 │
2779
3450
  # # └─────┴──────┴─────┴──────┘
2780
3451
  def rle_id
2781
- _from_rbexpr(_rbexpr.rle_id)
3452
+ wrap_expr(_rbexpr.rle_id)
2782
3453
  end
2783
3454
 
2784
3455
  # Filter a single column.
@@ -2786,8 +3457,12 @@ module Polars
2786
3457
  # Mostly useful in an aggregation context. If you want to filter on a DataFrame
2787
3458
  # level, use `LazyFrame#filter`.
2788
3459
  #
2789
- # @param predicate [Expr]
2790
- # Boolean expression.
3460
+ # @param predicates [Array]
3461
+ # Expression(s) that evaluates to a boolean Series.
3462
+ # @param constraints [Hash]
3463
+ # Column filters; use `name = value` to filter columns by the supplied value.
3464
+ # Each constraint will behave the same as `Polars.col(name).eq(value)`, and
3465
+ # be implicitly joined with the other filter conditions using `&`.
2791
3466
  #
2792
3467
  # @return [Expr]
2793
3468
  #
@@ -2816,64 +3491,25 @@ module Polars
2816
3491
  # # │ g1 ┆ 1 ┆ 2 │
2817
3492
  # # │ g2 ┆ 0 ┆ 3 │
2818
3493
  # # └───────────┴─────┴─────┘
2819
- def filter(predicate)
2820
- _from_rbexpr(_rbexpr.filter(predicate._rbexpr))
2821
- end
2822
-
2823
- # Filter a single column.
2824
- #
2825
- # Alias for {#filter}.
2826
- #
2827
- # @param predicate [Expr]
2828
- # Boolean expression.
2829
- #
2830
- # @return [Expr]
2831
- #
2832
- # @example
2833
- # df = Polars::DataFrame.new(
2834
- # {
2835
- # "group_col" => ["g1", "g1", "g2"],
2836
- # "b" => [1, 2, 3]
2837
- # }
2838
- # )
2839
- # (
2840
- # df.group_by("group_col").agg(
2841
- # [
2842
- # Polars.col("b").where(Polars.col("b") < 2).sum.alias("lt"),
2843
- # Polars.col("b").where(Polars.col("b") >= 2).sum.alias("gte")
2844
- # ]
2845
- # )
2846
- # ).sort("group_col")
2847
- # # =>
2848
- # # shape: (2, 3)
2849
- # # ┌───────────┬─────┬─────┐
2850
- # # │ group_col ┆ lt ┆ gte │
2851
- # # │ --- ┆ --- ┆ --- │
2852
- # # │ str ┆ i64 ┆ i64 │
2853
- # # ╞═══════════╪═════╪═════╡
2854
- # # │ g1 ┆ 1 ┆ 2 │
2855
- # # │ g2 ┆ 0 ┆ 3 │
2856
- # # └───────────┴─────┴─────┘
2857
- def where(predicate)
2858
- filter(predicate)
3494
+ def filter(*predicates, **constraints)
3495
+ predicate = Utils.parse_predicates_constraints_into_expression(
3496
+ *predicates, **constraints
3497
+ )
3498
+ wrap_expr(_rbexpr.filter(predicate))
2859
3499
  end
2860
3500
 
2861
- # Apply a custom Ruby function to a Series or sequence of Series.
3501
+ # Apply a custom Ruby function to a Series or array of Series.
2862
3502
  #
2863
- # The output of this custom function must be a Series.
2864
- # If you want to apply a custom function elementwise over single values, see
2865
- # {#apply}. A use case for `map` is when you want to transform an
2866
- # expression with a third-party library.
2867
- #
2868
- # Read more in [the book](https://pola-rs.github.io/polars-book/user-guide/dsl/custom_functions.html).
2869
- #
2870
- # @param return_dtype [Symbol]
3503
+ # @param return_dtype [Object]
2871
3504
  # Dtype of the output Series.
2872
- # @param agg_list [Boolean]
2873
- # Aggregate list.
2874
3505
  # @param is_elementwise [Boolean]
2875
3506
  # If set to true this can run in the streaming engine, but may yield
2876
3507
  # incorrect results in group-by. Ensure you know what you are doing!
3508
+ # @param returns_scalar [Boolean]
3509
+ # If the function returns a scalar, by default it will be wrapped in
3510
+ # a list in the output, since the assumption is that the function
3511
+ # always returns something Series-like. If you want to keep the
3512
+ # result as a scalar, set this argument to True.
2877
3513
  #
2878
3514
  # @return [Expr]
2879
3515
  #
@@ -2884,7 +3520,7 @@ module Polars
2884
3520
  # "cosine" => [1.0, 0.0, -1.0, 0.0]
2885
3521
  # }
2886
3522
  # )
2887
- # df.select(Polars.all.map { |x| x.to_numpy.argmax })
3523
+ # df.select(Polars.all.map_batches(returns_scalar: true) { |x| x.to_numo.argmax })
2888
3524
  # # =>
2889
3525
  # # shape: (1, 2)
2890
3526
  # # ┌──────┬────────┐
@@ -2894,22 +3530,24 @@ module Polars
2894
3530
  # # ╞══════╪════════╡
2895
3531
  # # │ 1 ┆ 0 │
2896
3532
  # # └──────┴────────┘
2897
- # def map_batches(return_dtype: nil, agg_list: false, is_elementwise: false, returns_scalar: false, &f)
2898
- # if !return_dtype.nil?
2899
- # return_dtype = Utils.rb_type_to_dtype(return_dtype)
2900
- # end
2901
- # _from_rbexpr(
2902
- # _rbexpr.map_batches(
2903
- # # TODO _map_batches_wrapper
2904
- # f,
2905
- # return_dtype,
2906
- # agg_list,
2907
- # is_elementwise,
2908
- # returns_scalar
2909
- # )
2910
- # )
2911
- # end
2912
- # alias_method :map, :map_batches
3533
+ def map_batches(
3534
+ return_dtype: nil,
3535
+ is_elementwise: false,
3536
+ returns_scalar: false,
3537
+ &function
3538
+ )
3539
+ _wrap = lambda do |sl, *args, **kwargs|
3540
+ function.(sl[0], *args, **kwargs)
3541
+ end
3542
+
3543
+ F.map_batches(
3544
+ [self],
3545
+ return_dtype: return_dtype,
3546
+ is_elementwise: is_elementwise,
3547
+ returns_scalar: returns_scalar,
3548
+ &_wrap
3549
+ )
3550
+ end
2913
3551
 
2914
3552
  # Apply a custom/user-defined function (UDF) in a GroupBy or Projection context.
2915
3553
  #
@@ -2934,24 +3572,24 @@ module Polars
2934
3572
  # Wherever possible you should strongly prefer the native expression API
2935
3573
  # to achieve the best performance.
2936
3574
  #
2937
- # @param return_dtype [Symbol]
3575
+ # @param return_dtype [Object]
2938
3576
  # Dtype of the output Series.
2939
3577
  # If not set, polars will assume that
2940
3578
  # the dtype remains unchanged.
2941
3579
  #
2942
3580
  # @return [Expr]
2943
3581
  #
2944
- # @example
3582
+ # @example The function is applied to each element of column `'a'`:
2945
3583
  # df = Polars::DataFrame.new(
2946
3584
  # {
2947
3585
  # "a" => [1, 2, 3, 1],
2948
3586
  # "b" => ["a", "b", "c", "c"]
2949
3587
  # }
2950
3588
  # )
2951
- #
2952
- # @example In a selection context, the function is applied by row.
2953
- # df.with_column(
2954
- # Polars.col("a").map_elements { |x| x * 2 }.alias("a_times_2")
3589
+ # df.with_columns(
3590
+ # Polars.col("a")
3591
+ # .map_elements(return_dtype: Polars.self_dtype) { |x| x * 2 }
3592
+ # .alias("a_times_2")
2955
3593
  # )
2956
3594
  # # =>
2957
3595
  # # shape: (4, 3)
@@ -2965,44 +3603,37 @@ module Polars
2965
3603
  # # │ 3 ┆ c ┆ 6 │
2966
3604
  # # │ 1 ┆ c ┆ 2 │
2967
3605
  # # └─────┴─────┴───────────┘
2968
- #
2969
- # @example In a GroupBy context the function is applied by group:
2970
- # df.lazy
2971
- # .group_by("b", maintain_order: true)
2972
- # .agg(
2973
- # [
2974
- # Polars.col("a").map_elements { |x| x.sum }
2975
- # ]
2976
- # )
2977
- # .collect
2978
- # # =>
2979
- # # shape: (3, 2)
2980
- # # ┌─────┬─────┐
2981
- # # b ┆ a │
2982
- # # │ --- ┆ --- │
2983
- # # │ str ┆ i64 │
2984
- # # ╞═════╪═════╡
2985
- # # a ┆ 1 │
2986
- # # │ b ┆ 2 │
2987
- # # │ c ┆ 4 │
2988
- # # └─────┴─────┘
2989
- # def map_elements(
2990
- # return_dtype: nil,
2991
- # skip_nulls: true,
2992
- # pass_name: false,
2993
- # strategy: "thread_local",
2994
- # &f
2995
- # )
2996
- # if pass_name
2997
- # raise Todo
2998
- # else
2999
- # wrap_f = lambda do |x|
3000
- # x.map_elements(return_dtype: return_dtype, skip_nulls: skip_nulls, &f)
3001
- # end
3002
- # end
3003
- # map_batches(agg_list: true, return_dtype: return_dtype, &wrap_f)
3004
- # end
3005
- # alias_method :apply, :map_elements
3606
+ def map_elements(
3607
+ return_dtype: nil,
3608
+ skip_nulls: true,
3609
+ pass_name: false,
3610
+ strategy: "thread_local",
3611
+ returns_scalar: false,
3612
+ &function
3613
+ )
3614
+ if pass_name
3615
+ raise Todo
3616
+ else
3617
+ wrap_f = lambda do |x, **kwargs|
3618
+ return_dtype = kwargs[:return_dtype]
3619
+ x.map_elements(return_dtype: return_dtype, skip_nulls: skip_nulls, &function)
3620
+ end
3621
+ end
3622
+
3623
+ if strategy == "thread_local"
3624
+ map_batches(
3625
+ return_dtype: return_dtype,
3626
+ returns_scalar: false,
3627
+ is_elementwise: true,
3628
+ &wrap_f
3629
+ )
3630
+ elsif strategy == "threading"
3631
+ raise Todo
3632
+ else
3633
+ msg = "strategy #{strategy.inspect} is not supported"
3634
+ raise ArgumentError, msg
3635
+ end
3636
+ end
3006
3637
 
3007
3638
  # Explode a list or utf8 Series. This means that every item is expanded to a new
3008
3639
  # row.
@@ -3011,6 +3642,11 @@ module Polars
3011
3642
  #
3012
3643
  # @return [Expr]
3013
3644
  #
3645
+ # @deprecated
3646
+ # `Expr#flatten` is deprecated and will be removed in a future version.
3647
+ # Use `Expr.list.explode(keep_nulls: false, empty_as_null: false)` instead,
3648
+ # which provides the behavior you likely expect.
3649
+ #
3014
3650
  # @example
3015
3651
  # df = Polars::DataFrame.new(
3016
3652
  # {
@@ -3030,7 +3666,7 @@ module Polars
3030
3666
  # # │ b ┆ [2, 3, 4] │
3031
3667
  # # └───────┴───────────┘
3032
3668
  def flatten
3033
- _from_rbexpr(_rbexpr.explode)
3669
+ explode(empty_as_null: true, keep_nulls: true)
3034
3670
  end
3035
3671
 
3036
3672
  # Explode a list or utf8 Series.
@@ -3056,8 +3692,8 @@ module Polars
3056
3692
  # # │ 5 │
3057
3693
  # # │ 6 │
3058
3694
  # # └─────┘
3059
- def explode
3060
- _from_rbexpr(_rbexpr.explode)
3695
+ def explode(empty_as_null: true, keep_nulls: true)
3696
+ wrap_expr(_rbexpr.explode(empty_as_null, keep_nulls))
3061
3697
  end
3062
3698
 
3063
3699
  # Take every nth value in the Series and return as a new Series.
@@ -3079,9 +3715,8 @@ module Polars
3079
3715
  # # │ 7 │
3080
3716
  # # └─────┘
3081
3717
  def gather_every(n, offset = 0)
3082
- _from_rbexpr(_rbexpr.gather_every(n, offset))
3718
+ wrap_expr(_rbexpr.gather_every(n, offset))
3083
3719
  end
3084
- alias_method :take_every, :gather_every
3085
3720
 
3086
3721
  # Get the first `n` rows.
3087
3722
  #
@@ -3105,7 +3740,7 @@ module Polars
3105
3740
  # # │ 3 │
3106
3741
  # # └─────┘
3107
3742
  def head(n = 10)
3108
- _from_rbexpr(_rbexpr.head(n))
3743
+ wrap_expr(_rbexpr.head(n))
3109
3744
  end
3110
3745
 
3111
3746
  # Get the last `n` rows.
@@ -3130,7 +3765,7 @@ module Polars
3130
3765
  # # │ 7 │
3131
3766
  # # └─────┘
3132
3767
  def tail(n = 10)
3133
- _from_rbexpr(_rbexpr.tail(n))
3768
+ wrap_expr(_rbexpr.tail(n))
3134
3769
  end
3135
3770
 
3136
3771
  # Get the first `n` rows.
@@ -3160,39 +3795,122 @@ module Polars
3160
3795
  head(n)
3161
3796
  end
3162
3797
 
3163
- # Method equivalent of equality operator `expr == other`.
3798
+ # Method equivalent of bitwise "and" operator `expr & other & ...`.
3164
3799
  #
3165
- # @param other [Object]
3166
- # A literal or expression value to compare with.
3800
+ # @param others [Array]
3801
+ # One or more integer or boolean expressions to evaluate/combine.
3167
3802
  #
3168
3803
  # @return [Expr]
3804
+ #
3169
3805
  # @example
3170
3806
  # df = Polars::DataFrame.new(
3171
3807
  # {
3172
- # "x" => [1.0, 2.0, Float::NAN, 4.0],
3173
- # "y" => [2.0, 2.0, Float::NAN, 4.0]
3808
+ # "x" => [5, 6, 7, 4, 8],
3809
+ # "y" => [1.5, 2.5, 1.0, 4.0, -5.75],
3810
+ # "z" => [-9, 2, -1, 4, 8]
3174
3811
  # }
3175
3812
  # )
3176
- # df.with_columns(
3177
- # Polars.col("x").eq(Polars.col("y")).alias("x == y")
3813
+ # df.select(
3814
+ # (Polars.col("x") >= Polars.col("z"))
3815
+ # .and_(
3816
+ # Polars.col("y") >= Polars.col("z"),
3817
+ # Polars.col("y") == Polars.col("y"),
3818
+ # Polars.col("z") <= Polars.col("x"),
3819
+ # Polars.col("y") != Polars.col("x"),
3820
+ # )
3821
+ # .alias("all")
3178
3822
  # )
3179
3823
  # # =>
3180
- # # shape: (4, 3)
3181
- # # ┌─────┬─────┬────────┐
3182
- # # │ x ┆ y ┆ x == y
3183
- # # │ --- ┆ --- ┆ ---
3184
- # # │ f64 ┆ f64 ┆ bool
3185
- # # ╞═════╪═════╪════════╡
3186
- # # │ 1.0 ┆ 2.0 ┆ false
3187
- # # │ 2.0 ┆ 2.0 ┆ true
3188
- # # │ NaN ┆ NaN ┆ true
3189
- # # │ 4.0 ┆ 4.0 ┆ true
3190
- # # └─────┴─────┴────────┘
3824
+ # # shape: (5, 1)
3825
+ # # ┌───────┐
3826
+ # # │ all
3827
+ # # │ ---
3828
+ # # │ bool
3829
+ # # ╞═══════╡
3830
+ # # │ true
3831
+ # # │ true
3832
+ # # │ true
3833
+ # # │ false
3834
+ # # │ false │
3835
+ # # └───────┘
3836
+ def and_(*others)
3837
+ ([self] + others).reduce(:&)
3838
+ end
3839
+
3840
+ # Method equivalent of bitwise "or" operator `expr | other | ...`.
3841
+ #
3842
+ # @param others [Array]
3843
+ # One or more integer or boolean expressions to evaluate/combine.
3844
+ #
3845
+ # @return [Expr]
3846
+ #
3847
+ # @example
3848
+ # df = Polars::DataFrame.new(
3849
+ # {
3850
+ # "x" => [5, 6, 7, 4, 8],
3851
+ # "y" => [1.5, 2.5, 1.0, 4.0, -5.75],
3852
+ # "z" => [-9, 2, -1, 4, 8]
3853
+ # }
3854
+ # )
3855
+ # df.select(
3856
+ # (Polars.col("x") == Polars.col("y"))
3857
+ # .or_(
3858
+ # Polars.col("x") == Polars.col("y"),
3859
+ # Polars.col("y") == Polars.col("z"),
3860
+ # Polars.col("y").cast(Integer) == Polars.col("z"),
3861
+ # )
3862
+ # .alias("any")
3863
+ # )
3864
+ # # =>
3865
+ # # shape: (5, 1)
3866
+ # # ┌───────┐
3867
+ # # │ any │
3868
+ # # │ --- │
3869
+ # # │ bool │
3870
+ # # ╞═══════╡
3871
+ # # │ false │
3872
+ # # │ true │
3873
+ # # │ false │
3874
+ # # │ true │
3875
+ # # │ false │
3876
+ # # └───────┘
3877
+ def or_(*others)
3878
+ ([self] + others).reduce(:|)
3879
+ end
3880
+
3881
+ # Method equivalent of equality operator `expr == other`.
3882
+ #
3883
+ # @param other [Object]
3884
+ # A literal or expression value to compare with.
3885
+ #
3886
+ # @return [Expr]
3887
+ # @example
3888
+ # df = Polars::DataFrame.new(
3889
+ # {
3890
+ # "x" => [1.0, 2.0, Float::NAN, 4.0],
3891
+ # "y" => [2.0, 2.0, Float::NAN, 4.0]
3892
+ # }
3893
+ # )
3894
+ # df.with_columns(
3895
+ # Polars.col("x").eq(Polars.col("y")).alias("x == y")
3896
+ # )
3897
+ # # =>
3898
+ # # shape: (4, 3)
3899
+ # # ┌─────┬─────┬────────┐
3900
+ # # │ x ┆ y ┆ x == y │
3901
+ # # │ --- ┆ --- ┆ --- │
3902
+ # # │ f64 ┆ f64 ┆ bool │
3903
+ # # ╞═════╪═════╪════════╡
3904
+ # # │ 1.0 ┆ 2.0 ┆ false │
3905
+ # # │ 2.0 ┆ 2.0 ┆ true │
3906
+ # # │ NaN ┆ NaN ┆ true │
3907
+ # # │ 4.0 ┆ 4.0 ┆ true │
3908
+ # # └─────┴─────┴────────┘
3191
3909
  def eq(other)
3192
3910
  self == other
3193
3911
  end
3194
3912
 
3195
- # Method equivalent of equality operator `expr == other` where `None == None`.
3913
+ # Method equivalent of equality operator `expr == other` where `nil == nil`.
3196
3914
  #
3197
3915
  # This differs from default `eq` where null values are propagated.
3198
3916
  #
@@ -3228,7 +3946,7 @@ module Polars
3228
3946
  # # └──────┴──────┴────────┴────────────────┘
3229
3947
  def eq_missing(other)
3230
3948
  other = Utils.parse_into_expression(other, str_as_lit: true)
3231
- _from_rbexpr(_rbexpr.eq_missing(other))
3949
+ wrap_expr(_rbexpr.eq_missing(other))
3232
3950
  end
3233
3951
 
3234
3952
  # Method equivalent of "greater than or equal" operator `expr >= other`.
@@ -3396,7 +4114,7 @@ module Polars
3396
4114
  self != other
3397
4115
  end
3398
4116
 
3399
- # Method equivalent of equality operator `expr != other` where `None == None`.
4117
+ # Method equivalent of equality operator `expr != other` where `nil == nil`.
3400
4118
  #
3401
4119
  # This differs from default `ne` where null values are propagated.
3402
4120
  #
@@ -3432,7 +4150,7 @@ module Polars
3432
4150
  # # └──────┴──────┴────────┴────────────────┘
3433
4151
  def ne_missing(other)
3434
4152
  other = Utils.parse_into_expression(other, str_as_lit: true)
3435
- _from_rbexpr(_rbexpr.neq_missing(other))
4153
+ wrap_expr(_rbexpr.neq_missing(other))
3436
4154
  end
3437
4155
 
3438
4156
  # Method equivalent of addition operator `expr + other`.
@@ -3509,7 +4227,7 @@ module Polars
3509
4227
  # # │ 5 ┆ 2.5 ┆ 2 │
3510
4228
  # # └─────┴─────┴──────┘
3511
4229
  def floordiv(other)
3512
- _from_rbexpr(_rbexpr.floordiv(_to_rbexpr(other)))
4230
+ wrap_expr(_rbexpr.floordiv(_to_rbexpr(other)))
3513
4231
  end
3514
4232
 
3515
4233
  # Method equivalent of modulus operator `expr % other`.
@@ -3711,7 +4429,9 @@ module Polars
3711
4429
  # Check if elements of this expression are present in the other Series.
3712
4430
  #
3713
4431
  # @param other [Object]
3714
- # Series or sequence of primitive type.
4432
+ # Series or array of primitive type.
4433
+ # @param nulls_equal [Boolean]
4434
+ # If true, treat null as a distinct value. Null values will not propagate.
3715
4435
  #
3716
4436
  # @return [Expr]
3717
4437
  #
@@ -3719,29 +4439,21 @@ module Polars
3719
4439
  # df = Polars::DataFrame.new(
3720
4440
  # {"sets" => [[1, 2, 3], [1, 2], [9, 10]], "optional_members" => [1, 2, 3]}
3721
4441
  # )
3722
- # df.select([Polars.col("optional_members").is_in("sets").alias("contains")])
4442
+ # df.with_columns(contains: Polars.col("optional_members").is_in("sets"))
3723
4443
  # # =>
3724
- # # shape: (3, 1)
3725
- # # ┌──────────┐
3726
- # # │ contains │
3727
- # # │ --- │
3728
- # # │ bool │
3729
- # # ╞══════════╡
3730
- # # │ true │
3731
- # # │ true │
3732
- # # │ false │
3733
- # # └──────────┘
3734
- def is_in(other)
3735
- if other.is_a?(::Array)
3736
- if other.length == 0
3737
- other = Polars.lit(nil)._rbexpr
3738
- else
3739
- other = Polars.lit(Series.new(other))._rbexpr
3740
- end
3741
- else
3742
- other = Utils.parse_into_expression(other, str_as_lit: false)
3743
- end
3744
- _from_rbexpr(_rbexpr.is_in(other))
4444
+ # # shape: (3, 3)
4445
+ # # ┌───────────┬──────────────────┬──────────┐
4446
+ # # │ sets ┆ optional_members ┆ contains │
4447
+ # # │ --- ┆ --- ┆ ---
4448
+ # # │ list[i64] ┆ i64 ┆ bool │
4449
+ # # ╞═══════════╪══════════════════╪══════════╡
4450
+ # # │ [1, 2, 3] ┆ 1 ┆ true │
4451
+ # # │ [1, 2] ┆ 2 ┆ true │
4452
+ # # │ [9, 10] ┆ 3 ┆ false │
4453
+ # # └───────────┴──────────────────┴──────────┘
4454
+ def is_in(other, nulls_equal: false)
4455
+ other = Utils.parse_into_expression(other)
4456
+ wrap_expr(_rbexpr.is_in(other, nulls_equal))
3745
4457
  end
3746
4458
  alias_method :in?, :is_in
3747
4459
 
@@ -3777,7 +4489,7 @@ module Polars
3777
4489
  # # └─────────────────┘
3778
4490
  def repeat_by(by)
3779
4491
  by = Utils.parse_into_expression(by, str_as_lit: false)
3780
- _from_rbexpr(_rbexpr.repeat_by(by))
4492
+ wrap_expr(_rbexpr.repeat_by(by))
3781
4493
  end
3782
4494
 
3783
4495
  # Check if this expression is between start and end.
@@ -3850,14 +4562,51 @@ module Polars
3850
4562
  lower_bound = Utils.parse_into_expression(lower_bound)
3851
4563
  upper_bound = Utils.parse_into_expression(upper_bound)
3852
4564
 
3853
- _from_rbexpr(
4565
+ wrap_expr(
3854
4566
  _rbexpr.is_between(lower_bound, upper_bound, closed)
3855
4567
  )
3856
4568
  end
3857
4569
 
4570
+ # Check if this expression is close, i.e. almost equal, to the other expression.
4571
+ #
4572
+ # @param abs_tol [Float]
4573
+ # Absolute tolerance. This is the maximum allowed absolute difference between
4574
+ # two values. Must be non-negative.
4575
+ # @param rel_tol [Float]
4576
+ # Relative tolerance. This is the maximum allowed difference between two
4577
+ # values, relative to the larger absolute value. Must be in the range [0, 1).
4578
+ # @param nans_equal [Boolean]
4579
+ # Whether NaN values should be considered equal.
4580
+ #
4581
+ # @return [Expr]
4582
+ #
4583
+ # @example
4584
+ # df = Polars::DataFrame.new({"a" => [1.5, 2.0, 2.5], "b" => [1.55, 2.2, 3.0]})
4585
+ # df.with_columns(Polars.col("a").is_close("b", abs_tol: 0.1).alias("is_close"))
4586
+ # # =>
4587
+ # # shape: (3, 3)
4588
+ # # ┌─────┬──────┬──────────┐
4589
+ # # │ a ┆ b ┆ is_close │
4590
+ # # │ --- ┆ --- ┆ --- │
4591
+ # # │ f64 ┆ f64 ┆ bool │
4592
+ # # ╞═════╪══════╪══════════╡
4593
+ # # │ 1.5 ┆ 1.55 ┆ true │
4594
+ # # │ 2.0 ┆ 2.2 ┆ false │
4595
+ # # │ 2.5 ┆ 3.0 ┆ false │
4596
+ # # └─────┴──────┴──────────┘
4597
+ def is_close(
4598
+ other,
4599
+ abs_tol: 0.0,
4600
+ rel_tol: 1.0e-09,
4601
+ nans_equal: false
4602
+ )
4603
+ other = Utils.parse_into_expression(other)
4604
+ wrap_expr(_rbexpr.is_close(other, abs_tol, rel_tol, nans_equal))
4605
+ end
4606
+
3858
4607
  # Hash the elements in the selection.
3859
4608
  #
3860
- # The hash value is of type `:u64`.
4609
+ # The hash value is of type `UInt64`.
3861
4610
  #
3862
4611
  # @param seed [Integer]
3863
4612
  # Random seed parameter. Defaults to 0.
@@ -3877,7 +4626,7 @@ module Polars
3877
4626
  # "b" => ["x", nil, "z"]
3878
4627
  # }
3879
4628
  # )
3880
- # df.with_column(Polars.all._hash(10, 20, 30, 40))
4629
+ # df.with_columns(Polars.all.hash_(10, 20, 30, 40))
3881
4630
  # # =>
3882
4631
  # # shape: (3, 2)
3883
4632
  # # ┌──────────────────────┬──────────────────────┐
@@ -3889,30 +4638,35 @@ module Polars
3889
4638
  # # │ 16386608652769605760 ┆ 11638928888656214026 │
3890
4639
  # # │ 11638928888656214026 ┆ 11040941213715918520 │
3891
4640
  # # └──────────────────────┴──────────────────────┘
3892
- def _hash(seed = 0, seed_1 = nil, seed_2 = nil, seed_3 = nil)
4641
+ def hash_(seed = 0, seed_1 = nil, seed_2 = nil, seed_3 = nil)
3893
4642
  k0 = seed
3894
4643
  k1 = seed_1.nil? ? seed : seed_1
3895
4644
  k2 = seed_2.nil? ? seed : seed_2
3896
4645
  k3 = seed_3.nil? ? seed : seed_3
3897
- _from_rbexpr(_rbexpr._hash(k0, k1, k2, k3))
4646
+ wrap_expr(_rbexpr._hash(k0, k1, k2, k3))
3898
4647
  end
3899
4648
 
3900
- # 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.
3901
4653
  #
3902
- # This operation is only allowed for 64bit integers. For lower bits integers,
3903
- # you can safely use that cast operation.
4654
+ # Either `signed` or `dtype` can be specified.
3904
4655
  #
3905
4656
  # @param signed [Boolean]
3906
- # If true, reinterpret as `:i64`. Otherwise, reinterpret as `:u64`.
4657
+ # If true, reinterpret as signed integer. Otherwise, reinterpret
4658
+ # as unsigned integer.
4659
+ # @param dtype [Object]
4660
+ # DataType to reinterpret to.
3907
4661
  #
3908
4662
  # @return [Expr]
3909
4663
  #
3910
4664
  # @example
3911
- # s = Polars::Series.new("a", [1, 1, 2], dtype: :u64)
4665
+ # s = Polars::Series.new("a", [1, 1, 2], dtype: Polars::UInt64)
3912
4666
  # df = Polars::DataFrame.new([s])
3913
4667
  # df.select(
3914
4668
  # [
3915
- # Polars.col("a").reinterpret(signed: true).alias("reinterpreted"),
4669
+ # Polars.col("a").reinterpret(dtype: Polars::Int64).alias("reinterpreted"),
3916
4670
  # Polars.col("a").alias("original")
3917
4671
  # ]
3918
4672
  # )
@@ -3927,8 +4681,13 @@ module Polars
3927
4681
  # # │ 1 ┆ 1 │
3928
4682
  # # │ 2 ┆ 2 │
3929
4683
  # # └───────────────┴──────────┘
3930
- def reinterpret(signed: false)
3931
- _from_rbexpr(_rbexpr.reinterpret(signed))
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
4688
+ end
4689
+
4690
+ wrap_expr(_rbexpr.reinterpret(signed, dtype))
3932
4691
  end
3933
4692
 
3934
4693
  # Print the value that this expression evaluates to and pass on the value.
@@ -3937,14 +4696,14 @@ module Polars
3937
4696
  #
3938
4697
  # @example
3939
4698
  # df = Polars::DataFrame.new({"foo" => [1, 1, 2]})
3940
- # df.select(Polars.col("foo").cumsum._inspect("value is: %s").alias("bar"))
4699
+ # df.select(Polars.col("foo").cum_sum.inspect_("value is: %s").alias("bar"))
3941
4700
  # # =>
3942
4701
  # # value is: shape: (3,)
3943
4702
  # # Series: 'foo' [i64]
3944
4703
  # # [
3945
- # # 1
3946
- # # 2
3947
- # # 4
4704
+ # # 1
4705
+ # # 2
4706
+ # # 4
3948
4707
  # # ]
3949
4708
  # # shape: (3, 1)
3950
4709
  # # ┌─────┐
@@ -3956,14 +4715,14 @@ module Polars
3956
4715
  # # │ 2 │
3957
4716
  # # │ 4 │
3958
4717
  # # └─────┘
3959
- # def _inspect(fmt = "%s")
3960
- # inspect = lambda do |s|
3961
- # puts(fmt % [s])
3962
- # s
3963
- # end
4718
+ def inspect_(fmt = "%s")
4719
+ inspect = lambda do |s|
4720
+ puts(fmt % [s])
4721
+ s
4722
+ end
3964
4723
 
3965
- # map(return_dtype: nil, agg_list: true, &inspect)
3966
- # end
4724
+ map_batches(return_dtype: F.dtype_of(self), &inspect)
4725
+ end
3967
4726
 
3968
4727
  # Fill nulls with linear interpolation over missing values.
3969
4728
  #
@@ -3991,7 +4750,38 @@ module Polars
3991
4750
  # # │ 3.0 ┆ 3.0 │
3992
4751
  # # └─────┴─────┘
3993
4752
  def interpolate(method: "linear")
3994
- _from_rbexpr(_rbexpr.interpolate(method))
4753
+ wrap_expr(_rbexpr.interpolate(method))
4754
+ end
4755
+
4756
+ # Fill null values using interpolation based on another column.
4757
+ #
4758
+ # @param by [Expr] Column to interpolate values based on.
4759
+ #
4760
+ # @return [Expr]
4761
+ #
4762
+ # @example Fill null values using linear interpolation.
4763
+ # df = Polars::DataFrame.new(
4764
+ # {
4765
+ # "a" => [1, nil, nil, 3],
4766
+ # "b" => [1, 2, 7, 8]
4767
+ # }
4768
+ # )
4769
+ # df.with_columns(a_interpolated: Polars.col("a").interpolate_by("b"))
4770
+ # # =>
4771
+ # # shape: (4, 3)
4772
+ # # ┌──────┬─────┬────────────────┐
4773
+ # # │ a ┆ b ┆ a_interpolated │
4774
+ # # │ --- ┆ --- ┆ --- │
4775
+ # # │ i64 ┆ i64 ┆ f64 │
4776
+ # # ╞══════╪═════╪════════════════╡
4777
+ # # │ 1 ┆ 1 ┆ 1.0 │
4778
+ # # │ null ┆ 2 ┆ 1.285714 │
4779
+ # # │ null ┆ 7 ┆ 2.714286 │
4780
+ # # │ 3 ┆ 8 ┆ 3.0 │
4781
+ # # └──────┴─────┴────────────────┘
4782
+ def interpolate_by(by)
4783
+ by = Utils.parse_into_expression(by)
4784
+ wrap_expr(_rbexpr.interpolate_by(by))
3995
4785
  end
3996
4786
 
3997
4787
  # Apply a rolling min based on another column.
@@ -4018,14 +4808,12 @@ module Polars
4018
4808
  # (which may not be 24 hours, due to daylight savings). Similarly for
4019
4809
  # "calendar week", "calendar month", "calendar quarter", and
4020
4810
  # "calendar year".
4021
- # @param min_periods [Integer]
4811
+ # @param min_samples [Integer]
4022
4812
  # The number of values in the window that should be non-null before computing
4023
4813
  # a result.
4024
4814
  # @param closed ['left', 'right', 'both', 'none']
4025
4815
  # Define which sides of the temporal interval are closed (inclusive),
4026
4816
  # defaults to `'right'`.
4027
- # @param warn_if_unsorted [Boolean]
4028
- # Warn if data is not known to be sorted by `by` column.
4029
4817
  #
4030
4818
  # @return [Expr]
4031
4819
  #
@@ -4086,14 +4874,13 @@ module Polars
4086
4874
  def rolling_min_by(
4087
4875
  by,
4088
4876
  window_size,
4089
- min_periods: 1,
4090
- closed: "right",
4091
- warn_if_unsorted: nil
4877
+ min_samples: 1,
4878
+ closed: "right"
4092
4879
  )
4093
4880
  window_size = _prepare_rolling_by_window_args(window_size)
4094
4881
  by = Utils.parse_into_expression(by)
4095
- _from_rbexpr(
4096
- _rbexpr.rolling_min_by(by, window_size, min_periods, closed)
4882
+ wrap_expr(
4883
+ _rbexpr.rolling_min_by(by, window_size, min_samples, closed)
4097
4884
  )
4098
4885
  end
4099
4886
 
@@ -4121,14 +4908,12 @@ module Polars
4121
4908
  # (which may not be 24 hours, due to daylight savings). Similarly for
4122
4909
  # "calendar week", "calendar month", "calendar quarter", and
4123
4910
  # "calendar year".
4124
- # @param min_periods [Integer]
4911
+ # @param min_samples [Integer]
4125
4912
  # The number of values in the window that should be non-null before computing
4126
4913
  # a result.
4127
4914
  # @param closed ['left', 'right', 'both', 'none']
4128
4915
  # Define which sides of the temporal interval are closed (inclusive),
4129
4916
  # defaults to `'right'`.
4130
- # @param warn_if_unsorted [Boolean]
4131
- # Warn if data is not known to be sorted by `by` column.
4132
4917
  #
4133
4918
  # @return [Expr]
4134
4919
  #
@@ -4215,14 +5000,13 @@ module Polars
4215
5000
  def rolling_max_by(
4216
5001
  by,
4217
5002
  window_size,
4218
- min_periods: 1,
4219
- closed: "right",
4220
- warn_if_unsorted: nil
5003
+ min_samples: 1,
5004
+ closed: "right"
4221
5005
  )
4222
5006
  window_size = _prepare_rolling_by_window_args(window_size)
4223
5007
  by = Utils.parse_into_expression(by)
4224
- _from_rbexpr(
4225
- _rbexpr.rolling_max_by(by, window_size, min_periods, closed)
5008
+ wrap_expr(
5009
+ _rbexpr.rolling_max_by(by, window_size, min_samples, closed)
4226
5010
  )
4227
5011
  end
4228
5012
 
@@ -4250,14 +5034,12 @@ module Polars
4250
5034
  # (which may not be 24 hours, due to daylight savings). Similarly for
4251
5035
  # "calendar week", "calendar month", "calendar quarter", and
4252
5036
  # "calendar year".
4253
- # @param min_periods [Integer]
5037
+ # @param min_samples [Integer]
4254
5038
  # The number of values in the window that should be non-null before computing
4255
5039
  # a result.
4256
5040
  # @param closed ['left', 'right', 'both', 'none']
4257
5041
  # Define which sides of the temporal interval are closed (inclusive),
4258
5042
  # defaults to `'right'`.
4259
- # @param warn_if_unsorted [Boolean]
4260
- # Warn if data is not known to be sorted by `by` column.
4261
5043
  #
4262
5044
  # @return [Expr]
4263
5045
  #
@@ -4346,17 +5128,16 @@ module Polars
4346
5128
  def rolling_mean_by(
4347
5129
  by,
4348
5130
  window_size,
4349
- min_periods: 1,
4350
- closed: "right",
4351
- warn_if_unsorted: nil
5131
+ min_samples: 1,
5132
+ closed: "right"
4352
5133
  )
4353
5134
  window_size = _prepare_rolling_by_window_args(window_size)
4354
5135
  by = Utils.parse_into_expression(by)
4355
- _from_rbexpr(
5136
+ wrap_expr(
4356
5137
  _rbexpr.rolling_mean_by(
4357
5138
  by,
4358
5139
  window_size,
4359
- min_periods,
5140
+ min_samples,
4360
5141
  closed
4361
5142
  )
4362
5143
  )
@@ -4386,14 +5167,12 @@ module Polars
4386
5167
  # (which may not be 24 hours, due to daylight savings). Similarly for
4387
5168
  # "calendar week", "calendar month", "calendar quarter", and
4388
5169
  # "calendar year".
4389
- # @param min_periods [Integer]
5170
+ # @param min_samples [Integer]
4390
5171
  # The number of values in the window that should be non-null before computing
4391
5172
  # a result.
4392
5173
  # @param closed ['left', 'right', 'both', 'none']
4393
5174
  # Define which sides of the temporal interval are closed (inclusive),
4394
5175
  # defaults to `'right'`.
4395
- # @param warn_if_unsorted [Boolean]
4396
- # Warn if data is not known to be sorted by `by` column.
4397
5176
  #
4398
5177
  # @return [Expr]
4399
5178
  #
@@ -4480,14 +5259,13 @@ module Polars
4480
5259
  def rolling_sum_by(
4481
5260
  by,
4482
5261
  window_size,
4483
- min_periods: 1,
4484
- closed: "right",
4485
- warn_if_unsorted: nil
5262
+ min_samples: 0,
5263
+ closed: "right"
4486
5264
  )
4487
5265
  window_size = _prepare_rolling_by_window_args(window_size)
4488
5266
  by = Utils.parse_into_expression(by)
4489
- _from_rbexpr(
4490
- _rbexpr.rolling_sum_by(by, window_size, min_periods, closed)
5267
+ wrap_expr(
5268
+ _rbexpr.rolling_sum_by(by, window_size, min_samples, closed)
4491
5269
  )
4492
5270
  end
4493
5271
 
@@ -4515,7 +5293,7 @@ module Polars
4515
5293
  # (which may not be 24 hours, due to daylight savings). Similarly for
4516
5294
  # "calendar week", "calendar month", "calendar quarter", and
4517
5295
  # "calendar year".
4518
- # @param min_periods [Integer]
5296
+ # @param min_samples [Integer]
4519
5297
  # The number of values in the window that should be non-null before computing
4520
5298
  # a result.
4521
5299
  # @param closed ['left', 'right', 'both', 'none']
@@ -4523,8 +5301,6 @@ module Polars
4523
5301
  # defaults to `'right'`.
4524
5302
  # @param ddof [Integer]
4525
5303
  # "Delta Degrees of Freedom": The divisor for a length N window is N - ddof
4526
- # @param warn_if_unsorted [Boolean]
4527
- # Warn if data is not known to be sorted by `by` column.
4528
5304
  #
4529
5305
  # @return [Expr]
4530
5306
  #
@@ -4611,18 +5387,17 @@ module Polars
4611
5387
  def rolling_std_by(
4612
5388
  by,
4613
5389
  window_size,
4614
- min_periods: 1,
5390
+ min_samples: 1,
4615
5391
  closed: "right",
4616
- ddof: 1,
4617
- warn_if_unsorted: nil
5392
+ ddof: 1
4618
5393
  )
4619
5394
  window_size = _prepare_rolling_by_window_args(window_size)
4620
5395
  by = Utils.parse_into_expression(by)
4621
- _from_rbexpr(
5396
+ wrap_expr(
4622
5397
  _rbexpr.rolling_std_by(
4623
5398
  by,
4624
5399
  window_size,
4625
- min_periods,
5400
+ min_samples,
4626
5401
  closed,
4627
5402
  ddof
4628
5403
  )
@@ -4653,7 +5428,7 @@ module Polars
4653
5428
  # (which may not be 24 hours, due to daylight savings). Similarly for
4654
5429
  # "calendar week", "calendar month", "calendar quarter", and
4655
5430
  # "calendar year".
4656
- # @param min_periods [Integer]
5431
+ # @param min_samples [Integer]
4657
5432
  # The number of values in the window that should be non-null before computing
4658
5433
  # a result.
4659
5434
  # @param closed ['left', 'right', 'both', 'none']
@@ -4661,8 +5436,6 @@ module Polars
4661
5436
  # defaults to `'right'`.
4662
5437
  # @param ddof [Integer]
4663
5438
  # "Delta Degrees of Freedom": The divisor for a length N window is N - ddof
4664
- # @param warn_if_unsorted [Boolean]
4665
- # Warn if data is not known to be sorted by `by` column.
4666
5439
  #
4667
5440
  # @return [Expr]
4668
5441
  #
@@ -4749,18 +5522,17 @@ module Polars
4749
5522
  def rolling_var_by(
4750
5523
  by,
4751
5524
  window_size,
4752
- min_periods: 1,
5525
+ min_samples: 1,
4753
5526
  closed: "right",
4754
- ddof: 1,
4755
- warn_if_unsorted: nil
5527
+ ddof: 1
4756
5528
  )
4757
5529
  window_size = _prepare_rolling_by_window_args(window_size)
4758
5530
  by = Utils.parse_into_expression(by)
4759
- _from_rbexpr(
5531
+ wrap_expr(
4760
5532
  _rbexpr.rolling_var_by(
4761
5533
  by,
4762
5534
  window_size,
4763
- min_periods,
5535
+ min_samples,
4764
5536
  closed,
4765
5537
  ddof
4766
5538
  )
@@ -4791,14 +5563,12 @@ module Polars
4791
5563
  # (which may not be 24 hours, due to daylight savings). Similarly for
4792
5564
  # "calendar week", "calendar month", "calendar quarter", and
4793
5565
  # "calendar year".
4794
- # @param min_periods [Integer]
5566
+ # @param min_samples [Integer]
4795
5567
  # The number of values in the window that should be non-null before computing
4796
5568
  # a result.
4797
5569
  # @param closed ['left', 'right', 'both', 'none']
4798
5570
  # Define which sides of the temporal interval are closed (inclusive),
4799
5571
  # defaults to `'right'`.
4800
- # @param warn_if_unsorted [Boolean]
4801
- # Warn if data is not known to be sorted by `by` column.
4802
5572
  #
4803
5573
  # @return [Expr]
4804
5574
  #
@@ -4861,14 +5631,13 @@ module Polars
4861
5631
  def rolling_median_by(
4862
5632
  by,
4863
5633
  window_size,
4864
- min_periods: 1,
4865
- closed: "right",
4866
- warn_if_unsorted: nil
5634
+ min_samples: 1,
5635
+ closed: "right"
4867
5636
  )
4868
5637
  window_size = _prepare_rolling_by_window_args(window_size)
4869
5638
  by = Utils.parse_into_expression(by)
4870
- _from_rbexpr(
4871
- _rbexpr.rolling_median_by(by, window_size, min_periods, closed)
5639
+ wrap_expr(
5640
+ _rbexpr.rolling_median_by(by, window_size, min_samples, closed)
4872
5641
  )
4873
5642
  end
4874
5643
 
@@ -4876,10 +5645,6 @@ module Polars
4876
5645
  #
4877
5646
  # @param by [String]
4878
5647
  # This column must be of dtype Datetime or Date.
4879
- # @param quantile [Float]
4880
- # Quantile between 0.0 and 1.0.
4881
- # @param interpolation ['nearest', 'higher', 'lower', 'midpoint', 'linear']
4882
- # Interpolation method.
4883
5648
  # @param window_size [String]
4884
5649
  # The length of the window. Can be a dynamic
4885
5650
  # temporal size indicated by a timedelta or the following string language:
@@ -4900,14 +5665,16 @@ module Polars
4900
5665
  # (which may not be 24 hours, due to daylight savings). Similarly for
4901
5666
  # "calendar week", "calendar month", "calendar quarter", and
4902
5667
  # "calendar year".
4903
- # @param min_periods [Integer]
5668
+ # @param quantile [Float]
5669
+ # Quantile between 0.0 and 1.0.
5670
+ # @param interpolation ['nearest', 'higher', 'lower', 'midpoint', 'linear']
5671
+ # Interpolation method.
5672
+ # @param min_samples [Integer]
4904
5673
  # The number of values in the window that should be non-null before computing
4905
5674
  # a result.
4906
5675
  # @param closed ['left', 'right', 'both', 'none']
4907
5676
  # Define which sides of the temporal interval are closed (inclusive),
4908
5677
  # defaults to `'right'`.
4909
- # @param warn_if_unsorted [Boolean]
4910
- # Warn if data is not known to be sorted by `by` column.
4911
5678
  #
4912
5679
  # @return [Expr]
4913
5680
  #
@@ -4972,24 +5739,110 @@ module Polars
4972
5739
  window_size,
4973
5740
  quantile:,
4974
5741
  interpolation: "nearest",
4975
- min_periods: 1,
4976
- closed: "right",
4977
- warn_if_unsorted: nil
5742
+ min_samples: 1,
5743
+ closed: "right"
4978
5744
  )
4979
5745
  window_size = _prepare_rolling_by_window_args(window_size)
4980
5746
  by = Utils.parse_into_expression(by)
4981
- _from_rbexpr(
5747
+ wrap_expr(
4982
5748
  _rbexpr.rolling_quantile_by(
4983
5749
  by,
4984
5750
  quantile,
4985
5751
  interpolation,
4986
5752
  window_size,
4987
- min_periods,
5753
+ min_samples,
4988
5754
  closed,
4989
5755
  )
4990
5756
  )
4991
5757
  end
4992
5758
 
5759
+ # Compute a rolling rank based on another column.
5760
+ #
5761
+ # @note
5762
+ # This functionality is considered **unstable**. It may be changed
5763
+ # at any point without it being considered a breaking change.
5764
+ #
5765
+ # Given a `by` column `<t_0, t_1, ..., t_n>`, then `closed: "right"`
5766
+ # (the default) means the windows will be:
5767
+ #
5768
+ # - (t_0 - window_size, t_0]
5769
+ # - (t_1 - window_size, t_1]
5770
+ # - ...
5771
+ # - (t_n - window_size, t_n]
5772
+ #
5773
+ # @param by [Expr]
5774
+ # Should be `DateTime`, `Date`, `UInt64`, `UInt32`, `Int64`,
5775
+ # or `Int32` data type (note that the integral ones require using `'i'`
5776
+ # in `window size`).
5777
+ # @param window_size [String]
5778
+ # The length of the window. Can be a dynamic
5779
+ # temporal size indicated by a timedelta or the following string language:
5780
+ #
5781
+ # - 1ns (1 nanosecond)
5782
+ # - 1us (1 microsecond)
5783
+ # - 1ms (1 millisecond)
5784
+ # - 1s (1 second)
5785
+ # - 1m (1 minute)
5786
+ # - 1h (1 hour)
5787
+ # - 1d (1 calendar day)
5788
+ # - 1w (1 calendar week)
5789
+ # - 1mo (1 calendar month)
5790
+ # - 1q (1 calendar quarter)
5791
+ # - 1y (1 calendar year)
5792
+ # - 1i (1 index count)
5793
+ #
5794
+ # By "calendar day", we mean the corresponding time on the next day
5795
+ # (which may not be 24 hours, due to daylight savings). Similarly for
5796
+ # "calendar week", "calendar month", "calendar quarter", and
5797
+ # "calendar year".
5798
+ # @param method ['average', 'min', 'max', 'dense', 'random']
5799
+ # The method used to assign ranks to tied elements.
5800
+ # The following methods are available (default is 'average'):
5801
+ #
5802
+ # - 'average' : The average of the ranks that would have been assigned to
5803
+ # all the tied values is assigned to each value.
5804
+ # - 'min' : The minimum of the ranks that would have been assigned to all
5805
+ # the tied values is assigned to each value. (This is also referred to
5806
+ # as "competition" ranking.)
5807
+ # - 'max' : The maximum of the ranks that would have been assigned to all
5808
+ # the tied values is assigned to each value.
5809
+ # - 'dense' : Like 'min', but the rank of the next highest element is
5810
+ # assigned the rank immediately after those assigned to the tied
5811
+ # elements.
5812
+ # - 'random' : Choose a random rank for each value in a tie.
5813
+ # @param seed [Integer]
5814
+ # Random seed used when `method: 'random'`. If set to nil (default), a
5815
+ # random seed is generated for each rolling rank operation.
5816
+ # @param min_samples [Integer]
5817
+ # The number of values in the window that should be non-null before computing
5818
+ # a result.
5819
+ # @param closed ['left', 'right', 'both', 'none']
5820
+ # Define which sides of the temporal interval are closed (inclusive),
5821
+ # defaults to `'right'`.
5822
+ #
5823
+ # @return [Expr]
5824
+ def rolling_rank_by(
5825
+ by,
5826
+ window_size,
5827
+ method: "average",
5828
+ seed: nil,
5829
+ min_samples: 1,
5830
+ closed: "right"
5831
+ )
5832
+ window_size = _prepare_rolling_by_window_args(window_size)
5833
+ by_rbexpr = Utils.parse_into_expression(by)
5834
+ Utils.wrap_expr(
5835
+ _rbexpr.rolling_rank_by(
5836
+ by_rbexpr,
5837
+ window_size,
5838
+ method,
5839
+ seed,
5840
+ min_samples,
5841
+ closed
5842
+ )
5843
+ )
5844
+ end
5845
+
4993
5846
  # Apply a rolling min (moving min) over the values in this array.
4994
5847
  #
4995
5848
  # A window of length `window_size` will traverse the array. The values that fill
@@ -5017,9 +5870,9 @@ module Polars
5017
5870
  # @param weights [Array]
5018
5871
  # An optional slice with the same length as the window that will be multiplied
5019
5872
  # elementwise with the values in the window.
5020
- # @param min_periods [Integer]
5873
+ # @param min_samples [Integer]
5021
5874
  # The number of values in the window that should be non-null before computing
5022
- # a result. If None, it will be set equal to window size.
5875
+ # a result. If nil, it will be set equal to window size.
5023
5876
  # @param center [Boolean]
5024
5877
  # Set the labels at the center of the window
5025
5878
  #
@@ -5029,7 +5882,7 @@ module Polars
5029
5882
  #
5030
5883
  # @note
5031
5884
  # If you want to compute multiple aggregation statistics over the same dynamic
5032
- # window, consider using `group_by_rolling` this method can cache the window size
5885
+ # window, consider using `rolling` this method can cache the window size
5033
5886
  # computation.
5034
5887
  #
5035
5888
  # @return [Expr]
@@ -5058,12 +5911,12 @@ module Polars
5058
5911
  def rolling_min(
5059
5912
  window_size,
5060
5913
  weights: nil,
5061
- min_periods: nil,
5914
+ min_samples: nil,
5062
5915
  center: false
5063
5916
  )
5064
- _from_rbexpr(
5917
+ wrap_expr(
5065
5918
  _rbexpr.rolling_min(
5066
- window_size, weights, min_periods, center
5919
+ window_size, weights, min_samples, center
5067
5920
  )
5068
5921
  )
5069
5922
  end
@@ -5095,9 +5948,9 @@ module Polars
5095
5948
  # @param weights [Array]
5096
5949
  # An optional slice with the same length as the window that will be multiplied
5097
5950
  # elementwise with the values in the window.
5098
- # @param min_periods [Integer]
5951
+ # @param min_samples [Integer]
5099
5952
  # The number of values in the window that should be non-null before computing
5100
- # a result. If None, it will be set equal to window size.
5953
+ # a result. If nil, it will be set equal to window size.
5101
5954
  # @param center [Boolean]
5102
5955
  # Set the labels at the center of the window
5103
5956
  #
@@ -5107,7 +5960,7 @@ module Polars
5107
5960
  #
5108
5961
  # @note
5109
5962
  # If you want to compute multiple aggregation statistics over the same dynamic
5110
- # window, consider using `group_by_rolling` this method can cache the window size
5963
+ # window, consider using `rolling` this method can cache the window size
5111
5964
  # computation.
5112
5965
  #
5113
5966
  # @return [Expr]
@@ -5136,12 +5989,12 @@ module Polars
5136
5989
  def rolling_max(
5137
5990
  window_size,
5138
5991
  weights: nil,
5139
- min_periods: nil,
5992
+ min_samples: nil,
5140
5993
  center: false
5141
5994
  )
5142
- _from_rbexpr(
5995
+ wrap_expr(
5143
5996
  _rbexpr.rolling_max(
5144
- window_size, weights, min_periods, center
5997
+ window_size, weights, min_samples, center
5145
5998
  )
5146
5999
  )
5147
6000
  end
@@ -5173,9 +6026,9 @@ module Polars
5173
6026
  # @param weights [Array]
5174
6027
  # An optional slice with the same length as the window that will be multiplied
5175
6028
  # elementwise with the values in the window.
5176
- # @param min_periods [Integer]
6029
+ # @param min_samples [Integer]
5177
6030
  # The number of values in the window that should be non-null before computing
5178
- # a result. If None, it will be set equal to window size.
6031
+ # a result. If nil, it will be set equal to window size.
5179
6032
  # @param center [Boolean]
5180
6033
  # Set the labels at the center of the window
5181
6034
  #
@@ -5185,7 +6038,7 @@ module Polars
5185
6038
  #
5186
6039
  # @note
5187
6040
  # If you want to compute multiple aggregation statistics over the same dynamic
5188
- # window, consider using `group_by_rolling` this method can cache the window size
6041
+ # window, consider using `rolling` this method can cache the window size
5189
6042
  # computation.
5190
6043
  #
5191
6044
  # @return [Expr]
@@ -5214,12 +6067,12 @@ module Polars
5214
6067
  def rolling_mean(
5215
6068
  window_size,
5216
6069
  weights: nil,
5217
- min_periods: nil,
6070
+ min_samples: nil,
5218
6071
  center: false
5219
6072
  )
5220
- _from_rbexpr(
6073
+ wrap_expr(
5221
6074
  _rbexpr.rolling_mean(
5222
- window_size, weights, min_periods, center
6075
+ window_size, weights, min_samples, center
5223
6076
  )
5224
6077
  )
5225
6078
  end
@@ -5251,9 +6104,9 @@ module Polars
5251
6104
  # @param weights [Array]
5252
6105
  # An optional slice with the same length as the window that will be multiplied
5253
6106
  # elementwise with the values in the window.
5254
- # @param min_periods [Integer]
6107
+ # @param min_samples [Integer]
5255
6108
  # The number of values in the window that should be non-null before computing
5256
- # a result. If None, it will be set equal to window size.
6109
+ # a result. If nil, it will be set equal to window size.
5257
6110
  # @param center [Boolean]
5258
6111
  # Set the labels at the center of the window
5259
6112
  #
@@ -5263,7 +6116,7 @@ module Polars
5263
6116
  #
5264
6117
  # @note
5265
6118
  # If you want to compute multiple aggregation statistics over the same dynamic
5266
- # window, consider using `group_by_rolling` this method can cache the window size
6119
+ # window, consider using `rolling` this method can cache the window size
5267
6120
  # computation.
5268
6121
  #
5269
6122
  # @return [Expr]
@@ -5292,12 +6145,12 @@ module Polars
5292
6145
  def rolling_sum(
5293
6146
  window_size,
5294
6147
  weights: nil,
5295
- min_periods: nil,
6148
+ min_samples: nil,
5296
6149
  center: false
5297
6150
  )
5298
- _from_rbexpr(
6151
+ wrap_expr(
5299
6152
  _rbexpr.rolling_sum(
5300
- window_size, weights, min_periods, center
6153
+ window_size, weights, min_samples, center
5301
6154
  )
5302
6155
  )
5303
6156
  end
@@ -5329,11 +6182,13 @@ module Polars
5329
6182
  # @param weights [Array]
5330
6183
  # An optional slice with the same length as the window that will be multiplied
5331
6184
  # elementwise with the values in the window.
5332
- # @param min_periods [Integer]
6185
+ # @param min_samples [Integer]
5333
6186
  # The number of values in the window that should be non-null before computing
5334
- # a result. If None, it will be set equal to window size.
6187
+ # a result. If nil, it will be set equal to window size.
5335
6188
  # @param center [Boolean]
5336
6189
  # Set the labels at the center of the window
6190
+ # @param ddof [Integer]
6191
+ # "Delta Degrees of Freedom": The divisor for a length N window is N - ddof
5337
6192
  #
5338
6193
  # @note
5339
6194
  # This functionality is experimental and may change without it being considered a
@@ -5341,7 +6196,7 @@ module Polars
5341
6196
  #
5342
6197
  # @note
5343
6198
  # If you want to compute multiple aggregation statistics over the same dynamic
5344
- # window, consider using `group_by_rolling` this method can cache the window size
6199
+ # window, consider using `rolling` this method can cache the window size
5345
6200
  # computation.
5346
6201
  #
5347
6202
  # @return [Expr]
@@ -5370,13 +6225,13 @@ module Polars
5370
6225
  def rolling_std(
5371
6226
  window_size,
5372
6227
  weights: nil,
5373
- min_periods: nil,
6228
+ min_samples: nil,
5374
6229
  center: false,
5375
6230
  ddof: 1
5376
6231
  )
5377
- _from_rbexpr(
6232
+ wrap_expr(
5378
6233
  _rbexpr.rolling_std(
5379
- window_size, weights, min_periods, center, ddof
6234
+ window_size, weights, min_samples, center, ddof
5380
6235
  )
5381
6236
  )
5382
6237
  end
@@ -5408,11 +6263,13 @@ module Polars
5408
6263
  # @param weights [Array]
5409
6264
  # An optional slice with the same length as the window that will be multiplied
5410
6265
  # elementwise with the values in the window.
5411
- # @param min_periods [Integer]
6266
+ # @param min_samples [Integer]
5412
6267
  # The number of values in the window that should be non-null before computing
5413
- # a result. If None, it will be set equal to window size.
6268
+ # a result. If nil, it will be set equal to window size.
5414
6269
  # @param center [Boolean]
5415
6270
  # Set the labels at the center of the window
6271
+ # @param ddof [Integer]
6272
+ # "Delta Degrees of Freedom": The divisor for a length N window is N - ddof
5416
6273
  #
5417
6274
  # @note
5418
6275
  # This functionality is experimental and may change without it being considered a
@@ -5420,7 +6277,7 @@ module Polars
5420
6277
  #
5421
6278
  # @note
5422
6279
  # If you want to compute multiple aggregation statistics over the same dynamic
5423
- # window, consider using `group_by_rolling` this method can cache the window size
6280
+ # window, consider using `rolling` this method can cache the window size
5424
6281
  # computation.
5425
6282
  #
5426
6283
  # @return [Expr]
@@ -5449,13 +6306,13 @@ module Polars
5449
6306
  def rolling_var(
5450
6307
  window_size,
5451
6308
  weights: nil,
5452
- min_periods: nil,
6309
+ min_samples: nil,
5453
6310
  center: false,
5454
6311
  ddof: 1
5455
6312
  )
5456
- _from_rbexpr(
6313
+ wrap_expr(
5457
6314
  _rbexpr.rolling_var(
5458
- window_size, weights, min_periods, center, ddof
6315
+ window_size, weights, min_samples, center, ddof
5459
6316
  )
5460
6317
  )
5461
6318
  end
@@ -5483,9 +6340,9 @@ module Polars
5483
6340
  # @param weights [Array]
5484
6341
  # An optional slice with the same length as the window that will be multiplied
5485
6342
  # elementwise with the values in the window.
5486
- # @param min_periods [Integer]
6343
+ # @param min_samples [Integer]
5487
6344
  # The number of values in the window that should be non-null before computing
5488
- # a result. If None, it will be set equal to window size.
6345
+ # a result. If nil, it will be set equal to window size.
5489
6346
  # @param center [Boolean]
5490
6347
  # Set the labels at the center of the window
5491
6348
  #
@@ -5495,7 +6352,7 @@ module Polars
5495
6352
  #
5496
6353
  # @note
5497
6354
  # If you want to compute multiple aggregation statistics over the same dynamic
5498
- # window, consider using `group_by_rolling` this method can cache the window size
6355
+ # window, consider using `rolling` this method can cache the window size
5499
6356
  # computation.
5500
6357
  #
5501
6358
  # @return [Expr]
@@ -5524,12 +6381,12 @@ module Polars
5524
6381
  def rolling_median(
5525
6382
  window_size,
5526
6383
  weights: nil,
5527
- min_periods: nil,
6384
+ min_samples: nil,
5528
6385
  center: false
5529
6386
  )
5530
- _from_rbexpr(
6387
+ wrap_expr(
5531
6388
  _rbexpr.rolling_median(
5532
- window_size, weights, min_periods, center
6389
+ window_size, weights, min_samples, center
5533
6390
  )
5534
6391
  )
5535
6392
  end
@@ -5561,9 +6418,9 @@ module Polars
5561
6418
  # @param weights [Array]
5562
6419
  # An optional slice with the same length as the window that will be multiplied
5563
6420
  # elementwise with the values in the window.
5564
- # @param min_periods [Integer]
6421
+ # @param min_samples [Integer]
5565
6422
  # The number of values in the window that should be non-null before computing
5566
- # a result. If None, it will be set equal to window size.
6423
+ # a result. If nil, it will be set equal to window size.
5567
6424
  # @param center [Boolean]
5568
6425
  # Set the labels at the center of the window
5569
6426
  #
@@ -5573,7 +6430,7 @@ module Polars
5573
6430
  #
5574
6431
  # @note
5575
6432
  # If you want to compute multiple aggregation statistics over the same dynamic
5576
- # window, consider using `group_by_rolling` this method can cache the window size
6433
+ # window, consider using `rolling` this method can cache the window size
5577
6434
  # computation.
5578
6435
  #
5579
6436
  # @return [Expr]
@@ -5594,89 +6451,98 @@ module Polars
5594
6451
  # # ╞══════╡
5595
6452
  # # │ null │
5596
6453
  # # │ null │
5597
- # # │ 1.0 │
5598
6454
  # # │ 2.0 │
5599
6455
  # # │ 3.0 │
5600
6456
  # # │ 4.0 │
6457
+ # # │ 6.0 │
5601
6458
  # # └──────┘
5602
6459
  def rolling_quantile(
5603
6460
  quantile,
5604
6461
  interpolation: "nearest",
5605
6462
  window_size: 2,
5606
6463
  weights: nil,
5607
- min_periods: nil,
6464
+ min_samples: nil,
5608
6465
  center: false
5609
6466
  )
5610
- _from_rbexpr(
6467
+ wrap_expr(
5611
6468
  _rbexpr.rolling_quantile(
5612
- quantile, interpolation, window_size, weights, min_periods, center
6469
+ quantile, interpolation, window_size, weights, min_samples, center
5613
6470
  )
5614
6471
  )
5615
6472
  end
5616
6473
 
5617
- # Apply a custom rolling window function.
6474
+ # Compute a rolling rank.
5618
6475
  #
5619
- # Prefer the specific rolling window functions over this one, as they are faster.
6476
+ # @note
6477
+ # This functionality is considered **unstable**. It may be changed
6478
+ # at any point without it being considered a breaking change.
5620
6479
  #
5621
- # Prefer:
5622
- # * rolling_min
5623
- # * rolling_max
5624
- # * rolling_mean
5625
- # * rolling_sum
6480
+ # A window of length `window_size` will traverse the array. The values
6481
+ # that fill this window will be ranked according to the `method`
6482
+ # parameter. The resulting values will be the rank of the value that is
6483
+ # at the end of the sliding window.
5626
6484
  #
5627
6485
  # @param window_size [Integer]
5628
- # The length of the window.
5629
- # @param weights [Object]
5630
- # An optional slice with the same length as the window that will be multiplied
5631
- # elementwise with the values in the window.
5632
- # @param min_periods [Integer]
6486
+ # Integer size of the rolling window.
6487
+ # @param method ['average', 'min', 'max', 'dense', 'random']
6488
+ # The method used to assign ranks to tied elements.
6489
+ # The following methods are available (default is 'average'):
6490
+ #
6491
+ # - 'average' : The average of the ranks that would have been assigned to
6492
+ # all the tied values is assigned to each value.
6493
+ # - 'min' : The minimum of the ranks that would have been assigned to all
6494
+ # the tied values is assigned to each value. (This is also referred to
6495
+ # as "competition" ranking.)
6496
+ # - 'max' : The maximum of the ranks that would have been assigned to all
6497
+ # the tied values is assigned to each value.
6498
+ # - 'dense' : Like 'min', but the rank of the next highest element is
6499
+ # assigned the rank immediately after those assigned to the tied
6500
+ # elements.
6501
+ # - 'random' : Choose a random rank for each value in a tie.
6502
+ # @param seed [Integer]
6503
+ # Random seed used when `method: 'random'`. If set to nil (default), a
6504
+ # random seed is generated for each rolling rank operation.
6505
+ # @param min_samples [Integer]
5633
6506
  # The number of values in the window that should be non-null before computing
5634
- # a result. If nil, it will be set equal to window size.
6507
+ # a result. If set to `nil` (default), it will be set equal to `window_size`.
5635
6508
  # @param center [Boolean]
5636
- # Set the labels at the center of the window
6509
+ # Set the labels at the center of the window.
5637
6510
  #
5638
6511
  # @return [Expr]
5639
6512
  #
5640
6513
  # @example
5641
- # df = Polars::DataFrame.new(
5642
- # {
5643
- # "A" => [1.0, 2.0, 9.0, 2.0, 13.0]
5644
- # }
5645
- # )
5646
- # df.select(
5647
- # [
5648
- # Polars.col("A").rolling_apply(window_size: 3) { |s| s.std }
5649
- # ]
5650
- # )
6514
+ # df = Polars::DataFrame.new({"a" => [1, 4, 4, 1, 9]})
6515
+ # df.select(Polars.col("a").rolling_rank(3, method: "average"))
5651
6516
  # # =>
5652
6517
  # # shape: (5, 1)
5653
- # # ┌──────────┐
5654
- # # │ A
5655
- # # │ ---
5656
- # # │ f64
5657
- # # ╞══════════╡
5658
- # # │ null
5659
- # # │ null
5660
- # # │ 4.358899
5661
- # # │ 4.041452
5662
- # # │ 5.567764
5663
- # # └──────────┘
5664
- # def rolling_apply(
5665
- # window_size:,
5666
- # weights: nil,
5667
- # min_periods: nil,
5668
- # center: false,
5669
- # &function
5670
- # )
5671
- # if min_periods.nil?
5672
- # min_periods = window_size
5673
- # end
5674
- # _from_rbexpr(
5675
- # _rbexpr.rolling_apply(
5676
- # function, window_size, weights, min_periods, center
5677
- # )
5678
- # )
5679
- # end
6518
+ # # ┌──────┐
6519
+ # # │ a
6520
+ # # │ ---
6521
+ # # │ f64
6522
+ # # ╞══════╡
6523
+ # # │ null
6524
+ # # │ null
6525
+ # # │ 2.5
6526
+ # # │ 1.0
6527
+ # # │ 3.0
6528
+ # # └──────┘
6529
+ def rolling_rank(
6530
+ window_size,
6531
+ method: "average",
6532
+ seed: nil,
6533
+ min_samples: nil,
6534
+ center: false
6535
+ )
6536
+ Utils.wrap_expr(
6537
+ _rbexpr.rolling_rank(
6538
+ window_size,
6539
+ method,
6540
+ seed,
6541
+ min_samples,
6542
+ center
6543
+ )
6544
+ )
6545
+ end
5680
6546
 
5681
6547
  # Compute a rolling skew.
5682
6548
  #
@@ -5684,6 +6550,11 @@ module Polars
5684
6550
  # Integer size of the rolling window.
5685
6551
  # @param bias [Boolean]
5686
6552
  # If false, the calculations are corrected for statistical bias.
6553
+ # @param min_samples [Integer]
6554
+ # The number of values in the window that should be non-null before computing
6555
+ # a result. If set to `nil` (default), it will be set equal to `window_size`.
6556
+ # @param center [Boolean]
6557
+ # Set the labels at the center of the window.
5687
6558
  #
5688
6559
  # @return [Expr]
5689
6560
  #
@@ -5702,13 +6573,131 @@ module Polars
5702
6573
  # # │ 0.381802 │
5703
6574
  # # │ 0.47033 │
5704
6575
  # # └──────────┘
5705
- def rolling_skew(window_size, bias: true)
5706
- _from_rbexpr(_rbexpr.rolling_skew(window_size, bias))
6576
+ def rolling_skew(window_size, bias: true, min_samples: nil, center: false)
6577
+ wrap_expr(_rbexpr.rolling_skew(window_size, bias, min_samples, center))
5707
6578
  end
5708
6579
 
5709
- # Compute absolute values.
6580
+ # Compute a rolling kurtosis.
5710
6581
  #
5711
- # @return [Expr]
6582
+ # @note
6583
+ # This functionality is considered **unstable**. It may be changed
6584
+ # at any point without it being considered a breaking change.
6585
+ #
6586
+ # The window at a given row will include the row itself, and the `window_size - 1`
6587
+ # elements before it.
6588
+ #
6589
+ # @param window_size [Integer]
6590
+ # Integer size of the rolling window.
6591
+ # @param fisher [Boolean]
6592
+ # If true, Fisher's definition is used (normal ==> 0.0). If false,
6593
+ # Pearson's definition is used (normal ==> 3.0).
6594
+ # @param bias [Boolean]
6595
+ # If false, the calculations are corrected for statistical bias.
6596
+ # @param min_samples [Integer]
6597
+ # The number of values in the window that should be non-null before computing
6598
+ # a result. If set to `nil` (default), it will be set equal to `window_size`.
6599
+ # @param center
6600
+ # Set the labels at the center of the window.
6601
+ #
6602
+ # @return [Expr]
6603
+ #
6604
+ # @example
6605
+ # df = Polars::DataFrame.new({"a" => [1, 4, 2, 9]})
6606
+ # df.select(Polars.col("a").rolling_kurtosis(3))
6607
+ # # =>
6608
+ # # shape: (4, 1)
6609
+ # # ┌──────┐
6610
+ # # │ a │
6611
+ # # │ --- │
6612
+ # # │ f64 │
6613
+ # # ╞══════╡
6614
+ # # │ null │
6615
+ # # │ null │
6616
+ # # │ -1.5 │
6617
+ # # │ -1.5 │
6618
+ # # └──────┘
6619
+ def rolling_kurtosis(
6620
+ window_size,
6621
+ fisher: true,
6622
+ bias: true,
6623
+ min_samples: nil,
6624
+ center: false
6625
+ )
6626
+ wrap_expr(
6627
+ _rbexpr.rolling_kurtosis(
6628
+ window_size,
6629
+ fisher,
6630
+ bias,
6631
+ min_samples,
6632
+ center
6633
+ )
6634
+ )
6635
+ end
6636
+
6637
+ # Compute a custom rolling window function.
6638
+ #
6639
+ # @note
6640
+ # This functionality is considered **unstable**. It may be changed
6641
+ # at any point without it being considered a breaking change.
6642
+ #
6643
+ # @param window_size [Integer]
6644
+ # The length of the window in number of elements.
6645
+ # @param weights [Object]
6646
+ # An optional slice with the same length as the window that will be multiplied
6647
+ # elementwise with the values in the window.
6648
+ # @param min_samples [Integer]
6649
+ # The number of values in the window that should be non-null before computing
6650
+ # a result. If set to `nil` (default), it will be set equal to `window_size`.
6651
+ # @param center [Boolean]
6652
+ # Set the labels at the center of the window.
6653
+ #
6654
+ # @return [Expr]
6655
+ #
6656
+ # @example
6657
+ # df = Polars::DataFrame.new({"a" => [11.0, 2.0, 9.0, Float::NAN, 8.0]})
6658
+ # df.select(Polars.col("a").rolling_map(3) { |v| v.drop_nans.sum })
6659
+ # # =>
6660
+ # # shape: (5, 1)
6661
+ # # ┌──────┐
6662
+ # # │ a │
6663
+ # # │ --- │
6664
+ # # │ f64 │
6665
+ # # ╞══════╡
6666
+ # # │ null │
6667
+ # # │ null │
6668
+ # # │ 22.0 │
6669
+ # # │ 11.0 │
6670
+ # # │ 17.0 │
6671
+ # # └──────┘
6672
+ def rolling_map(
6673
+ window_size,
6674
+ weights: nil,
6675
+ min_samples: nil,
6676
+ center: false,
6677
+ &function
6678
+ )
6679
+ if min_samples.nil?
6680
+ min_samples = window_size
6681
+ end
6682
+
6683
+ _wrap = lambda do |rbs|
6684
+ s = Utils.wrap_s(rbs)
6685
+ rv = function.(s)
6686
+ if rv.is_a?(Series)
6687
+ rv._s
6688
+ else
6689
+ Series.new([rv])._s
6690
+ end
6691
+ end
6692
+
6693
+ wrap_expr(
6694
+ _rbexpr.rolling_map(_wrap, window_size, weights, min_samples, center)
6695
+ )
6696
+ end
6697
+
6698
+ # Compute absolute values.
6699
+ #
6700
+ # @return [Expr]
5712
6701
  #
5713
6702
  # @example
5714
6703
  # df = Polars::DataFrame.new(
@@ -5730,40 +6719,7 @@ module Polars
5730
6719
  # # │ 2.0 │
5731
6720
  # # └─────┘
5732
6721
  def abs
5733
- _from_rbexpr(_rbexpr.abs)
5734
- end
5735
-
5736
- # Get the index values that would sort this column.
5737
- #
5738
- # Alias for {#arg_sort}.
5739
- #
5740
- # @param reverse [Boolean]
5741
- # Sort in reverse (descending) order.
5742
- # @param nulls_last [Boolean]
5743
- # Place null values last instead of first.
5744
- #
5745
- # @return [expr]
5746
- #
5747
- # @example
5748
- # df = Polars::DataFrame.new(
5749
- # {
5750
- # "a" => [20, 10, 30]
5751
- # }
5752
- # )
5753
- # df.select(Polars.col("a").argsort)
5754
- # # =>
5755
- # # shape: (3, 1)
5756
- # # ┌─────┐
5757
- # # │ a │
5758
- # # │ --- │
5759
- # # │ u32 │
5760
- # # ╞═════╡
5761
- # # │ 1 │
5762
- # # │ 0 │
5763
- # # │ 2 │
5764
- # # └─────┘
5765
- def argsort(reverse: false, nulls_last: false)
5766
- arg_sort(reverse: reverse, nulls_last: nulls_last)
6722
+ wrap_expr(_rbexpr.abs)
5767
6723
  end
5768
6724
 
5769
6725
  # Assign ranks to data, dealing with ties appropriately.
@@ -5786,7 +6742,7 @@ module Polars
5786
6742
  # the order that the values occur in the Series.
5787
6743
  # - 'random' : Like 'ordinal', but the rank for ties is not dependent
5788
6744
  # on the order that the values occur in the Series.
5789
- # @param reverse [Boolean]
6745
+ # @param descending [Boolean]
5790
6746
  # Reverse the operation.
5791
6747
  # @param seed [Integer]
5792
6748
  # If `method: "random"`, use this as seed.
@@ -5826,8 +6782,8 @@ module Polars
5826
6782
  # # │ 2 │
5827
6783
  # # │ 5 │
5828
6784
  # # └─────┘
5829
- def rank(method: "average", reverse: false, seed: nil)
5830
- _from_rbexpr(_rbexpr.rank(method, reverse, seed))
6785
+ def rank(method: "average", descending: false, seed: nil)
6786
+ wrap_expr(_rbexpr.rank(method, descending, seed))
5831
6787
  end
5832
6788
 
5833
6789
  # Calculate the n-th discrete difference.
@@ -5858,7 +6814,8 @@ module Polars
5858
6814
  # # │ 20 │
5859
6815
  # # └──────┘
5860
6816
  def diff(n: 1, null_behavior: "ignore")
5861
- _from_rbexpr(_rbexpr.diff(n, null_behavior))
6817
+ n = Utils.parse_into_expression(n)
6818
+ wrap_expr(_rbexpr.diff(n, null_behavior))
5862
6819
  end
5863
6820
 
5864
6821
  # Computes percentage change between values.
@@ -5879,7 +6836,7 @@ module Polars
5879
6836
  # "a" => [10, 11, 12, nil, 12]
5880
6837
  # }
5881
6838
  # )
5882
- # df.with_column(Polars.col("a").pct_change.alias("pct_change"))
6839
+ # df.with_columns(Polars.col("a").pct_change.alias("pct_change"))
5883
6840
  # # =>
5884
6841
  # # shape: (5, 2)
5885
6842
  # # ┌──────┬────────────┐
@@ -5890,12 +6847,12 @@ module Polars
5890
6847
  # # │ 10 ┆ null │
5891
6848
  # # │ 11 ┆ 0.1 │
5892
6849
  # # │ 12 ┆ 0.090909 │
5893
- # # │ null ┆ 0.0
5894
- # # │ 12 ┆ 0.0
6850
+ # # │ null ┆ null
6851
+ # # │ 12 ┆ null
5895
6852
  # # └──────┴────────────┘
5896
6853
  def pct_change(n: 1)
5897
6854
  n = Utils.parse_into_expression(n)
5898
- _from_rbexpr(_rbexpr.pct_change(n))
6855
+ wrap_expr(_rbexpr.pct_change(n))
5899
6856
  end
5900
6857
 
5901
6858
  # Compute the sample skewness of a data set.
@@ -5924,7 +6881,7 @@ module Polars
5924
6881
  # # │ 0.343622 │
5925
6882
  # # └──────────┘
5926
6883
  def skew(bias: true)
5927
- _from_rbexpr(_rbexpr.skew(bias))
6884
+ wrap_expr(_rbexpr.skew(bias))
5928
6885
  end
5929
6886
 
5930
6887
  # Compute the kurtosis (Fisher or Pearson) of a dataset.
@@ -5932,7 +6889,7 @@ module Polars
5932
6889
  # Kurtosis is the fourth central moment divided by the square of the
5933
6890
  # variance. If Fisher's definition is used, then 3.0 is subtracted from
5934
6891
  # the result to give 0.0 for a normal distribution.
5935
- # If bias is False then the kurtosis is calculated using k statistics to
6892
+ # If bias is false then the kurtosis is calculated using k statistics to
5936
6893
  # eliminate bias coming from biased moment estimators
5937
6894
  #
5938
6895
  # @param fisher [Boolean]
@@ -5956,7 +6913,7 @@ module Polars
5956
6913
  # # │ -1.153061 │
5957
6914
  # # └───────────┘
5958
6915
  def kurtosis(fisher: true, bias: true)
5959
- _from_rbexpr(_rbexpr.kurtosis(fisher, bias))
6916
+ wrap_expr(_rbexpr.kurtosis(fisher, bias))
5960
6917
  end
5961
6918
 
5962
6919
  # Set values outside the given boundaries to the boundary value.
@@ -5973,7 +6930,7 @@ module Polars
5973
6930
  #
5974
6931
  # @example
5975
6932
  # df = Polars::DataFrame.new({"foo" => [-50, 5, nil, 50]})
5976
- # df.with_column(Polars.col("foo").clip(1, 10).alias("foo_clipped"))
6933
+ # df.with_columns(Polars.col("foo").clip(1, 10).alias("foo_clipped"))
5977
6934
  # # =>
5978
6935
  # # shape: (4, 2)
5979
6936
  # # ┌──────┬─────────────┐
@@ -5993,69 +6950,7 @@ module Polars
5993
6950
  if !upper_bound.nil?
5994
6951
  upper_bound = Utils.parse_into_expression(upper_bound)
5995
6952
  end
5996
- _from_rbexpr(_rbexpr.clip(lower_bound, upper_bound))
5997
- end
5998
-
5999
- # Clip (limit) the values in an array to a `min` boundary.
6000
- #
6001
- # Only works for numerical types.
6002
- #
6003
- # If you want to clip other dtypes, consider writing a "when, then, otherwise"
6004
- # expression. See `when` for more information.
6005
- #
6006
- # @param lower_bound [Numeric]
6007
- # Minimum value.
6008
- #
6009
- # @return [Expr]
6010
- #
6011
- # @example
6012
- # df = Polars::DataFrame.new({"foo" => [-50, 5, nil, 50]})
6013
- # df.with_column(Polars.col("foo").clip_min(0).alias("foo_clipped"))
6014
- # # =>
6015
- # # shape: (4, 2)
6016
- # # ┌──────┬─────────────┐
6017
- # # │ foo ┆ foo_clipped │
6018
- # # │ --- ┆ --- │
6019
- # # │ i64 ┆ i64 │
6020
- # # ╞══════╪═════════════╡
6021
- # # │ -50 ┆ 0 │
6022
- # # │ 5 ┆ 5 │
6023
- # # │ null ┆ null │
6024
- # # │ 50 ┆ 50 │
6025
- # # └──────┴─────────────┘
6026
- def clip_min(lower_bound)
6027
- clip(lower_bound, nil)
6028
- end
6029
-
6030
- # Clip (limit) the values in an array to a `max` boundary.
6031
- #
6032
- # Only works for numerical types.
6033
- #
6034
- # If you want to clip other dtypes, consider writing a "when, then, otherwise"
6035
- # expression. See `when` for more information.
6036
- #
6037
- # @param upper_bound [Numeric]
6038
- # Maximum value.
6039
- #
6040
- # @return [Expr]
6041
- #
6042
- # @example
6043
- # df = Polars::DataFrame.new({"foo" => [-50, 5, nil, 50]})
6044
- # df.with_column(Polars.col("foo").clip_max(0).alias("foo_clipped"))
6045
- # # =>
6046
- # # shape: (4, 2)
6047
- # # ┌──────┬─────────────┐
6048
- # # │ foo ┆ foo_clipped │
6049
- # # │ --- ┆ --- │
6050
- # # │ i64 ┆ i64 │
6051
- # # ╞══════╪═════════════╡
6052
- # # │ -50 ┆ -50 │
6053
- # # │ 5 ┆ 0 │
6054
- # # │ null ┆ null │
6055
- # # │ 50 ┆ 0 │
6056
- # # └──────┴─────────────┘
6057
- def clip_max(upper_bound)
6058
- clip(nil, upper_bound)
6953
+ wrap_expr(_rbexpr.clip(lower_bound, upper_bound))
6059
6954
  end
6060
6955
 
6061
6956
  # Calculate the lower bound.
@@ -6078,7 +6973,7 @@ module Polars
6078
6973
  # # │ -9223372036854775808 │
6079
6974
  # # └──────────────────────┘
6080
6975
  def lower_bound
6081
- _from_rbexpr(_rbexpr.lower_bound)
6976
+ wrap_expr(_rbexpr.lower_bound)
6082
6977
  end
6083
6978
 
6084
6979
  # Calculate the upper bound.
@@ -6101,7 +6996,7 @@ module Polars
6101
6996
  # # │ 9223372036854775807 │
6102
6997
  # # └─────────────────────┘
6103
6998
  def upper_bound
6104
- _from_rbexpr(_rbexpr.upper_bound)
6999
+ wrap_expr(_rbexpr.upper_bound)
6105
7000
  end
6106
7001
 
6107
7002
  # Compute the element-wise indication of the sign.
@@ -6125,7 +7020,7 @@ module Polars
6125
7020
  # # │ null │
6126
7021
  # # └──────┘
6127
7022
  def sign
6128
- _from_rbexpr(_rbexpr.sign)
7023
+ wrap_expr(_rbexpr.sign)
6129
7024
  end
6130
7025
 
6131
7026
  # Compute the element-wise value for the sine.
@@ -6145,7 +7040,7 @@ module Polars
6145
7040
  # # │ 0.0 │
6146
7041
  # # └─────┘
6147
7042
  def sin
6148
- _from_rbexpr(_rbexpr.sin)
7043
+ wrap_expr(_rbexpr.sin)
6149
7044
  end
6150
7045
 
6151
7046
  # Compute the element-wise value for the cosine.
@@ -6165,7 +7060,7 @@ module Polars
6165
7060
  # # │ 1.0 │
6166
7061
  # # └─────┘
6167
7062
  def cos
6168
- _from_rbexpr(_rbexpr.cos)
7063
+ wrap_expr(_rbexpr.cos)
6169
7064
  end
6170
7065
 
6171
7066
  # Compute the element-wise value for the tangent.
@@ -6185,7 +7080,27 @@ module Polars
6185
7080
  # # │ 1.557408 │
6186
7081
  # # └──────────┘
6187
7082
  def tan
6188
- _from_rbexpr(_rbexpr.tan)
7083
+ wrap_expr(_rbexpr.tan)
7084
+ end
7085
+
7086
+ # Compute the element-wise value for the cotangent.
7087
+ #
7088
+ # @return [Expr]
7089
+ #
7090
+ # @example
7091
+ # df = Polars::DataFrame.new({"a" => [1.0]})
7092
+ # df.select(Polars.col("a").cot.round(2))
7093
+ # # =>
7094
+ # # shape: (1, 1)
7095
+ # # ┌──────┐
7096
+ # # │ a │
7097
+ # # │ --- │
7098
+ # # │ f64 │
7099
+ # # ╞══════╡
7100
+ # # │ 0.64 │
7101
+ # # └──────┘
7102
+ def cot
7103
+ wrap_expr(_rbexpr.cot)
6189
7104
  end
6190
7105
 
6191
7106
  # Compute the element-wise value for the inverse sine.
@@ -6205,7 +7120,7 @@ module Polars
6205
7120
  # # │ 1.570796 │
6206
7121
  # # └──────────┘
6207
7122
  def arcsin
6208
- _from_rbexpr(_rbexpr.arcsin)
7123
+ wrap_expr(_rbexpr.arcsin)
6209
7124
  end
6210
7125
 
6211
7126
  # Compute the element-wise value for the inverse cosine.
@@ -6225,7 +7140,7 @@ module Polars
6225
7140
  # # │ 1.570796 │
6226
7141
  # # └──────────┘
6227
7142
  def arccos
6228
- _from_rbexpr(_rbexpr.arccos)
7143
+ wrap_expr(_rbexpr.arccos)
6229
7144
  end
6230
7145
 
6231
7146
  # Compute the element-wise value for the inverse tangent.
@@ -6245,7 +7160,7 @@ module Polars
6245
7160
  # # │ 0.785398 │
6246
7161
  # # └──────────┘
6247
7162
  def arctan
6248
- _from_rbexpr(_rbexpr.arctan)
7163
+ wrap_expr(_rbexpr.arctan)
6249
7164
  end
6250
7165
 
6251
7166
  # Compute the element-wise value for the hyperbolic sine.
@@ -6265,7 +7180,7 @@ module Polars
6265
7180
  # # │ 1.175201 │
6266
7181
  # # └──────────┘
6267
7182
  def sinh
6268
- _from_rbexpr(_rbexpr.sinh)
7183
+ wrap_expr(_rbexpr.sinh)
6269
7184
  end
6270
7185
 
6271
7186
  # Compute the element-wise value for the hyperbolic cosine.
@@ -6285,7 +7200,7 @@ module Polars
6285
7200
  # # │ 1.543081 │
6286
7201
  # # └──────────┘
6287
7202
  def cosh
6288
- _from_rbexpr(_rbexpr.cosh)
7203
+ wrap_expr(_rbexpr.cosh)
6289
7204
  end
6290
7205
 
6291
7206
  # Compute the element-wise value for the hyperbolic tangent.
@@ -6305,7 +7220,7 @@ module Polars
6305
7220
  # # │ 0.761594 │
6306
7221
  # # └──────────┘
6307
7222
  def tanh
6308
- _from_rbexpr(_rbexpr.tanh)
7223
+ wrap_expr(_rbexpr.tanh)
6309
7224
  end
6310
7225
 
6311
7226
  # Compute the element-wise value for the inverse hyperbolic sine.
@@ -6325,7 +7240,7 @@ module Polars
6325
7240
  # # │ 0.881374 │
6326
7241
  # # └──────────┘
6327
7242
  def arcsinh
6328
- _from_rbexpr(_rbexpr.arcsinh)
7243
+ wrap_expr(_rbexpr.arcsinh)
6329
7244
  end
6330
7245
 
6331
7246
  # Compute the element-wise value for the inverse hyperbolic cosine.
@@ -6345,7 +7260,7 @@ module Polars
6345
7260
  # # │ 0.0 │
6346
7261
  # # └─────┘
6347
7262
  def arccosh
6348
- _from_rbexpr(_rbexpr.arccosh)
7263
+ wrap_expr(_rbexpr.arccosh)
6349
7264
  end
6350
7265
 
6351
7266
  # Compute the element-wise value for the inverse hyperbolic tangent.
@@ -6365,12 +7280,68 @@ module Polars
6365
7280
  # # │ inf │
6366
7281
  # # └─────┘
6367
7282
  def arctanh
6368
- _from_rbexpr(_rbexpr.arctanh)
7283
+ wrap_expr(_rbexpr.arctanh)
7284
+ end
7285
+
7286
+ # Convert from radians to degrees.
7287
+ #
7288
+ # @return [Expr]
7289
+ #
7290
+ # @example
7291
+ # df = Polars::DataFrame.new({"a" => (-4...5).map { |x| x * Math::PI }})
7292
+ # df.select(Polars.col("a").degrees)
7293
+ # # =>
7294
+ # # shape: (9, 1)
7295
+ # # ┌────────┐
7296
+ # # │ a │
7297
+ # # │ --- │
7298
+ # # │ f64 │
7299
+ # # ╞════════╡
7300
+ # # │ -720.0 │
7301
+ # # │ -540.0 │
7302
+ # # │ -360.0 │
7303
+ # # │ -180.0 │
7304
+ # # │ 0.0 │
7305
+ # # │ 180.0 │
7306
+ # # │ 360.0 │
7307
+ # # │ 540.0 │
7308
+ # # │ 720.0 │
7309
+ # # └────────┘
7310
+ def degrees
7311
+ wrap_expr(_rbexpr.degrees)
7312
+ end
7313
+
7314
+ # Convert from degrees to radians.
7315
+ #
7316
+ # @return [Expr]
7317
+ #
7318
+ # @example
7319
+ # df = Polars::DataFrame.new({"a" => [-720, -540, -360, -180, 0, 180, 360, 540, 720]})
7320
+ # df.select(Polars.col("a").radians)
7321
+ # # =>
7322
+ # # shape: (9, 1)
7323
+ # # ┌────────────┐
7324
+ # # │ a │
7325
+ # # │ --- │
7326
+ # # │ f64 │
7327
+ # # ╞════════════╡
7328
+ # # │ -12.566371 │
7329
+ # # │ -9.424778 │
7330
+ # # │ -6.283185 │
7331
+ # # │ -3.141593 │
7332
+ # # │ 0.0 │
7333
+ # # │ 3.141593 │
7334
+ # # │ 6.283185 │
7335
+ # # │ 9.424778 │
7336
+ # # │ 12.566371 │
7337
+ # # └────────────┘
7338
+ def radians
7339
+ wrap_expr(_rbexpr.radians)
6369
7340
  end
6370
7341
 
6371
7342
  # Reshape this Expr to a flat Series or a Series of Lists.
6372
7343
  #
6373
- # @param dims [Array]
7344
+ # @param dimensions [Array]
6374
7345
  # Tuple of the dimension sizes. If a -1 is used in any of the dimensions, that
6375
7346
  # dimension is inferred.
6376
7347
  #
@@ -6410,14 +7381,14 @@ module Polars
6410
7381
  # # │ 8 │
6411
7382
  # # │ 9 │
6412
7383
  # # └─────┘
6413
- def reshape(dims)
6414
- _from_rbexpr(_rbexpr.reshape(dims))
7384
+ def reshape(dimensions)
7385
+ wrap_expr(_rbexpr.reshape(dimensions))
6415
7386
  end
6416
7387
 
6417
7388
  # Shuffle the contents of this expr.
6418
7389
  #
6419
7390
  # @param seed [Integer]
6420
- # Seed for the random number generator. If set to None (default), a random
7391
+ # Seed for the random number generator. If set to nil (default), a random
6421
7392
  # seed is generated using the `random` module.
6422
7393
  #
6423
7394
  # @return [Expr]
@@ -6433,35 +7404,35 @@ module Polars
6433
7404
  # # │ i64 │
6434
7405
  # # ╞═════╡
6435
7406
  # # │ 2 │
6436
- # # │ 1 │
6437
7407
  # # │ 3 │
7408
+ # # │ 1 │
6438
7409
  # # └─────┘
6439
7410
  def shuffle(seed: nil)
6440
7411
  if seed.nil?
6441
7412
  seed = rand(10000)
6442
7413
  end
6443
- _from_rbexpr(_rbexpr.shuffle(seed))
7414
+ wrap_expr(_rbexpr.shuffle(seed))
6444
7415
  end
6445
7416
 
6446
7417
  # Sample from this expression.
6447
7418
  #
6448
- # @param frac [Float]
7419
+ # @param fraction [Float]
6449
7420
  # Fraction of items to return. Cannot be used with `n`.
6450
7421
  # @param with_replacement [Boolean]
6451
7422
  # Allow values to be sampled more than once.
6452
7423
  # @param shuffle [Boolean]
6453
7424
  # Shuffle the order of sampled data points.
6454
7425
  # @param seed [Integer]
6455
- # Seed for the random number generator. If set to None (default), a random
7426
+ # Seed for the random number generator. If set to nil (default), a random
6456
7427
  # seed is used.
6457
7428
  # @param n [Integer]
6458
- # Number of items to return. Cannot be used with `frac`.
7429
+ # Number of items to return. Cannot be used with `fraction`.
6459
7430
  #
6460
7431
  # @return [Expr]
6461
7432
  #
6462
7433
  # @example
6463
7434
  # df = Polars::DataFrame.new({"a" => [1, 2, 3]})
6464
- # df.select(Polars.col("a").sample(frac: 1.0, with_replacement: true, seed: 1))
7435
+ # df.select(Polars.col("a").sample(fraction: 1.0, with_replacement: true, seed: 1))
6465
7436
  # # =>
6466
7437
  # # shape: (3, 1)
6467
7438
  # # ┌─────┐
@@ -6470,31 +7441,31 @@ module Polars
6470
7441
  # # │ i64 │
6471
7442
  # # ╞═════╡
6472
7443
  # # │ 3 │
6473
- # # │ 1
7444
+ # # │ 3
6474
7445
  # # │ 1 │
6475
7446
  # # └─────┘
6476
7447
  def sample(
6477
- frac: nil,
6478
- with_replacement: true,
7448
+ fraction: nil,
7449
+ with_replacement: false,
6479
7450
  shuffle: false,
6480
7451
  seed: nil,
6481
7452
  n: nil
6482
7453
  )
6483
- if !n.nil? && !frac.nil?
6484
- raise ArgumentError, "cannot specify both `n` and `frac`"
7454
+ if !n.nil? && !fraction.nil?
7455
+ raise ArgumentError, "cannot specify both `n` and `fraction`"
6485
7456
  end
6486
7457
 
6487
- if !n.nil? && frac.nil?
7458
+ if !n.nil? && fraction.nil?
6488
7459
  n = Utils.parse_into_expression(n)
6489
- return _from_rbexpr(_rbexpr.sample_n(n, with_replacement, shuffle, seed))
7460
+ return wrap_expr(_rbexpr.sample_n(n, with_replacement, shuffle, seed))
6490
7461
  end
6491
7462
 
6492
- if frac.nil?
6493
- frac = 1.0
7463
+ if fraction.nil?
7464
+ fraction = 1.0
6494
7465
  end
6495
- frac = Utils.parse_into_expression(frac)
6496
- _from_rbexpr(
6497
- _rbexpr.sample_frac(frac, with_replacement, shuffle, seed)
7466
+ fraction = Utils.parse_into_expression(fraction)
7467
+ wrap_expr(
7468
+ _rbexpr.sample_frac(fraction, with_replacement, shuffle, seed)
6498
7469
  )
6499
7470
  end
6500
7471
 
@@ -6522,11 +7493,80 @@ module Polars
6522
7493
  half_life: nil,
6523
7494
  alpha: nil,
6524
7495
  adjust: true,
6525
- min_periods: 1,
6526
- ignore_nulls: true
7496
+ min_samples: 1,
7497
+ ignore_nulls: false
6527
7498
  )
6528
7499
  alpha = _prepare_alpha(com, span, half_life, alpha)
6529
- _from_rbexpr(_rbexpr.ewm_mean(alpha, adjust, min_periods, ignore_nulls))
7500
+ wrap_expr(_rbexpr.ewm_mean(alpha, adjust, min_samples, ignore_nulls))
7501
+ end
7502
+
7503
+ # Compute time-based exponentially weighted moving average.
7504
+ #
7505
+ # @param by [Object]
7506
+ # Times to calculate average by. Should be `DateTime`, `Date`, `UInt64`,
7507
+ # `UInt32`, `Int64`, or `Int32` data type.
7508
+ # @param half_life [Object]
7509
+ # Unit over which observation decays to half its value.
7510
+ #
7511
+ # Can be created either from a timedelta, or
7512
+ # by using the following string language:
7513
+ #
7514
+ # - 1ns (1 nanosecond)
7515
+ # - 1us (1 microsecond)
7516
+ # - 1ms (1 millisecond)
7517
+ # - 1s (1 second)
7518
+ # - 1m (1 minute)
7519
+ # - 1h (1 hour)
7520
+ # - 1d (1 day)
7521
+ # - 1w (1 week)
7522
+ # - 1i (1 index count)
7523
+ #
7524
+ # Or combine them:
7525
+ # "3d12h4m25s" # 3 days, 12 hours, 4 minutes, and 25 seconds
7526
+ #
7527
+ # Note that `half_life` is treated as a constant duration - calendar
7528
+ # durations such as months (or even days in the time-zone-aware case)
7529
+ # are not supported, please express your duration in an approximately
7530
+ # equivalent number of hours (e.g. '370h' instead of '1mo').
7531
+ #
7532
+ # @return [Expr]
7533
+ #
7534
+ # @example
7535
+ # df = Polars::DataFrame.new(
7536
+ # {
7537
+ # "values": [0, 1, 2, nil, 4],
7538
+ # "times": [
7539
+ # Date.new(2020, 1, 1),
7540
+ # Date.new(2020, 1, 3),
7541
+ # Date.new(2020, 1, 10),
7542
+ # Date.new(2020, 1, 15),
7543
+ # Date.new(2020, 1, 17)
7544
+ # ]
7545
+ # }
7546
+ # ).sort("times")
7547
+ # df.with_columns(
7548
+ # result: Polars.col("values").ewm_mean_by("times", half_life: "4d")
7549
+ # )
7550
+ # # =>
7551
+ # # shape: (5, 3)
7552
+ # # ┌────────┬────────────┬──────────┐
7553
+ # # │ values ┆ times ┆ result │
7554
+ # # │ --- ┆ --- ┆ --- │
7555
+ # # │ i64 ┆ date ┆ f64 │
7556
+ # # ╞════════╪════════════╪══════════╡
7557
+ # # │ 0 ┆ 2020-01-01 ┆ 0.0 │
7558
+ # # │ 1 ┆ 2020-01-03 ┆ 0.292893 │
7559
+ # # │ 2 ┆ 2020-01-10 ┆ 1.492474 │
7560
+ # # │ null ┆ 2020-01-15 ┆ null │
7561
+ # # │ 4 ┆ 2020-01-17 ┆ 3.254508 │
7562
+ # # └────────┴────────────┴──────────┘
7563
+ def ewm_mean_by(
7564
+ by,
7565
+ half_life:
7566
+ )
7567
+ by = Utils.parse_into_expression(by)
7568
+ half_life = Utils.parse_as_duration_string(half_life)
7569
+ wrap_expr(_rbexpr.ewm_mean_by(by, half_life))
6530
7570
  end
6531
7571
 
6532
7572
  # Exponentially-weighted moving standard deviation.
@@ -6554,11 +7594,11 @@ module Polars
6554
7594
  alpha: nil,
6555
7595
  adjust: true,
6556
7596
  bias: false,
6557
- min_periods: 1,
6558
- ignore_nulls: true
7597
+ min_samples: 1,
7598
+ ignore_nulls: false
6559
7599
  )
6560
7600
  alpha = _prepare_alpha(com, span, half_life, alpha)
6561
- _from_rbexpr(_rbexpr.ewm_std(alpha, adjust, bias, min_periods, ignore_nulls))
7601
+ wrap_expr(_rbexpr.ewm_std(alpha, adjust, bias, min_samples, ignore_nulls))
6562
7602
  end
6563
7603
 
6564
7604
  # Exponentially-weighted moving variance.
@@ -6586,11 +7626,11 @@ module Polars
6586
7626
  alpha: nil,
6587
7627
  adjust: true,
6588
7628
  bias: false,
6589
- min_periods: 1,
6590
- ignore_nulls: true
7629
+ min_samples: 1,
7630
+ ignore_nulls: false
6591
7631
  )
6592
7632
  alpha = _prepare_alpha(com, span, half_life, alpha)
6593
- _from_rbexpr(_rbexpr.ewm_var(alpha, adjust, bias, min_periods, ignore_nulls))
7633
+ wrap_expr(_rbexpr.ewm_var(alpha, adjust, bias, min_samples, ignore_nulls))
6594
7634
  end
6595
7635
 
6596
7636
  # Extend the Series with given number of values.
@@ -6620,7 +7660,9 @@ module Polars
6620
7660
  # # │ 99 │
6621
7661
  # # └────────┘
6622
7662
  def extend_constant(value, n)
6623
- _from_rbexpr(_rbexpr.extend_constant(value, n))
7663
+ value = Utils.parse_into_expression(value, str_as_lit: true)
7664
+ n = Utils.parse_into_expression(n)
7665
+ wrap_expr(_rbexpr.extend_constant(value, n))
6624
7666
  end
6625
7667
 
6626
7668
  # Count all unique values and create a struct mapping value to count.
@@ -6674,7 +7716,7 @@ module Polars
6674
7716
  name = "count"
6675
7717
  end
6676
7718
  end
6677
- _from_rbexpr(
7719
+ wrap_expr(
6678
7720
  _rbexpr.value_counts(sort, parallel, name, normalize)
6679
7721
  )
6680
7722
  end
@@ -6709,7 +7751,7 @@ module Polars
6709
7751
  # # │ 3 │
6710
7752
  # # └─────┘
6711
7753
  def unique_counts
6712
- _from_rbexpr(_rbexpr.unique_counts)
7754
+ wrap_expr(_rbexpr.unique_counts)
6713
7755
  end
6714
7756
 
6715
7757
  # Compute the logarithm to a given base.
@@ -6734,7 +7776,32 @@ module Polars
6734
7776
  # # │ 1.584963 │
6735
7777
  # # └──────────┘
6736
7778
  def log(base = Math::E)
6737
- _from_rbexpr(_rbexpr.log(base))
7779
+ base_rbexpr = Utils.parse_into_expression(base)
7780
+ wrap_expr(_rbexpr.log(base_rbexpr))
7781
+ end
7782
+
7783
+ # Compute the natural logarithm of each element plus one.
7784
+ #
7785
+ # This computes `log(1 + x)` but is more numerically stable for `x` close to zero.
7786
+ #
7787
+ # @return [Expr]
7788
+ #
7789
+ # @example
7790
+ # df = Polars::DataFrame.new({"a" => [1, 2, 3]})
7791
+ # df.select(Polars.col("a").log1p)
7792
+ # # =>
7793
+ # # shape: (3, 1)
7794
+ # # ┌──────────┐
7795
+ # # │ a │
7796
+ # # │ --- │
7797
+ # # │ f64 │
7798
+ # # ╞══════════╡
7799
+ # # │ 0.693147 │
7800
+ # # │ 1.098612 │
7801
+ # # │ 1.386294 │
7802
+ # # └──────────┘
7803
+ def log1p
7804
+ wrap_expr(_rbexpr.log1p)
6738
7805
  end
6739
7806
 
6740
7807
  # Computes the entropy.
@@ -6742,7 +7809,7 @@ module Polars
6742
7809
  # Uses the formula `-sum(pk * log(pk)` where `pk` are discrete probabilities.
6743
7810
  #
6744
7811
  # @param base [Float]
6745
- # Given base, defaults to `e`.
7812
+ # Given base, defaults to `2`.
6746
7813
  # @param normalize [Boolean]
6747
7814
  # Normalize pk if it doesn't sum to 1.
6748
7815
  #
@@ -6772,20 +7839,17 @@ module Polars
6772
7839
  # # ╞═══════════╡
6773
7840
  # # │ -6.754888 │
6774
7841
  # # └───────────┘
6775
- def entropy(base: 2, normalize: true)
6776
- _from_rbexpr(_rbexpr.entropy(base, normalize))
7842
+ def entropy(base: Math::E, normalize: true)
7843
+ wrap_expr(_rbexpr.entropy(base, normalize))
6777
7844
  end
6778
7845
 
6779
7846
  # Run an expression over a sliding window that increases `1` slot every iteration.
6780
7847
  #
6781
7848
  # @param expr [Expr]
6782
7849
  # Expression to evaluate
6783
- # @param min_periods [Integer]
7850
+ # @param min_samples [Integer]
6784
7851
  # Number of valid values there should be in the window before the expression
6785
7852
  # is evaluated. valid values = `length - null_count`
6786
- # @param parallel [Boolean]
6787
- # Run in parallel. Don't do this in a group by or another operation that
6788
- # already has much parallelization.
6789
7853
  #
6790
7854
  # @return [Expr]
6791
7855
  #
@@ -6819,9 +7883,9 @@ module Polars
6819
7883
  # # │ -15 │
6820
7884
  # # │ -24 │
6821
7885
  # # └────────┘
6822
- def cumulative_eval(expr, min_periods: 1, parallel: false)
6823
- _from_rbexpr(
6824
- _rbexpr.cumulative_eval(expr._rbexpr, min_periods, parallel)
7886
+ def cumulative_eval(expr, min_samples: 1)
7887
+ wrap_expr(
7888
+ _rbexpr.cumulative_eval(expr._rbexpr, min_samples)
6825
7889
  )
6826
7890
  end
6827
7891
 
@@ -6831,6 +7895,8 @@ module Polars
6831
7895
  #
6832
7896
  # @param descending [Boolean]
6833
7897
  # Whether the `Series` order is descending.
7898
+ # @param nulls_last [Boolean]
7899
+ # Whether the nulls are at the end.
6834
7900
  #
6835
7901
  # @return [Expr]
6836
7902
  #
@@ -6850,12 +7916,16 @@ module Polars
6850
7916
  # # ╞════════╡
6851
7917
  # # │ 3 │
6852
7918
  # # └────────┘
6853
- def set_sorted(descending: false)
6854
- _from_rbexpr(_rbexpr.set_sorted_flag(descending))
7919
+ def set_sorted(descending: false, nulls_last: false)
7920
+ wrap_expr(_rbexpr.set_sorted_flag(descending, nulls_last))
6855
7921
  end
6856
7922
 
6857
7923
  # Aggregate to list.
6858
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
+ #
6859
7929
  # @return [Expr]
6860
7930
  #
6861
7931
  # @example
@@ -6875,55 +7945,85 @@ module Polars
6875
7945
  # # ╞═══════════╪═══════════╡
6876
7946
  # # │ [1, 2, 3] ┆ [4, 5, 6] │
6877
7947
  # # └───────────┴───────────┘
6878
- def implode
6879
- _from_rbexpr(_rbexpr.implode)
7948
+ def implode(maintain_order: true)
7949
+ wrap_expr(_rbexpr.implode(maintain_order))
6880
7950
  end
6881
7951
 
6882
- # Shrink numeric columns to the minimal required datatype.
7952
+ # Bin values into buckets and count their occurrences.
7953
+ #
7954
+ # @note
7955
+ # This functionality is considered **unstable**. It may be changed
7956
+ # at any point without it being considered a breaking change.
6883
7957
  #
6884
- # Shrink to the dtype needed to fit the extrema of this `Series`.
6885
- # This can be used to reduce memory pressure.
7958
+ # @param bins [Object]
7959
+ # Bin edges. If nil given, we determine the edges based on the data.
7960
+ # @param bin_count [Integer]
7961
+ # If `bins` is not provided, `bin_count` uniform bins are created that fully
7962
+ # encompass the data.
7963
+ # @param include_category [Boolean]
7964
+ # Include a column that shows the intervals as categories.
7965
+ # @param include_breakpoint [Boolean]
7966
+ # Include a column that indicates the upper breakpoint.
6886
7967
  #
6887
7968
  # @return [Expr]
6888
7969
  #
6889
7970
  # @example
6890
- # Polars::DataFrame.new(
6891
- # {
6892
- # "a" => [1, 2, 3],
6893
- # "b" => [1, 2, 2 << 32],
6894
- # "c" => [-1, 2, 1 << 30],
6895
- # "d" => [-112, 2, 112],
6896
- # "e" => [-112, 2, 129],
6897
- # "f" => ["a", "b", "c"],
6898
- # "g" => [0.1, 1.32, 0.12],
6899
- # "h" => [true, nil, false]
6900
- # }
6901
- # ).select(Polars.all.shrink_dtype)
6902
- # # =>
6903
- # # shape: (3, 8)
6904
- # # ┌─────┬────────────┬────────────┬──────┬──────┬─────┬──────┬───────┐
6905
- # # │ a ┆ b ┆ c ┆ d ┆ e ┆ f ┆ g ┆ h │
6906
- # # --- --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
6907
- # # │ i8 ┆ i64 ┆ i32 ┆ i8 ┆ i16 ┆ str ┆ f32 ┆ bool │
6908
- # # ╞═════╪════════════╪════════════╪══════╪══════╪═════╪══════╪═══════╡
6909
- # # │ 1 ┆ 1 ┆ -1 ┆ -112 ┆ -112 ┆ a ┆ 0.1 ┆ true │
6910
- # # 2 2 ┆ 2 ┆ 2 ┆ 2 ┆ b ┆ 1.32 ┆ null │
6911
- # # │ 3 ┆ 8589934592 ┆ 1073741824 ┆ 112 ┆ 129 ┆ c ┆ 0.12 ┆ false │
6912
- # # └─────┴────────────┴────────────┴──────┴──────┴─────┴──────┴───────┘
6913
- def shrink_dtype
6914
- _from_rbexpr(_rbexpr.shrink_dtype)
7971
+ # df = Polars::DataFrame.new({"a" => [1, 3, 8, 8, 2, 1, 3]})
7972
+ # df.select(Polars.col("a").hist(bins: [1, 2, 3]))
7973
+ # # =>
7974
+ # # shape: (2, 1)
7975
+ # # ┌─────┐
7976
+ # # a │
7977
+ # # ---
7978
+ # # u32
7979
+ # # ╞═════╡
7980
+ # # 3 │
7981
+ # # │ 2 │
7982
+ # # └─────┘
7983
+ #
7984
+ # @example
7985
+ # df.select(
7986
+ # Polars.col("a").hist(
7987
+ # bins: [1, 2, 3], include_breakpoint: true, include_category: true
7988
+ # )
7989
+ # )
7990
+ # # =>
7991
+ # # shape: (2, 1)
7992
+ # # ┌──────────────────────┐
7993
+ # # │ a │
7994
+ # # │ --- │
7995
+ # # │ struct[3] │
7996
+ # # ╞══════════════════════╡
7997
+ # # │ {2.0,"[1.0, 2.0]",3} │
7998
+ # # │ {3.0,"(2.0, 3.0]",2} │
7999
+ # # └──────────────────────┘
8000
+ def hist(
8001
+ bins: nil,
8002
+ bin_count: nil,
8003
+ include_category: false,
8004
+ include_breakpoint: false
8005
+ )
8006
+ if !bins.nil?
8007
+ if bins.is_a?(::Array)
8008
+ bins = Polars::Series.new(bins)
8009
+ end
8010
+ bins = Utils.parse_into_expression(bins)
8011
+ end
8012
+ wrap_expr(
8013
+ _rbexpr.hist(bins, bin_count, include_category, include_breakpoint)
8014
+ )
6915
8015
  end
6916
8016
 
6917
8017
  # Replace values by different values.
6918
8018
  #
6919
8019
  # @param old [Object]
6920
- # Value or sequence of values to replace.
6921
- # Accepts expression input. Sequences are parsed as Series,
8020
+ # Value or array of values to replace.
8021
+ # Accepts expression input. Arrays are parsed as Series,
6922
8022
  # other non-expression inputs are parsed as literals.
6923
8023
  # Also accepts a mapping of values to their replacement.
6924
8024
  # @param new [Object]
6925
- # Value or sequence of values to replace by.
6926
- # Accepts expression input. Sequences are parsed as Series,
8025
+ # Value or array of values to replace by.
8026
+ # Accepts expression input. Arrays are parsed as Series,
6927
8027
  # other non-expression inputs are parsed as literals.
6928
8028
  # Length must match the length of `old` or have length 1.
6929
8029
  # @param default [Object]
@@ -6952,7 +8052,7 @@ module Polars
6952
8052
  # # │ 3 ┆ 3 │
6953
8053
  # # └─────┴──────────┘
6954
8054
  #
6955
- # @example Replace multiple values by passing sequences to the `old` and `new` parameters.
8055
+ # @example Replace multiple values by passing arrays to the `old` and `new` parameters.
6956
8056
  # df.with_columns(replaced: Polars.col("a").replace([2, 3], [100, 200]))
6957
8057
  # # =>
6958
8058
  # # shape: (4, 2)
@@ -7070,7 +8170,7 @@ module Polars
7070
8170
  old = Utils.parse_into_expression(old, str_as_lit: true)
7071
8171
  new = Utils.parse_into_expression(new, str_as_lit: true)
7072
8172
 
7073
- result = _from_rbexpr(_rbexpr.replace(old, new))
8173
+ result = wrap_expr(_rbexpr.replace(old, new))
7074
8174
 
7075
8175
  if !return_dtype.nil?
7076
8176
  result = result.cast(return_dtype)
@@ -7082,14 +8182,14 @@ module Polars
7082
8182
  # Replace all values by different values.
7083
8183
  #
7084
8184
  # @param old [Object]
7085
- # Value or sequence of values to replace.
7086
- # Accepts expression input. Sequences are parsed as Series,
8185
+ # Value or array of values to replace.
8186
+ # Accepts expression input. Arrays are parsed as Series,
7087
8187
  # other non-expression inputs are parsed as literals.
7088
8188
  # Also accepts a mapping of values to their replacement as syntactic sugar for
7089
- # `replace_all(old: Series.new(mapping.keys), new: Serie.new(mapping.values))`.
8189
+ # `replace_all(old: Series.new(mapping.keys), new: Series.new(mapping.values))`.
7090
8190
  # @param new [Object]
7091
- # Value or sequence of values to replace by.
7092
- # Accepts expression input. Sequences are parsed as Series,
8191
+ # Value or array of values to replace by.
8192
+ # Accepts expression input. Arrays are parsed as Series,
7093
8193
  # other non-expression inputs are parsed as literals.
7094
8194
  # Length must match the length of `old` or have length 1.
7095
8195
  # @param default [Object]
@@ -7105,7 +8205,7 @@ module Polars
7105
8205
  # @note
7106
8206
  # The global string cache must be enabled when replacing categorical values.
7107
8207
  #
7108
- # @example Replace values by passing sequences to the `old` and `new` parameters.
8208
+ # @example Replace values by passing arrays to the `old` and `new` parameters.
7109
8209
  # df = Polars::DataFrame.new({"a" => [1, 2, 2, 3]})
7110
8210
  # df.with_columns(
7111
8211
  # replaced: Polars.col("a").replace_strict([1, 2, 3], [100, 200, 300])
@@ -7222,11 +8322,161 @@ module Polars
7222
8322
 
7223
8323
  default = default.eql?(NO_DEFAULT) ? nil : Utils.parse_into_expression(default, str_as_lit: true)
7224
8324
 
7225
- _from_rbexpr(
8325
+ wrap_expr(
7226
8326
  _rbexpr.replace_strict(old, new, default, return_dtype)
7227
8327
  )
7228
8328
  end
7229
8329
 
8330
+ # Evaluate the number of set bits.
8331
+ #
8332
+ # @return [Expr]
8333
+ def bitwise_count_ones
8334
+ wrap_expr(_rbexpr.bitwise_count_ones)
8335
+ end
8336
+
8337
+ # Evaluate the number of unset bits.
8338
+ #
8339
+ # @return [Expr]
8340
+ def bitwise_count_zeros
8341
+ wrap_expr(_rbexpr.bitwise_count_zeros)
8342
+ end
8343
+
8344
+ # Evaluate the number most-significant set bits before seeing an unset bit.
8345
+ #
8346
+ # @return [Expr]
8347
+ def bitwise_leading_ones
8348
+ wrap_expr(_rbexpr.bitwise_leading_ones)
8349
+ end
8350
+
8351
+ # Evaluate the number most-significant unset bits before seeing a set bit.
8352
+ #
8353
+ # @return [Expr]
8354
+ def bitwise_leading_zeros
8355
+ wrap_expr(_rbexpr.bitwise_leading_zeros)
8356
+ end
8357
+
8358
+ # Evaluate the number least-significant set bits before seeing an unset bit.
8359
+ #
8360
+ # @return [Expr]
8361
+ def bitwise_trailing_ones
8362
+ wrap_expr(_rbexpr.bitwise_trailing_ones)
8363
+ end
8364
+
8365
+ # Evaluate the number least-significant unset bits before seeing a set bit.
8366
+ #
8367
+ # @return [Expr]
8368
+ def bitwise_trailing_zeros
8369
+ wrap_expr(_rbexpr.bitwise_trailing_zeros)
8370
+ end
8371
+
8372
+ # Perform an aggregation of bitwise ANDs.
8373
+ #
8374
+ # @return [Expr]
8375
+ #
8376
+ # @example
8377
+ # df = Polars::DataFrame.new({"n" => [-1, 0, 1]})
8378
+ # df.select(Polars.col("n").bitwise_and)
8379
+ # # =>
8380
+ # # shape: (1, 1)
8381
+ # # ┌─────┐
8382
+ # # │ n │
8383
+ # # │ --- │
8384
+ # # │ i64 │
8385
+ # # ╞═════╡
8386
+ # # │ 0 │
8387
+ # # └─────┘
8388
+ #
8389
+ # @example
8390
+ # df = Polars::DataFrame.new(
8391
+ # {"grouper" => ["a", "a", "a", "b", "b"], "n" => [-1, 0, 1, -1, 1]}
8392
+ # )
8393
+ # df.group_by("grouper", maintain_order: true).agg(Polars.col("n").bitwise_and)
8394
+ # # =>
8395
+ # # shape: (2, 2)
8396
+ # # ┌─────────┬─────┐
8397
+ # # │ grouper ┆ n │
8398
+ # # │ --- ┆ --- │
8399
+ # # │ str ┆ i64 │
8400
+ # # ╞═════════╪═════╡
8401
+ # # │ a ┆ 0 │
8402
+ # # │ b ┆ 1 │
8403
+ # # └─────────┴─────┘
8404
+ def bitwise_and
8405
+ wrap_expr(_rbexpr.bitwise_and)
8406
+ end
8407
+
8408
+ # Perform an aggregation of bitwise ORs.
8409
+ #
8410
+ # @return [Expr]
8411
+ #
8412
+ # @example
8413
+ # df = Polars::DataFrame.new({"n" => [-1, 0, 1]})
8414
+ # df.select(Polars.col("n").bitwise_or)
8415
+ # # =>
8416
+ # # shape: (1, 1)
8417
+ # # ┌─────┐
8418
+ # # │ n │
8419
+ # # │ --- │
8420
+ # # │ i64 │
8421
+ # # ╞═════╡
8422
+ # # │ -1 │
8423
+ # # └─────┘
8424
+ #
8425
+ # @example
8426
+ # df = Polars::DataFrame.new(
8427
+ # {"grouper" => ["a", "a", "a", "b", "b"], "n" => [-1, 0, 1, -1, 1]}
8428
+ # )
8429
+ # df.group_by("grouper", maintain_order: true).agg(Polars.col("n").bitwise_or)
8430
+ # # =>
8431
+ # # shape: (2, 2)
8432
+ # # ┌─────────┬─────┐
8433
+ # # │ grouper ┆ n │
8434
+ # # │ --- ┆ --- │
8435
+ # # │ str ┆ i64 │
8436
+ # # ╞═════════╪═════╡
8437
+ # # │ a ┆ -1 │
8438
+ # # │ b ┆ -1 │
8439
+ # # └─────────┴─────┘
8440
+ def bitwise_or
8441
+ wrap_expr(_rbexpr.bitwise_or)
8442
+ end
8443
+
8444
+ # Perform an aggregation of bitwise XORs.
8445
+ #
8446
+ # @return [Expr]
8447
+ #
8448
+ # @example
8449
+ # df = Polars::DataFrame.new({"n" => [-1, 0, 1]})
8450
+ # df.select(Polars.col("n").bitwise_xor)
8451
+ # # =>
8452
+ # # shape: (1, 1)
8453
+ # # ┌─────┐
8454
+ # # │ n │
8455
+ # # │ --- │
8456
+ # # │ i64 │
8457
+ # # ╞═════╡
8458
+ # # │ -2 │
8459
+ # # └─────┘
8460
+ #
8461
+ # @example
8462
+ # df = Polars::DataFrame.new(
8463
+ # {"grouper" => ["a", "a", "a", "b", "b"], "n" => [-1, 0, 1, -1, 1]}
8464
+ # )
8465
+ # df.group_by("grouper", maintain_order: true).agg(Polars.col("n").bitwise_xor)
8466
+ # # =>
8467
+ # # shape: (2, 2)
8468
+ # # ┌─────────┬─────┐
8469
+ # # │ grouper ┆ n │
8470
+ # # │ --- ┆ --- │
8471
+ # # │ str ┆ i64 │
8472
+ # # ╞═════════╪═════╡
8473
+ # # │ a ┆ -2 │
8474
+ # # │ b ┆ -2 │
8475
+ # # └─────────┴─────┘
8476
+ def bitwise_xor
8477
+ wrap_expr(_rbexpr.bitwise_xor)
8478
+ end
8479
+
7230
8480
  # Create an object namespace of all list related methods.
7231
8481
  #
7232
8482
  # @return [ListExpr]
@@ -7290,9 +8540,16 @@ module Polars
7290
8540
  StructExpr.new(self)
7291
8541
  end
7292
8542
 
8543
+ # Create an object namespace of all extension type related expressions.
8544
+ #
8545
+ # @return [ExtensionExpr]
8546
+ def ext
8547
+ ExtensionExpr.new(self)
8548
+ end
8549
+
7293
8550
  private
7294
8551
 
7295
- def _from_rbexpr(expr)
8552
+ def wrap_expr(expr)
7296
8553
  Utils.wrap_expr(expr)
7297
8554
  end
7298
8555
 
@@ -7337,19 +8594,6 @@ module Polars
7337
8594
  alpha
7338
8595
  end
7339
8596
 
7340
- def _prepare_rolling_window_args(window_size, min_periods)
7341
- if window_size.is_a?(Integer)
7342
- if min_periods.nil?
7343
- min_periods = window_size
7344
- end
7345
- window_size = "#{window_size}i"
7346
- end
7347
- if min_periods.nil?
7348
- min_periods = 1
7349
- end
7350
- [window_size, min_periods]
7351
- end
7352
-
7353
8597
  def _prepare_rolling_by_window_args(window_size)
7354
8598
  window_size
7355
8599
  end