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
@@ -6,6 +6,45 @@ module Polars
6
6
  @lgb = lgb
7
7
  end
8
8
 
9
+ # Filter groups with a list of predicates after aggregation.
10
+ #
11
+ # Using this method is equivalent to adding the predicates to the aggregation and
12
+ # filtering afterwards.
13
+ #
14
+ # This method can be chained and all conditions will be combined using `&`.
15
+ #
16
+ # @param predicates [Array]
17
+ # Expressions that evaluate to a boolean value for each group. Typically, this
18
+ # requires the use of an aggregation function. Multiple predicates are
19
+ # combined using `&`.
20
+ #
21
+ # @return [LazyGroupBy]
22
+ #
23
+ # @example Only keep groups that contain more than one element.
24
+ # ldf = Polars::DataFrame.new(
25
+ # {
26
+ # "a" => ["a", "b", "a", "b", "c"]
27
+ # }
28
+ # ).lazy
29
+ # ldf.group_by("a").having(
30
+ # Polars.len > 1
31
+ # ).agg.collect
32
+ # # =>
33
+ # # shape: (2, 1)
34
+ # # ┌─────┐
35
+ # # │ a │
36
+ # # │ --- │
37
+ # # │ str │
38
+ # # ╞═════╡
39
+ # # │ b │
40
+ # # │ a │
41
+ # # └─────┘
42
+ def having(*predicates)
43
+ rbexprs = Utils.parse_into_list_of_expressions(*predicates)
44
+ @lgb = @lgb.having(rbexprs)
45
+ self
46
+ end
47
+
9
48
  # Compute aggregations for each group of a group by operation.
10
49
  #
11
50
  # @param aggs [Array]
@@ -111,6 +150,65 @@ module Polars
111
150
  Utils.wrap_ldf(@lgb.agg(rbexprs))
112
151
  end
113
152
 
153
+ # Apply a custom/user-defined function (UDF) over the groups as a new DataFrame.
154
+ #
155
+ # @note
156
+ # This method is much slower than the native expressions API.
157
+ # Only use it if you cannot implement your logic otherwise.
158
+ #
159
+ # Using this is considered an anti-pattern as it will be very slow because:
160
+ #
161
+ # - it forces the engine to materialize the whole `DataFrames` for the groups.
162
+ # - it is not parallelized
163
+ # - it blocks optimizations as the passed python function is opaque to the
164
+ # optimizer
165
+ #
166
+ # The idiomatic way to apply custom functions over multiple columns is using:
167
+ #
168
+ # `Polars.struct([my_columns]).apply { |struct_series| ... }`
169
+ #
170
+ # @param schema [Object]
171
+ # Schema of the output function. This has to be known statically. If the
172
+ # given schema is incorrect, this is a bug in the caller's query and may
173
+ # lead to errors. If set to None, polars assumes the schema is unchanged.
174
+ #
175
+ # @return [LazyFrame]
176
+ #
177
+ # @example
178
+ # df = Polars::DataFrame.new(
179
+ # {
180
+ # "id" => [0, 1, 2, 3, 4],
181
+ # "color" => ["red", "green", "green", "red", "red"],
182
+ # "shape" => ["square", "triangle", "square", "triangle", "square"]
183
+ # }
184
+ # )
185
+ # (
186
+ # df.lazy
187
+ # .group_by("color")
188
+ # .map_groups(nil) { |group_df| group_df.sample(n: 2) }
189
+ # .collect
190
+ # )
191
+ # # =>
192
+ # # shape: (4, 3)
193
+ # # ┌─────┬───────┬──────────┐
194
+ # # │ id ┆ color ┆ shape │
195
+ # # │ --- ┆ --- ┆ --- │
196
+ # # │ i64 ┆ str ┆ str │
197
+ # # ╞═════╪═══════╪══════════╡
198
+ # # │ 1 ┆ green ┆ triangle │
199
+ # # │ 2 ┆ green ┆ square │
200
+ # # │ 4 ┆ red ┆ square │
201
+ # # │ 3 ┆ red ┆ triangle │
202
+ # # └─────┴───────┴──────────┘
203
+ def map_groups(
204
+ schema,
205
+ &function
206
+ )
207
+ Utils.wrap_ldf(
208
+ @lgb.map_groups(->(df) { function.(Utils.wrap_df(df))._df }, schema)
209
+ )
210
+ end
211
+
114
212
  # Get the first `n` rows of each group.
