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
@@ -11,6 +11,13 @@ module Polars
11
11
 
12
12
  # Evaluate whether all boolean values in a list are true.
13
13
  #
14
+ # @param ignore_nulls [Boolean]
15
+ # * If set to `true` (default), null values are ignored. If there
16
+ # are no non-null values, the output is `true`.
17
+ # * If set to `false`, [Kleene logic](https://en.wikipedia.org/wiki/Three-valued_logic) is used to deal with nulls:
18
+ # if the column contains any null values and no `false` values,
19
+ # the output is null.
20
+ #
14
21
  # @return [Expr]
15
22
  #
16
23
  # @example
@@ -32,12 +39,19 @@ module Polars
32
39
  # # │ [] ┆ true │
33
40
  # # │ null ┆ null │
34
41
  # # └────────────────┴───────┘
35
- def all
36
- Utils.wrap_expr(_rbexpr.list_all)
42
+ def all(ignore_nulls: true)
43
+ agg(F.element.all(ignore_nulls: ignore_nulls))
37
44
  end
38
45
 
39
46
  # Evaluate whether any boolean value in a list is true.
40
47
  #
48
+ # @param ignore_nulls [Boolean]
49
+ # * If set to `true` (default), null values are ignored. If there
50
+ # are no non-null values, the output is `false`.
51
+ # * If set to `false`, [Kleene logic](https://en.wikipedia.org/wiki/Three-valued_logic) is used to deal with nulls:
52
+ # if the column contains any null values and no `true` values,
53
+ # the output is null.
54
+ #
41
55
  # @return [Expr]
42
56
  #
43
57
  # @example
@@ -59,8 +73,8 @@ module Polars
59
73
  # # │ [] ┆ false │
60
74
  # # │ null ┆ null │
61
75
  # # └────────────────┴───────┘
62
- def any
63
- Utils.wrap_expr(_rbexpr.list_any)
76
+ def any(ignore_nulls: true)
77
+ agg(F.element.any(ignore_nulls: ignore_nulls))
64
78
  end
65
79
 
66
80
  # Get the length of the arrays as `:u32`.
@@ -69,7 +83,7 @@ module Polars
69
83
  #
70
84
  # @example
71
85
  # df = Polars::DataFrame.new({"foo" => [1, 2], "bar" => [["a", "b"], ["c"]]})
72
- # df.select(Polars.col("bar").list.lengths)
86
+ # df.select(Polars.col("bar").list.len)
73
87
  # # =>
74
88
  # # shape: (2, 1)
75
89
  # # ┌─────┐
@@ -80,10 +94,9 @@ module Polars
80
94
  # # │ 2 │
81
95
  # # │ 1 │
82
96
  # # └─────┘
83
- def lengths
97
+ def len
84
98
  Utils.wrap_expr(_rbexpr.list_len)
85
99
  end
86
- alias_method :len, :lengths
87
100
 
88
101
  # Drop all null values in the list.
89
102
  #
@@ -136,7 +149,7 @@ module Polars
136
149
  # # │ --- ┆ --- ┆ --- │
137
150
  # # │ list[i64] ┆ i64 ┆ list[i64] │
138
151
  # # ╞═══════════╪═════╪═══════════╡
139
- # # │ [1, 2, 3] ┆ 2 ┆ [2, 1] │
152
+ # # │ [1, 2, 3] ┆ 2 ┆ [2, 3] │
140
153
  # # │ [4, 5] ┆ 1 ┆ [5] │
141
154
  # # └───────────┴─────┴───────────┘
142
155
  def sample(n: nil, fraction: nil, with_replacement: false, shuffle: false, seed: nil)
@@ -243,8 +256,86 @@ module Polars
243
256
  Utils.wrap_expr(_rbexpr.list_mean)
244
257
  end
245
258
 