115
213
  #
116
214
  # @param n [Integer]
@@ -175,7 +273,343 @@ module Polars
175
273
  Utils.wrap_ldf(@lgb.tail(n))
176
274
  end
177
275
 
178
- # def apply
179
- # end
276
+ # Aggregate the groups into Series.
277
+ #
278
+ # @return [LazyFrame]
279
+ #
280
+ # @example
281
+ # ldf = Polars::DataFrame.new(
282
+ # {
283
+ # "a" => ["one", "two", "one", "two"],
284
+ # "b" => [1, 2, 3, 4]
285
+ # }
286
+ # ).lazy
287
+ # ldf.group_by("a", maintain_order: true).all.collect
288
+ # # =>
289
+ # # shape: (2, 2)
290
+ # # ┌─────┬───────────┐
291
+ # # │ a ┆ b │
292
+ # # │ --- ┆ --- │
293
+ # # │ str ┆ list[i64] │
294
+ # # ╞═════╪═══════════╡
295
+ # # │ one ┆ [1, 3] │
296
+ # # │ two ┆ [2, 4] │
297
+ # # └─────┴───────────┘
298
+ def all
299
+ agg(F.all)
300
+ end
301
+
302
+ # Return the number of rows in each group.
303
+ #
304
+ # @param name [String]
305
+ # Assign a name to the resulting column; if unset, defaults to "len".
306
+ #
307
+ # @return [LazyFrame]
308
+ #
309
+ # @example
310
+ # lf = Polars::LazyFrame.new({"a" => ["Apple", "Apple", "Orange"], "b" => [1, nil, 2]})
311
+ # lf.group_by("a").len.collect
312
+ # # =>
313
+ # # shape: (2, 2)
314
+ # # ┌────────┬─────┐
315
+ # # │ a ┆ len │
316
+ # # │ --- ┆ --- │
317
+ # # │ str ┆ u32 │
318
+ # # ╞════════╪═════╡
319
+ # # │ Apple ┆ 2 │
320
+ # # │ Orange ┆ 1 │
321
+ # # └────────┴─────┘
322
+ #
323
+ # @example
324
+ # lf.group_by("a").len(name: "n").collect
325
+ # # =>
326
+ # # shape: (2, 2)
327
+ # # ┌────────┬─────┐
328
+ # # │ a ┆ n │
329
+ # # │ --- ┆ --- │
330
+ # # │ str ┆ u32 │
331
+ # # ╞════════╪═════╡
332
+ # # │ Apple ┆ 2 │
333
+ # # │ Orange ┆ 1 │
334
+ # # └────────┴─────┘
335
+ def len(name: nil)
336
+ len_expr = F.len
337
+ if !name.nil?
338
+ len_expr = len_expr.alias(name)
339
+ end
340
+ agg(len_expr)
341
+ end
342
+
343
+ # Aggregate the first values in the group.
344
+ #
345
+ # @param ignore_nulls [Boolean]
346
+ # Ignore null values (default `false`).
347
+ # If set to `true`, the first non-null value for each aggregation is returned,
348
+ # otherwise `nil` is returned if no non-null value exists.
349
+ #
350
+ # @return [LazyFrame]
351
+ #
352
+ # @example
353
+ # ldf = Polars::DataFrame.new(
354
+ # {
355
+ # "a" => [1, 2, 2, 3, 4, 5],
356
+ # "b" => [0.5, 0.5, 4, 10, 13, 14],
357
+ # "c" => [true, true, true, false, false, true],
358
+ # "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
359
+ # }
360
+ # ).lazy
361
+ # ldf.group_by("d", maintain_order: true).first.collect
362
+ # # =>
363
+ # # shape: (3, 4)
364
+ # # ┌────────┬─────┬──────┬───────┐
365
+ # # │ d ┆ a ┆ b ┆ c │
366
+ # # │ --- ┆ --- ┆ --- ┆ --- │
367
+ # # │ str ┆ i64 ┆ f64 ┆ bool │
368
+ # # ╞════════╪═════╪══════╪═══════╡
369
+ # # │ Apple ┆ 1 ┆ 0.5 ┆ true │
370
+ # # │ Orange ┆ 2 ┆ 0.5 ┆ true │
371
+ # # │ Banana ┆ 4 ┆ 13.0 ┆ false │
372
+ # # └────────┴─────┴──────┴───────┘
373
+ def first(ignore_nulls: false)
374
+ agg(F.all.first(ignore_nulls: ignore_nulls))
375
+ end
376
+
377
+ # Aggregate the last values in the group.
378
+ #
379
+ # @param ignore_nulls [Boolean]
380
+ # Ignore null values (default `false`).
381
+ # If set to `true`, the last non-null value for each aggregation is returned,
382
+ # otherwise `nil` is returned if no non-null value exists.
383
+ #
384
+ #
385
+ # @return [LazyFrame]
386
+ #
387
+ # @example
388
+ # ldf = Polars::DataFrame.new(
389
+ # {
390
+ # "a" => [1, 2, 2, 3, 4, 5],
391
+ # "b" => [0.5, 0.5, 4, 10, 14, 13],
392
+ # "c" => [true, true, true, false, false, true],
393
+ # "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
394
+ # }
395
+ # ).lazy
396
+ # ldf.group_by("d", maintain_order: true).last.collect
397
+ # # =>
398
+ # # shape: (3, 4)
399
+ # # ┌────────┬─────┬──────┬───────┐
400
+ # # │ d ┆ a ┆ b ┆ c │
401
+ # # │ --- ┆ --- ┆ --- ┆ --- │
402
+ # # │ str ┆ i64 ┆ f64 ┆ bool │
403
+ # # ╞════════╪═════╪══════╪═══════╡
404
+ # # │ Apple ┆ 3 ┆ 10.0 ┆ false │
405
+ # # │ Orange ┆ 2 ┆ 0.5 ┆ true │
406
+ # # │ Banana ┆ 5 ┆ 13.0 ┆ true │
407
+ # # └────────┴─────┴──────┴───────┘
408
+ def last(ignore_nulls: false)
409
+ agg(F.all.last(ignore_nulls: ignore_nulls))
410
+ end
411
+
412
+ # Reduce the groups to the maximal value.
413
+ #
414
+ # @return [LazyFrame]
415
+ #
416
+ # @example
417
+ # ldf = Polars::DataFrame.new(
418
+ # {
419
+ # "a" => [1, 2, 2, 3, 4, 5],
420
+ # "b" => [0.5, 0.5, 4, 10, 13, 14],
421
+ # "c" => [true, true, true, false, false, true],
422
+ # "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
423
+ # }
424
+ # ).lazy
425
+ # ldf.group_by("d", maintain_order: true).max.collect
426
+ # # =>
427
+ # # shape: (3, 4)
428
+ # # ┌────────┬─────┬──────┬──────┐
429
+ # # │ d ┆ a ┆ b ┆ c │
430
+ # # │ --- ┆ --- ┆ --- ┆ --- │
431
+ # # │ str ┆ i64 ┆ f64 ┆ bool │
432
+ # # ╞════════╪═════╪══════╪══════╡
433
+ # # │ Apple ┆ 3 ┆ 10.0 ┆ true │
434
+ # # │ Orange ┆ 2 ┆ 0.5 ┆ true │
435
+ # # │ Banana ┆ 5 ┆ 14.0 ┆ true │
436
+ # # └────────┴─────┴──────┴──────┘
437
+ def max
438
+ agg(F.all.max)
439
+ end
440
+
441
+ # Reduce the groups to the mean values.
442
+ #
443
+ # @return [LazyFrame]
444
+ #
445
+ # @example
446
+ # ldf = Polars::DataFrame.new(
447
+ # {
448
+ # "a" => [1, 2, 2, 3, 4, 5],
449
+ # "b" => [0.5, 0.5, 4, 10, 13, 14],
450
+ # "c" => [true, true, true, false, false, true],
451
+ # "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
452
+ # }
453
+ # ).lazy
454
+ # ldf.group_by("d", maintain_order: true).mean.collect
455
+ # # =>
456
+ # # shape: (3, 4)
457
+ # # ┌────────┬─────┬──────────┬──────────┐
458
+ # # │ d ┆ a ┆ b ┆ c │
459
+ # # │ --- ┆ --- ┆ --- ┆ --- │
460
+ # # │ str ┆ f64 ┆ f64 ┆ f64 │
461
+ # # ╞════════╪═════╪══════════╪══════════╡
462
+ # # │ Apple ┆ 2.0 ┆ 4.833333 ┆ 0.666667 │
463
+ # # │ Orange ┆ 2.0 ┆ 0.5 ┆ 1.0 │
464
+ # # │ Banana ┆ 4.5 ┆ 13.5 ┆ 0.5 │
465
+ # # └────────┴─────┴──────────┴──────────┘
466
+ def mean
467
+ agg(F.all.mean)
468
+ end
469
+
470
+ # Return the median per group.
471
+ #
472
+ # @return [LazyFrame]
473
+ #
474
+ # @example
475
+ # ldf = Polars::DataFrame.new(
476
+ # {
477
+ # "a" => [1, 2, 2, 3, 4, 5],
478
+ # "b" => [0.5, 0.5, 4, 10, 13, 14],
479
+ # "d" => ["Apple", "Banana", "Apple", "Apple", "Banana", "Banana"]
480
+ # }
481
+ # ).lazy
482
+ # ldf.group_by("d", maintain_order: true).median.collect
483
+ # # =>
484
+ # # shape: (2, 3)
485
+ # # ┌────────┬─────┬──────┐
486
+ # # │ d ┆ a ┆ b │
487
+ # # │ --- ┆ --- ┆ --- │
488
+ # # │ str ┆ f64 ┆ f64 │
489
+ # # ╞════════╪═════╪══════╡
490
+ # # │ Apple ┆ 2.0 ┆ 4.0 │
491
+ # # │ Banana ┆ 4.0 ┆ 13.0 │
492
+ # # └────────┴─────┴──────┘
493
+ def median
494
+ agg(F.all.median)
495
+ end
496
+
497
+ # Reduce the groups to the minimal value.
498
+ #
499
+ # @return [LazyFrame]
500
+ #
501
+ # @example
502
+ # ldf = Polars::DataFrame.new(
503
+ # {
504
+ # "a" => [1, 2, 2, 3, 4, 5],
505
+ # "b" => [0.5, 0.5, 4, 10, 13, 14],
506
+ # "c" => [true, true, true, false, false, true],
507
+ # "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
508
+ # }
509
+ # ).lazy
510
+ # ldf.group_by("d", maintain_order: true).min.collect
511
+ # # =>
512
+ # # shape: (3, 4)
513
+ # # ┌────────┬─────┬──────┬───────┐
514
+ # # │ d ┆ a ┆ b ┆ c │
515
+ # # │ --- ┆ --- ┆ --- ┆ --- │
516
+ # # │ str ┆ i64 ┆ f64 ┆ bool │
517
+ # # ╞════════╪═════╪══════╪═══════╡
518
+ # # │ Apple ┆ 1 ┆ 0.5 ┆ false │
519
+ # # │ Orange ┆ 2 ┆ 0.5 ┆ true │
520
+ # # │ Banana ┆ 4 ┆ 13.0 ┆ false │
521
+ # # └────────┴─────┴──────┴───────┘
522
+ def min
523
+ agg(F.all.min)
524
+ end
525
+
526
+ # Count the unique values per group.
527
+ #
528
+ # @return [LazyFrame]
529
+ #
530
+ # @example
531
+ # ldf = Polars::DataFrame.new(
532
+ # {
533
+ # "a" => [1, 2, 1, 3, 4, 5],
534
+ # "b" => [0.5, 0.5, 0.5, 10, 13, 14],
535
+ # "d" => ["Apple", "Banana", "Apple", "Apple", "Banana", "Banana"]
536
+ # }
537
+ # ).lazy
538
+ # ldf.group_by("d", maintain_order: true).n_unique.collect
539
+ # # =>
540
+ # # shape: (2, 3)
541
+ # # ┌────────┬─────┬─────┐
542
+ # # │ d ┆ a ┆ b │
543
+ # # │ --- ┆ --- ┆ --- │
544
+ # # │ str ┆ u32 ┆ u32 │
545
+ # # ╞════════╪═════╪═════╡
546
+ # # │ Apple ┆ 2 ┆ 2 │
547
+ # # │ Banana ┆ 3 ┆ 3 │
548
+ # # └────────┴─────┴─────┘
549
+ def n_unique
550
+ agg(F.all.n_unique)
551
+ end
552
+
553
+ # Compute the quantile per group.
554
+ #
555
+ # @param quantile [Float]
556
+ # Quantile between 0.0 and 1.0.
557
+ # @param interpolation ['nearest', 'higher', 'lower', 'midpoint', 'linear', 'equiprobable']
558
+ # Interpolation method.
559
+ #
560
+ # @return [LazyFrame]
561
+ #
562
+ # @example
563
+ # ldf = Polars::DataFrame.new(
564
+ # {
565
+ # "a" => [1, 2, 2, 3, 4, 5],
566
+ # "b" => [0.5, 0.5, 4, 10, 13, 14],
567
+ # "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
568
+ # }
569
+ # ).lazy
570
+ # ldf.group_by("d", maintain_order: true).quantile(1).collect
571
+ # # =>
572
+ # # shape: (3, 3)
573
+ # # ┌────────┬─────┬──────┐
574
+ # # │ d ┆ a ┆ b │
575
+ # # │ --- ┆ --- ┆ --- │
576
+ # # │ str ┆ f64 ┆ f64 │
577
+ # # ╞════════╪═════╪══════╡
578
+ # # │ Apple ┆ 3.0 ┆ 10.0 │
579
+ # # │ Orange ┆ 2.0 ┆ 0.5 │
580
+ # # │ Banana ┆ 5.0 ┆ 14.0 │
581
+ # # └────────┴─────┴──────┘
582
+ def quantile(quantile, interpolation: "nearest")
583
+ agg(F.all.quantile(quantile, interpolation: interpolation))
584
+ end
585
+
586
+ # Reduce the groups to the sum.
587
+ #
588
+ # @return [LazyFrame]
589
+ #
590
+ # @example
591
+ # ldf = Polars::DataFrame.new(
592
+ # {
593
+ # "a" => [1, 2, 2, 3, 4, 5],
594
+ # "b" => [0.5, 0.5, 4, 10, 13, 14],
595
+ # "c" => [true, true, true, false, false, true],
596
+ # "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
597
+ # }
598
+ # ).lazy
599
+ # ldf.group_by("d", maintain_order: true).sum.collect
600
+ # # =>
601
+ # # shape: (3, 4)
602
+ # # ┌────────┬─────┬──────┬─────┐
603
+ # # │ d ┆ a ┆ b ┆ c │
604
+ # # │ --- ┆ --- ┆ --- ┆ --- │
605
+ # # │ str ┆ i64 ┆ f64 ┆ u32 │
606
+ # # ╞════════╪═════╪══════╪═════╡
607
+ # # │ Apple ┆ 6 ┆ 14.5 ┆ 2 │
608
+ # # │ Orange ┆ 2 ┆ 0.5 ┆ 1 │
609
+ # # │ Banana ┆ 9 ┆ 27.0 ┆ 1 │
610
+ # # └────────┴─────┴──────┴─────┘
611
+ def sum
612
+ agg(F.all.sum)
613
+ end
180
614
  end
181
615
  end