259
+ # Compute the median value of the lists in the array.
260
+ #
261
+ # @return [Expr]
262
+ #
263
+ # @example
264
+ # df = Polars::DataFrame.new({"values" => [[-1, 0, 1], [1, 10]]})
265
+ # df.with_columns(Polars.col("values").list.median.alias("median"))
266
+ # # =>
267
+ # # shape: (2, 2)
268
+ # # ┌────────────┬────────┐
269
+ # # │ values ┆ median │
270
+ # # │ --- ┆ --- │
271
+ # # │ list[i64] ┆ f64 │
272
+ # # ╞════════════╪════════╡
273
+ # # │ [-1, 0, 1] ┆ 0.0 │
274
+ # # │ [1, 10] ┆ 5.5 │
275
+ # # └────────────┴────────┘
276
+ def median
277
+ Utils.wrap_expr(_rbexpr.list_median)
278
+ end
279
+
280
+ # Compute the std value of the lists in the array.
281
+ #
282
+ # @param ddof [Integer]
283
+ # “Delta Degrees of Freedom”: the divisor used in the calculation is N - ddof,
284
+ # where N represents the number of elements.
285
+ # By default ddof is 1.
286
+ #
287
+ # @return [Expr]
288
+ #
289
+ # @example
290
+ # df = Polars::DataFrame.new({"values" => [[-1, 0, 1], [1, 10]]})
291
+ # df.with_columns(Polars.col("values").list.std.alias("std"))
292
+ # # =>
293
+ # # shape: (2, 2)
294
+ # # ┌────────────┬──────────┐
295
+ # # │ values ┆ std │
296
+ # # │ --- ┆ --- │
297
+ # # │ list[i64] ┆ f64 │
298
+ # # ╞════════════╪══════════╡
299
+ # # │ [-1, 0, 1] ┆ 1.0 │
300
+ # # │ [1, 10] ┆ 6.363961 │
301
+ # # └────────────┴──────────┘
302
+ def std(ddof: 1)
303
+ Utils.wrap_expr(_rbexpr.list_std(ddof))
304
+ end
305
+
306
+ # Compute the var value of the lists in the array.
307
+ #
308
+ # @param ddof [Integer]
309
+ # “Delta Degrees of Freedom”: the divisor used in the calculation is N - ddof,
310
+ # where N represents the number of elements.
311
+ # By default ddof is 1.
312
+ #
313
+ # @return [Expr]
314
+ #
315
+ # @example
316
+ # df = Polars::DataFrame.new({"values" => [[-1, 0, 1], [1, 10]]})
317
+ # df.with_columns(Polars.col("values").list.var.alias("var"))
318
+ # # =>
319
+ # # shape: (2, 2)
320
+ # # ┌────────────┬──────┐
321
+ # # │ values ┆ var │
322
+ # # │ --- ┆ --- │
323
+ # # │ list[i64] ┆ f64 │
324
+ # # ╞════════════╪══════╡
325
+ # # │ [-1, 0, 1] ┆ 1.0 │
326
+ # # │ [1, 10] ┆ 40.5 │
327
+ # # └────────────┴──────┘
328
+ def var(ddof: 1)
329
+ Utils.wrap_expr(_rbexpr.list_var(ddof))
330
+ end
331
+
246
332
  # Sort the arrays in the list.
247
333
  #
334
+ # @param descending [Boolean]
335
+ # Sort in descending order.
336
+ # @param nulls_last [Boolean]
337
+ # Place null values last.
338
+ #
248
339
  # @return [Expr]
249
340
  #
250
341
  # @example
@@ -264,8 +355,8 @@ module Polars
264
355
  # # │ [1, 2, 3] │
265
356
  # # │ [1, 2, 9] │
266
357
  # # └───────────┘
267
- def sort(reverse: false)
268
- Utils.wrap_expr(_rbexpr.list_sort(reverse))
358
+ def sort(descending: false, nulls_last: false)
359
+ Utils.wrap_expr(_rbexpr.list_sort(descending, nulls_last))
269
360
  end
270
361
 
271
362
  # Reverse the arrays in the list.
@@ -290,7 +381,7 @@ module Polars
290
381
  # # │ [2, 1, 9] │
291
382
  # # └───────────┘
292
383
  def reverse
293
- Utils.wrap_expr(_rbexpr.list_reverse)
384
+ eval(F.element.reverse)
294
385
  end
295
386
 
296
387
  # Get the unique/distinct values in the list.
@@ -314,7 +405,32 @@ module Polars
314
405
  # # │ [1, 2] │
315
406
  # # └───────────┘
316
407
  def unique(maintain_order: false)
317
- Utils.wrap_expr(_rbexpr.list_unique(maintain_order))
408
+ eval(F.element.unique(maintain_order: maintain_order))
409
+ end
410
+
411
+ # Count the number of unique values in every sub-lists.
412
+ #
413
+ # @return [Expr]
414
+ #
415
+ # @example
416
+ # df = Polars::DataFrame.new(
417
+ # {
418
+ # "a" => [[1, 1, 2], [2, 3, 4]]
419
+ # }
420
+ # )
421
+ # df.with_columns(n_unique: Polars.col("a").list.n_unique)
422
+ # # =>
423
+ # # shape: (2, 2)
424
+ # # ┌───────────┬──────────┐
425
+ # # │ a ┆ n_unique │
426
+ # # │ --- ┆ --- │
427
+ # # │ list[i64] ┆ u32 │
428
+ # # ╞═══════════╪══════════╡
429
+ # # │ [1, 1, 2] ┆ 2 │
430
+ # # │ [2, 3, 4] ┆ 3 │
431
+ # # └───────────┴──────────┘
432
+ def n_unique
433
+ agg(F.element.n_unique)
318
434
  end
319
435
 
320
436
  # Concat the arrays in a Series dtype List in linear time.
@@ -361,7 +477,7 @@ module Polars
361
477
  #
362
478
  # So index `0` would return the first item of every sublist
363
479
  # and index `-1` would return the last item of every sublist
364
- # if an index is out of bounds, it will return a `None`.
480
+ # if an index is out of bounds, it will return a `nil`.
365
481
  #
366
482
  # @param index [Integer]
367
483
  # Index to return per sublist
@@ -374,7 +490,7 @@ module Polars
374
490
  #
375
491
  # @example
376
492
  # df = Polars::DataFrame.new({"foo" => [[3, 2, 1], [], [1, 2]]})
377
- # df.select(Polars.col("foo").list.get(0))
493
+ # df.select(Polars.col("foo").list.get(0, null_on_oob: true))
378
494
  # # =>
379
495
  # # shape: (3, 1)
380
496
  # # ┌──────┐
@@ -386,7 +502,7 @@ module Polars
386
502
  # # │ null │
387
503
  # # │ 1 │
388
504
  # # └──────┘
389
- def get(index, null_on_oob: true)
505
+ def get(index, null_on_oob: false)
390
506
  index = Utils.parse_into_expression(index)
391
507
  Utils.wrap_expr(_rbexpr.list_get(index, null_on_oob))
392
508
  end
@@ -403,12 +519,12 @@ module Polars
403
519
  # The indices may be defined in a single column, or by sublists in another
404
520
  # column of dtype `List`.
405
521
  #
406
- # @param index [Object]
522
+ # @param indices [Object]
407
523
  # Indices to return per sublist
408
524
  # @param null_on_oob [Boolean]
409
525
  # Behavior if an index is out of bounds:
410
- # True -> set as null
411
- # False -> raise an error
526
+ # true -> set as null
527
+ # false -> raise an error
412
528
  # Note that defaulting to raising an error is much cheaper
413
529
  #
414
530
  # @return [Expr]
@@ -427,14 +543,52 @@ module Polars
427
543
  # # │ [] ┆ [null, null] │
428
544
  # # │ [1, 2, … 5] ┆ [1, 5] │
429
545
  # # └─────────────┴──────────────┘
430
- def gather(index, null_on_oob: false)
431
- if index.is_a?(::Array)
432
- index = Series.new(index)
433
- end
434
- index = Utils.parse_into_expression(index, str_as_lit: false)
435
- Utils.wrap_expr(_rbexpr.list_gather(index, null_on_oob))
546
+ def gather(indices, null_on_oob: false)
547
+ indices = Utils.parse_into_expression(indices)
548
+ Utils.wrap_expr(_rbexpr.list_gather(indices, null_on_oob))
549
+ end
550
+
551
+ # Take every n-th value start from offset in sublists.
552
+ #
553
+ # @param n [Integer]
554
+ # Gather every n-th element.
555
+ # @param offset [Integer]
556
+ # Starting index.
557
+ #
558
+ # @return [Expr]
559
+ #
560
+ # @example
561
+ # df = Polars::DataFrame.new(
562
+ # {
563
+ # "a" => [[1, 2, 3, 4, 5], [6, 7, 8], [9, 10, 11, 12]],
564
+ # "n" => [2, 1, 3],
565
+ # "offset" => [0, 1, 0]
566
+ # }
567
+ # )
568
+ # df.with_columns(
569
+ # gather_every: Polars.col("a").list.gather_every(
570
+ # Polars.col("n"), Polars.col("offset")
571
+ # )
572
+ # )
573
+ # # =>
574
+ # # shape: (3, 4)
575
+ # # ┌───────────────┬─────┬────────┬──────────────┐
576
+ # # │ a ┆ n ┆ offset ┆ gather_every │
577
+ # # │ --- ┆ --- ┆ --- ┆ --- │
578
+ # # │ list[i64] ┆ i64 ┆ i64 ┆ list[i64] │
579
+ # # ╞═══════════════╪═════╪════════╪══════════════╡
580
+ # # │ [1, 2, … 5] ┆ 2 ┆ 0 ┆ [1, 3, 5] │
581
+ # # │ [6, 7, 8] ┆ 1 ┆ 1 ┆ [7, 8] │
582
+ # # │ [9, 10, … 12] ┆ 3 ┆ 0 ┆ [9, 12] │
583
+ # # └───────────────┴─────┴────────┴──────────────┘
584
+ def gather_every(
585
+ n,
586
+ offset = 0
587
+ )
588
+ n = Utils.parse_into_expression(n)
589
+ offset = Utils.parse_into_expression(offset)
590
+ Utils.wrap_expr(_rbexpr.list_gather_every(n, offset))
436
591
  end
437
- alias_method :take, :gather
438
592
 
439
593
  # Get the first value of the sublists.
440
594
  #
@@ -455,7 +609,7 @@ module Polars
455
609
  # # │ 1 │
456
610
  # # └──────┘
457
611
  def first
458
- get(0)
612
+ get(0, null_on_oob: true)
459
613
  end
460
614
 
461
615
  # Get the last value of the sublists.
@@ -477,13 +631,57 @@ module Polars
477
631
  # # │ 2 │
478
632
  # # └──────┘
479
633
  def last
480
- get(-1)
634
+ get(-1, null_on_oob: true)
635
+ end
636
+
637
+ # Get the single value of the sublists.
638
+ #
639
+ # This errors if the sublist length is not exactly one.
640
+ #
641
+ # @param allow_empty [Boolean]
642
+ # Allow having no values to return `null`.
643
+ #
644
+ # @return [Expr]
645
+ #
646
+ # @example
647
+ # df = Polars::DataFrame.new({"a" => [[3], [1], [2]]})
648
+ # df.with_columns(item: Polars.col("a").list.item)
649
+ # # =>
650
+ # # shape: (3, 2)
651
+ # # ┌───────────┬──────┐
652
+ # # │ a ┆ item │
653
+ # # │ --- ┆ --- │
654
+ # # │ list[i64] ┆ i64 │
655
+ # # ╞═══════════╪══════╡
656
+ # # │ [3] ┆ 3 │
657
+ # # │ [1] ┆ 1 │
658
+ # # │ [2] ┆ 2 │
659
+ # # └───────────┴──────┘
660
+ #
661
+ # @example
662
+ # df = Polars::DataFrame.new({"a" => [[], [1], [2]]})
663
+ # df.select(Polars.col("a").list.item(allow_empty: true))
664
+ # # =>
665
+ # # shape: (3, 1)
666
+ # # ┌──────┐
667
+ # # │ a │
668
+ # # │ --- │
669
+ # # │ i64 │
670
+ # # ╞══════╡
671
+ # # │ null │
672
+ # # │ 1 │
673
+ # # │ 2 │
674
+ # # └──────┘
675
+ def item(allow_empty: false)
676
+ agg(F.element.item(allow_empty: allow_empty))
481
677
  end
482
678
 
483
679
  # Check if sublists contain the given item.
484
680
  #
485
681
  # @param item [Object]
486
682
  # Item that will be checked for membership
683
+ # @param nulls_equal [Boolean]
684
+ # If true, treat null as a distinct value. Null values will not propagate.
487
685
  #
488
686
  # @return [Expr]
489
687
  #
@@ -501,8 +699,8 @@ module Polars
501
699
  # # │ false │
502
700
  # # │ true │
503
701
  # # └───────┘
504
- def contains(item)
505
- Utils.wrap_expr(_rbexpr.list_contains(Utils.parse_into_expression(item)))
702
+ def contains(item, nulls_equal: true)
703
+ Utils.wrap_expr(_rbexpr.list_contains(Utils.parse_into_expression(item), nulls_equal))
506
704
  end
507
705
 
508
706
  # Join all string items in a sublist and place a separator between them.
@@ -698,7 +896,37 @@ module Polars
698
896
  Utils.wrap_expr(_rbexpr.list_tail(n))
699
897
  end
700
898
 
701
- # Count how often the value produced by ``element`` occurs.
899
+ # Returns a column with a separate row for every list element.
900
+ #
901
+ # @param empty_as_null [Boolean]
902
+ # Explode an empty list into a `null`.
903
+ # @param keep_nulls [Boolean]
904
+ # Explode a `null` list into a `null`.
905
+ #
906
+ # @return [Expr]
907
+ #
908
+ # @example
909
+ # df = Polars::DataFrame.new({"a" => [[1, 2, 3], [4, 5, 6]]})
910
+ # df.select(Polars.col("a").list.explode)
911
+ # # =>
912
+ # # shape: (6, 1)
913
+ # # ┌─────┐
914
+ # # │ a │
915
+ # # │ --- │
916
+ # # │ i64 │
917
+ # # ╞═════╡
918
+ # # │ 1 │
919
+ # # │ 2 │
920
+ # # │ 3 │
921
+ # # │ 4 │
922
+ # # │ 5 │
923
+ # # │ 6 │
924
+ # # └─────┘
925
+ def explode(empty_as_null: true, keep_nulls: true)
926
+ Utils.wrap_expr(_rbexpr.explode(empty_as_null, keep_nulls))
927
+ end
928
+
929
+ # Count how often the value produced by `element` occurs.
702
930
  #
703
931
  # @param element [Expr]
704
932
  # An expression that produces a single value
@@ -707,7 +935,7 @@ module Polars
707
935
  #
708
936
  # @example
709
937
  # df = Polars::DataFrame.new({"listcol" => [[0], [1], [1, 2, 3, 2], [1, 2, 1], [4, 4]]})
710
- # df.select(Polars.col("listcol").list.count_match(2).alias("number_of_twos"))
938
+ # df.select(Polars.col("listcol").list.count_matches(2).alias("number_of_twos"))
711
939
  # # =>
712
940
  # # shape: (5, 1)
713
941
  # # ┌────────────────┐
@@ -724,34 +952,79 @@ module Polars
724
952
  def count_matches(element)
725
953
  Utils.wrap_expr(_rbexpr.list_count_matches(Utils.parse_into_expression(element)))
726
954
  end
727
- alias_method :count_match, :count_matches
955
+
956
+ # Convert a List column into an Array column with the same inner data type.
957
+ #
958
+ # @param width [Integer]
959
+ # Width of the resulting Array column.
960
+ #
961
+ # @return [Expr]
962
+ #
963
+ # @example
964
+ # df = Polars::DataFrame.new(
965
+ # {"a" => [[1, 2], [3, 4]]},
966
+ # schema: {"a" => Polars::List.new(Polars::Int8)}
967
+ # )
968
+ # df.with_columns(array: Polars.col("a").list.to_array(2))
969
+ # # =>
970
+ # # shape: (2, 2)
971
+ # # ┌──────────┬──────────────┐
972
+ # # │ a ┆ array │
973
+ # # │ --- ┆ --- │
974
+ # # │ list[i8] ┆ array[i8, 2] │
975
+ # # ╞══════════╪══════════════╡
976
+ # # │ [1, 2] ┆ [1, 2] │
977
+ # # │ [3, 4] ┆ [3, 4] │
978
+ # # └──────────┴──────────────┘
979
+ def to_array(width)
980
+ Utils.wrap_expr(_rbexpr.list_to_array(width))
981
+ end
728
982
 
729
983
  # Convert the series of type `List` to a series of type `Struct`.
730
984
  #
731
985
  # @param n_field_strategy ["first_non_null", "max_width"]
732
- # Strategy to determine the number of fields of the struct.
733
- # @param name_generator [Object]
734
- # A custom function that can be used to generate the field names.
735
- # Default field names are `field_0, field_1 .. field_n`
986
+ # Deprecated and ignored.
987
+ # @param fields pArray
988
+ # If the name and number of the desired fields is known in advance
989
+ # a list of field names can be given, which will be assigned by index.
990
+ # Otherwise, to dynamically assign field names, a custom function can be
991
+ # used; if neither are set, fields will be `field_0, field_1 .. field_n`.
992
+ # @param upper_bound [Object]
993
+ # A polars `LazyFrame` needs to know the schema at all times, so the
994
+ # caller must provide an upper bound of the number of struct fields that
995
+ # will be created; if set incorrectly, subsequent operations may fail.
996
+ # (For example, an `all.sum` expression will look in the current
997
+ # schema to determine which columns to select).
998
+ #
999
+ # When operating on a `DataFrame`, the schema does not need to be
1000
+ # tracked or pre-determined, as the result will be eagerly evaluated,
1001
+ # so you can leave this parameter unset.
736
1002
  #
737
1003
  # @return [Expr]
738
1004
  #
739
1005
  # @example
740
- # df = Polars::DataFrame.new({"a" => [[1, 2, 3], [1, 2]]})
741
- # df.select([Polars.col("a").list.to_struct])
1006
+ # df = Polars::DataFrame.new({"n" => [[0, 1], [0, 1, 2]]})
1007
+ # df.with_columns(struct: Polars.col("n").list.to_struct(upper_bound: 2))
742
1008
  # # =>
743
- # # shape: (2, 1)
744
- # # ┌────────────┐
745
- # # │ a
746
- # # │ ---
747
- # # │ struct[3]
748
- # # ╞════════════╡
749
- # # │ {1,2,3}
750
- # # │ {1,2,null}
751
- # # └────────────┘
752
- def to_struct(n_field_strategy: "first_non_null", name_generator: nil)
753
- raise Todo if name_generator
754
- Utils.wrap_expr(_rbexpr.list_to_struct(n_field_strategy, name_generator, 0))
1009
+ # # shape: (2, 2)
1010
+ # # ┌───────────┬───────────┐
1011
+ # # │ n ┆ struct
1012
+ # # │ --- ┆ ---
1013
+ # # │ list[i64] ┆ struct[2]
1014
+ # # ╞═══════════╪═══════════╡
1015
+ # # │ [0, 1] ┆ {0,1}
1016
+ # # │ [0, 1, 2] ┆ {0,1}
1017
+ # # └───────────┴───────────┘
1018
+ def to_struct(n_field_strategy: nil, fields: nil, upper_bound: nil)
1019
+ if !fields.is_a?(::Array)
1020
+ if fields.nil?
1021
+ fields = upper_bound.times.map { |i| "field_#{i}" }
1022
+ else
1023
+ fields = upper_bound.times.map { |i| fields.(i) }
1024
+ end
1025
+ end
1026
+
1027
+ Utils.wrap_expr(_rbexpr.list_to_struct(fields))
755
1028
  end
756
1029
 
757
1030
  # Run any polars expression against the lists' elements.
@@ -759,18 +1032,12 @@ module Polars
759
1032
  # @param expr [Expr]
760
1033
  # Expression to run. Note that you can select an element with `Polars.first`, or
761
1034
  # `Polars.col`
762
- # @param parallel [Boolean]
763
- # Run all expression parallel. Don't activate this blindly.
764
- # Parallelism is worth it if there is enough work to do per thread.
765
- #
766
- # This likely should not be use in the group by context, because we already
767
- # parallel execution per group
768
1035
  #
769
1036
  # @return [Expr]
770
1037
  #
771
1038
  # @example
772
1039
  # df = Polars::DataFrame.new({"a" => [1, 8, 3], "b" => [4, 5, 2]})
773
- # df.with_column(
1040
+ # df.with_columns(
774
1041
  # Polars.concat_list(["a", "b"]).list.eval(Polars.element.rank).alias("rank")
775
1042
  # )
776
1043
  # # =>
@@ -784,8 +1051,233 @@ module Polars
784
1051
  # # │ 8 ┆ 5 ┆ [2.0, 1.0] │
785
1052
  # # │ 3 ┆ 2 ┆ [2.0, 1.0] │
786
1053
  # # └─────┴─────┴────────────┘
787
- def eval(expr, parallel: false)
788
- Utils.wrap_expr(_rbexpr.list_eval(expr._rbexpr, parallel))
1054
+ def eval(expr)
1055
+ Utils.wrap_expr(_rbexpr.list_eval(expr._rbexpr))
1056
+ end
1057
+
1058
+ # Run any polars aggregation expression against the lists' elements.
1059
+ #
1060
+ # @param expr [Expr]
1061
+ # Expression to run. Note that you can select an element with `Polars.element`.
1062
+ #
1063
+ # @return [Expr]
1064
+ #
1065
+ # @example
1066
+ # df = Polars::DataFrame.new({"a" => [[1, nil], [42, 13], [nil, nil]]})
1067
+ # df.with_columns(null_count: Polars.col("a").list.agg(Polars.element.null_count))
1068
+ # # =>
1069
+ # # shape: (3, 2)
1070
+ # # ┌──────────────┬────────────┐
1071
+ # # │ a ┆ null_count │
1072
+ # # │ --- ┆ --- │
1073
+ # # │ list[i64] ┆ u32 │
1074
+ # # ╞══════════════╪════════════╡
1075
+ # # │ [1, null] ┆ 1 │
1076
+ # # │ [42, 13] ┆ 0 │
1077
+ # # │ [null, null] ┆ 2 │
1078
+ # # └──────────────┴────────────┘
1079
+ #
1080
+ # @example
1081
+ # df.with_columns(no_nulls: Polars.col("a").list.agg(Polars.element.drop_nulls))
1082
+ # # =>
1083
+ # # shape: (3, 2)
1084
+ # # ┌──────────────┬───────────┐
1085
+ # # │ a ┆ no_nulls │
1086
+ # # │ --- ┆ --- │
1087
+ # # │ list[i64] ┆ list[i64] │
1088
+ # # ╞══════════════╪═══════════╡
1089
+ # # │ [1, null] ┆ [1] │
1090
+ # # │ [42, 13] ┆ [42, 13] │
1091
+ # # │ [null, null] ┆ [] │
1092
+ # # └──────────────┴───────────┘
1093
+ def agg(expr)
1094
+ Utils.wrap_expr(_rbexpr.list_agg(expr._rbexpr))
1095
+ end
1096
+
1097
+ # Filter elements in each list by a boolean expression.
1098
+ #
1099
+ # @param predicate [Object]
1100
+ # A boolean expression that is evaluated per list element.
1101
+ # You can refer to the current element with `Polars.element`.
1102
+ #
1103
+ # @return [Expr]
1104
+ #
1105
+ # @example
1106
+ # df = Polars::DataFrame.new({"a" => [1, 8, 3], "b" => [4, 5, 2]})
1107
+ # df.with_columns(
1108
+ # evens: Polars.concat_list("a", "b").list.filter(Polars.element % 2 == 0)
1109
+ # )
1110
+ # # =>
1111
+ # # shape: (3, 3)
1112
+ # # ┌─────┬─────┬───────────┐
1113
+ # # │ a ┆ b ┆ evens │
1114
+ # # │ --- ┆ --- ┆ --- │
1115
+ # # │ i64 ┆ i64 ┆ list[i64] │
1116
+ # # ╞═════╪═════╪═══════════╡
1117
+ # # │ 1 ┆ 4 ┆ [4] │
1118
+ # # │ 8 ┆ 5 ┆ [8] │
1119
+ # # │ 3 ┆ 2 ┆ [2] │
1120
+ # # └─────┴─────┴───────────┘
1121
+ def filter(predicate)
1122
+ Utils.wrap_expr(_rbexpr.list_filter(predicate._rbexpr))
1123
+ end
1124
+
1125
+ # Compute the SET UNION between the elements in this list and the elements of `other`.
1126
+ #
1127
+ # @param other [Object]
1128
+ # Right hand side of the set operation.
1129
+ #
1130
+ # @return [Expr]
1131
+ #
1132
+ # @example
1133
+ # df = Polars::DataFrame.new(
1134
+ # {
1135
+ # "a" => [[1, 2, 3], [], [nil, 3], [5, 6, 7]],
1136
+ # "b" => [[2, 3, 4], [3], [3, 4, nil], [6, 8]]
1137
+ # }
1138
+ # )
1139
+ # df.with_columns(
1140
+ # union: Polars.col("a").list.set_union("b")
1141
+ # )
1142
+ # # =>
1143
+ # # shape: (4, 3)
1144
+ # # ┌───────────┬──────────────┬──────────────┐
1145
+ # # │ a ┆ b ┆ union │
1146
+ # # │ --- ┆ --- ┆ --- │
1147
+ # # │ list[i64] ┆ list[i64] ┆ list[i64] │
1148
+ # # ╞═══════════╪══════════════╪══════════════╡
1149
+ # # │ [1, 2, 3] ┆ [2, 3, 4] ┆ [1, 2, … 4] │
1150
+ # # │ [] ┆ [3] ┆ [3] │
1151
+ # # │ [null, 3] ┆ [3, 4, null] ┆ [null, 3, 4] │
1152
+ # # │ [5, 6, 7] ┆ [6, 8] ┆ [5, 6, … 8] │
1153
+ # # └───────────┴──────────────┴──────────────┘
1154
+ def set_union(other)
1155
+ if other.respond_to?(:each)
1156
+ if !other.is_a?(::Array) && !other.is_a?(Series) && !other.is_a?(DataFrame)
1157
+ other = other.to_a
1158
+ end
1159
+ other = F.lit(other)._rbexpr
1160
+ else
1161
+ other = Utils.parse_into_expression(other)
1162
+ end
1163
+ Utils.wrap_expr(_rbexpr.list_set_operation(other, "union"))
1164
+ end
1165
+
1166
+ # Compute the SET DIFFERENCE between the elements in this list and the elements of `other`.
1167
+ #
1168
+ # @param other [Object]
1169
+ # Right hand side of the set operation.
1170
+ #
1171
+ # @return [Expr]
1172
+ #
1173
+ # @example
1174
+ # df = Polars::DataFrame.new(
1175
+ # {
1176
+ # "a" => [[1, 2, 3], [], [nil, 3], [5, 6, 7]],
1177
+ # "b" => [[2, 3, 4], [3], [3, 4, nil], [6, 8]]
1178
+ # }
1179
+ # )
1180
+ # df.with_columns(difference: Polars.col("a").list.set_difference("b"))
1181
+ # # =>
1182
+ # # shape: (4, 3)
1183
+ # # ┌───────────┬──────────────┬────────────┐
1184
+ # # │ a ┆ b ┆ difference │
1185
+ # # │ --- ┆ --- ┆ --- │
1186
+ # # │ list[i64] ┆ list[i64] ┆ list[i64] │
1187
+ # # ╞═══════════╪══════════════╪════════════╡
1188
+ # # │ [1, 2, 3] ┆ [2, 3, 4] ┆ [1] │
1189
+ # # │ [] ┆ [3] ┆ [] │
1190
+ # # │ [null, 3] ┆ [3, 4, null] ┆ [] │
1191
+ # # │ [5, 6, 7] ┆ [6, 8] ┆ [5, 7] │
1192
+ # # └───────────┴──────────────┴────────────┘
1193
+ def set_difference(other)
1194
+ if other.respond_to?(:each)
1195
+ if !other.is_a?(::Array) && !other.is_a?(Series) && !other.is_a?(DataFrame)
1196
+ other = other.to_a
1197
+ end
1198
+ other = F.lit(other)._rbexpr
1199
+ else
1200
+ other = Utils.parse_into_expression(other)
1201
+ end
1202
+ Utils.wrap_expr(_rbexpr.list_set_operation(other, "difference"))
1203
+ end
1204
+
1205
+ # Compute the SET INTERSECTION between the elements in this list and the elements of `other`.
1206
+ #
1207
+ # @param other [Object]
1208
+ # Right hand side of the set operation.
1209
+ #
1210
+ # @return [Expr]
1211
+ #
1212
+ # @example
1213
+ # df = Polars::DataFrame.new(
1214
+ # {
1215
+ # "a" => [[1, 2, 3], [], [nil, 3], [5, 6, 7]],
1216
+ # "b" => [[2, 3, 4], [3], [3, 4, nil], [6, 8]]
1217
+ # }
1218
+ # )
1219
+ # df.with_columns(intersection: Polars.col("a").list.set_intersection("b"))
1220
+ # # =>
1221
+ # # shape: (4, 3)
1222
+ # # ┌───────────┬──────────────┬──────────────┐
1223
+ # # │ a ┆ b ┆ intersection │
1224
+ # # │ --- ┆ --- ┆ --- │
1225
+ # # │ list[i64] ┆ list[i64] ┆ list[i64] │
1226
+ # # ╞═══════════╪══════════════╪══════════════╡
1227
+ # # │ [1, 2, 3] ┆ [2, 3, 4] ┆ [2, 3] │
1228
+ # # │ [] ┆ [3] ┆ [] │
1229
+ # # │ [null, 3] ┆ [3, 4, null] ┆ [null, 3] │
1230
+ # # │ [5, 6, 7] ┆ [6, 8] ┆ [6] │
1231
+ # # └───────────┴──────────────┴──────────────┘
1232
+ def set_intersection(other)
1233
+ if other.respond_to?(:each)
1234
+ if !other.is_a?(::Array) && !other.is_a?(Series) && !other.is_a?(DataFrame)
1235
+ other = other.to_a
1236
+ end
1237
+ other = F.lit(other)._rbexpr
1238
+ else
1239
+ other = Utils.parse_into_expression(other)
1240
+ end
1241
+ Utils.wrap_expr(_rbexpr.list_set_operation(other, "intersection"))
1242
+ end
1243
+
1244
+ # Compute the SET SYMMETRIC DIFFERENCE between the elements in this list and the elements of `other`.
1245
+ #
1246
+ # @param other [Object]
1247
+ # Right hand side of the set operation.
1248
+ #
1249
+ # @return [Expr]
1250
+ #
1251
+ # @example
1252
+ # df = Polars::DataFrame.new(
1253
+ # {
1254
+ # "a" => [[1, 2, 3], [], [nil, 3], [5, 6, 7]],
1255
+ # "b" => [[2, 3, 4], [3], [3, 4, nil], [6, 8]]
1256
+ # }
1257
+ # )
1258
+ # df.with_columns(sdiff: Polars.col("b").list.set_symmetric_difference("a"))
1259
+ # # =>
1260
+ # # shape: (4, 3)
1261
+ # # ┌───────────┬──────────────┬───────────┐
1262
+ # # │ a ┆ b ┆ sdiff │
1263
+ # # │ --- ┆ --- ┆ --- │
1264
+ # # │ list[i64] ┆ list[i64] ┆ list[i64] │
1265
+ # # ╞═══════════╪══════════════╪═══════════╡
1266
+ # # │ [1, 2, 3] ┆ [2, 3, 4] ┆ [4, 1] │
1267
+ # # │ [] ┆ [3] ┆ [3] │
1268
+ # # │ [null, 3] ┆ [3, 4, null] ┆ [4] │
1269
+ # # │ [5, 6, 7] ┆ [6, 8] ┆ [8, 5, 7] │
1270
+ # # └───────────┴──────────────┴───────────┘
1271
+ def set_symmetric_difference(other)
1272
+ if other.respond_to?(:each)
1273
+ if !other.is_a?(::Array) && !other.is_a?(Series) && !other.is_a?(DataFrame)
1274
+ other = other.to_a
1275
+ end
1276
+ other = F.lit(other)._rbexpr
1277
+ else
1278
+ other = Utils.parse_into_expression(other)
1279
+ end
1280
+ Utils.wrap_expr(_rbexpr.list_set_operation(other, "symmetric_difference"))
789
1281
  end
790
1282
  end
791
1283
  end