polars-df 0.15.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +274 -0
  3. data/Cargo.lock +1465 -867
  4. data/Cargo.toml +3 -0
  5. data/LICENSE.txt +2 -2
  6. data/README.md +87 -37
  7. data/ext/polars/Cargo.toml +47 -16
  8. data/ext/polars/src/c_api/allocator.rs +7 -0
  9. data/ext/polars/src/c_api/mod.rs +1 -0
  10. data/ext/polars/src/catalog/mod.rs +1 -0
  11. data/ext/polars/src/catalog/unity.rs +470 -0
  12. data/ext/polars/src/conversion/any_value.rs +99 -84
  13. data/ext/polars/src/conversion/categorical.rs +30 -0
  14. data/ext/polars/src/conversion/chunked_array.rs +71 -62
  15. data/ext/polars/src/conversion/datetime.rs +63 -0
  16. data/ext/polars/src/conversion/mod.rs +796 -312
  17. data/ext/polars/src/dataframe/construction.rs +6 -18
  18. data/ext/polars/src/dataframe/export.rs +30 -39
  19. data/ext/polars/src/dataframe/general.rs +294 -362
  20. data/ext/polars/src/dataframe/io.rs +33 -150
  21. data/ext/polars/src/dataframe/map.rs +175 -0
  22. data/ext/polars/src/dataframe/mod.rs +37 -5
  23. data/ext/polars/src/dataframe/serde.rs +23 -8
  24. data/ext/polars/src/error.rs +44 -7
  25. data/ext/polars/src/exceptions.rs +21 -8
  26. data/ext/polars/src/expr/array.rs +86 -22
  27. data/ext/polars/src/expr/binary.rs +50 -1
  28. data/ext/polars/src/expr/bitwise.rs +39 -0
  29. data/ext/polars/src/expr/categorical.rs +20 -0
  30. data/ext/polars/src/expr/datatype.rs +51 -0
  31. data/ext/polars/src/expr/datetime.rs +99 -41
  32. data/ext/polars/src/expr/extension.rs +12 -0
  33. data/ext/polars/src/expr/general.rs +252 -128
  34. data/ext/polars/src/expr/list.rs +56 -60
  35. data/ext/polars/src/expr/meta.rs +30 -35
  36. data/ext/polars/src/expr/mod.rs +28 -6
  37. data/ext/polars/src/expr/name.rs +29 -14
  38. data/ext/polars/src/expr/rolling.rs +111 -3
  39. data/ext/polars/src/expr/selector.rs +219 -0
  40. data/ext/polars/src/expr/serde.rs +28 -0
  41. data/ext/polars/src/expr/string.rs +118 -20
  42. data/ext/polars/src/expr/struct.rs +14 -1
  43. data/ext/polars/src/file.rs +194 -86
  44. data/ext/polars/src/functions/aggregation.rs +13 -12
  45. data/ext/polars/src/functions/business.rs +2 -3
  46. data/ext/polars/src/functions/eager.rs +3 -2
  47. data/ext/polars/src/functions/io.rs +90 -18
  48. data/ext/polars/src/functions/lazy.rs +267 -118
  49. data/ext/polars/src/functions/meta.rs +8 -7
  50. data/ext/polars/src/functions/misc.rs +1 -1
  51. data/ext/polars/src/functions/mod.rs +2 -1
  52. data/ext/polars/src/functions/range.rs +88 -31
  53. data/ext/polars/src/functions/strings.rs +6 -0
  54. data/ext/polars/src/functions/utils.rs +8 -0
  55. data/ext/polars/src/interop/arrow/mod.rs +52 -1
  56. data/ext/polars/src/interop/arrow/{to_ruby.rs → to_rb.rs} +37 -7
  57. data/ext/polars/src/interop/arrow/to_rust.rs +43 -0
  58. data/ext/polars/src/interop/numo/to_numo_df.rs +1 -1
  59. data/ext/polars/src/interop/numo/to_numo_series.rs +72 -50
  60. data/ext/polars/src/io/cloud_options.rs +107 -0
  61. data/ext/polars/src/io/mod.rs +4 -0
  62. data/ext/polars/src/io/scan_options.rs +113 -0
  63. data/ext/polars/src/io/sink_options.rs +46 -0
  64. data/ext/polars/src/io/sink_output.rs +21 -0
  65. data/ext/polars/src/lazyframe/exitable.rs +39 -0
  66. data/ext/polars/src/lazyframe/general.rs +846 -368
  67. data/ext/polars/src/lazyframe/mod.rs +58 -5
  68. data/ext/polars/src/lazyframe/optflags.rs +59 -0
  69. data/ext/polars/src/lazyframe/serde.rs +36 -4
  70. data/ext/polars/src/lazyframe/sink.rs +46 -0
  71. data/ext/polars/src/lazygroupby.rs +38 -9
  72. data/ext/polars/src/lib.rs +574 -165
  73. data/ext/polars/src/map/lazy.rs +44 -74
  74. data/ext/polars/src/map/mod.rs +18 -254
  75. data/ext/polars/src/map/series.rs +241 -1087
  76. data/ext/polars/src/on_startup.rs +192 -9
  77. data/ext/polars/src/prelude.rs +1 -0
  78. data/ext/polars/src/rb_modules.rs +10 -57
  79. data/ext/polars/src/ruby/exceptions.rs +26 -0
  80. data/ext/polars/src/ruby/gvl.rs +104 -0
  81. data/ext/polars/src/ruby/lazy.rs +46 -0
  82. data/ext/polars/src/ruby/mod.rs +11 -0
  83. data/ext/polars/src/ruby/numo.rs +52 -0
  84. data/ext/polars/src/ruby/plan_callback.rs +198 -0
  85. data/ext/polars/src/ruby/rb_modules.rs +16 -0
  86. data/ext/polars/src/ruby/ruby_convert_registry.rs +51 -0
  87. data/ext/polars/src/ruby/ruby_function.rs +11 -0
  88. data/ext/polars/src/ruby/ruby_udf.rs +164 -0
  89. data/ext/polars/src/ruby/thread.rs +65 -0
  90. data/ext/polars/src/ruby/utils.rs +39 -0
  91. data/ext/polars/src/series/aggregation.rs +116 -91
  92. data/ext/polars/src/series/arithmetic.rs +16 -22
  93. data/ext/polars/src/series/comparison.rs +101 -222
  94. data/ext/polars/src/series/construction.rs +80 -70
  95. data/ext/polars/src/series/export.rs +98 -56
  96. data/ext/polars/src/series/general.rs +323 -440
  97. data/ext/polars/src/series/import.rs +22 -5
  98. data/ext/polars/src/series/map.rs +103 -0
  99. data/ext/polars/src/series/mod.rs +57 -15
  100. data/ext/polars/src/series/scatter.rs +139 -82
  101. data/ext/polars/src/sql.rs +16 -9
  102. data/ext/polars/src/testing/frame.rs +31 -0
  103. data/ext/polars/src/testing/mod.rs +5 -0
  104. data/ext/polars/src/testing/series.rs +31 -0
  105. data/ext/polars/src/timeout.rs +105 -0
  106. data/ext/polars/src/utils.rs +105 -4
  107. data/lib/polars/array_expr.rb +500 -22
  108. data/lib/polars/array_name_space.rb +384 -10
  109. data/lib/polars/batched_csv_reader.rb +48 -66
  110. data/lib/polars/binary_expr.rb +217 -0
  111. data/lib/polars/binary_name_space.rb +155 -1
  112. data/lib/polars/cat_expr.rb +224 -0
  113. data/lib/polars/cat_name_space.rb +132 -32
  114. data/lib/polars/catalog/unity/catalog_info.rb +20 -0
  115. data/lib/polars/catalog/unity/column_info.rb +31 -0
  116. data/lib/polars/catalog/unity/namespace_info.rb +21 -0
  117. data/lib/polars/catalog/unity/table_info.rb +50 -0
  118. data/lib/polars/catalog.rb +448 -0
  119. data/lib/polars/collect_batches.rb +22 -0
  120. data/lib/polars/config.rb +3 -3
  121. data/lib/polars/convert.rb +201 -36
  122. data/lib/polars/data_frame.rb +2851 -1017
  123. data/lib/polars/data_frame_plot.rb +173 -0
  124. data/lib/polars/data_type_expr.rb +52 -0
  125. data/lib/polars/data_type_group.rb +6 -0
  126. data/lib/polars/data_types.rb +118 -18
  127. data/lib/polars/date_time_expr.rb +426 -84
  128. data/lib/polars/date_time_name_space.rb +384 -111
  129. data/lib/polars/dynamic_group_by.rb +102 -10
  130. data/lib/polars/exceptions.rb +50 -5
  131. data/lib/polars/expr.rb +2159 -915
  132. data/lib/polars/extension_expr.rb +39 -0
  133. data/lib/polars/extension_name_space.rb +39 -0
  134. data/lib/polars/functions/aggregation/horizontal.rb +11 -6
  135. data/lib/polars/functions/aggregation/vertical.rb +2 -3
  136. data/lib/polars/functions/as_datatype.rb +290 -8
  137. data/lib/polars/functions/business.rb +95 -0
  138. data/lib/polars/functions/col.rb +6 -5
  139. data/lib/polars/functions/datatype.rb +62 -0
  140. data/lib/polars/functions/eager.rb +426 -24
  141. data/lib/polars/functions/escape_regex.rb +21 -0
  142. data/lib/polars/functions/lazy.rb +813 -195
  143. data/lib/polars/functions/lit.rb +21 -10
  144. data/lib/polars/functions/range/int_range.rb +74 -2
  145. data/lib/polars/functions/range/linear_space.rb +195 -0
  146. data/lib/polars/functions/range/time_range.rb +1 -1
  147. data/lib/polars/functions/repeat.rb +7 -12
  148. data/lib/polars/functions/whenthen.rb +2 -2
  149. data/lib/polars/group_by.rb +188 -58
  150. data/lib/polars/iceberg_dataset.rb +108 -0
  151. data/lib/polars/in_process_query.rb +37 -0
  152. data/lib/polars/io/cloud.rb +18 -0
  153. data/lib/polars/io/csv.rb +336 -128
  154. data/lib/polars/io/database.rb +19 -4
  155. data/lib/polars/io/delta.rb +134 -0
  156. data/lib/polars/io/iceberg.rb +34 -0
  157. data/lib/polars/io/ipc.rb +63 -63
  158. data/lib/polars/io/json.rb +16 -0
  159. data/lib/polars/io/lines.rb +172 -0
  160. data/lib/polars/io/ndjson.rb +176 -20
  161. data/lib/polars/io/parquet.rb +173 -95
  162. data/lib/polars/io/scan_options.rb +55 -0
  163. data/lib/polars/io/sink_options.rb +27 -0
  164. data/lib/polars/io/utils.rb +17 -0
  165. data/lib/polars/lazy_frame.rb +3017 -622
  166. data/lib/polars/lazy_group_by.rb +436 -2
  167. data/lib/polars/list_expr.rb +551 -59
  168. data/lib/polars/list_name_space.rb +465 -51
  169. data/lib/polars/meta_expr.rb +146 -24
  170. data/lib/polars/name_expr.rb +87 -2
  171. data/lib/polars/query_opt_flags.rb +264 -0
  172. data/lib/polars/rolling_group_by.rb +90 -5
  173. data/lib/polars/scan_cast_options.rb +86 -0
  174. data/lib/polars/schema.rb +128 -0
  175. data/lib/polars/selector.rb +245 -0
  176. data/lib/polars/selectors.rb +1048 -201
  177. data/lib/polars/series.rb +2522 -774
  178. data/lib/polars/series_plot.rb +72 -0
  179. data/lib/polars/slice.rb +1 -1
  180. data/lib/polars/sql_context.rb +13 -6
  181. data/lib/polars/string_cache.rb +19 -72
  182. data/lib/polars/string_expr.rb +561 -107
  183. data/lib/polars/string_name_space.rb +781 -109
  184. data/lib/polars/struct_expr.rb +139 -18
  185. data/lib/polars/struct_name_space.rb +19 -1
  186. data/lib/polars/testing.rb +24 -273
  187. data/lib/polars/utils/constants.rb +2 -0
  188. data/lib/polars/utils/construction/data_frame.rb +410 -0
  189. data/lib/polars/utils/construction/series.rb +350 -0
  190. data/lib/polars/utils/construction/utils.rb +9 -0
  191. data/lib/polars/utils/convert.rb +18 -8
  192. data/lib/polars/utils/deprecation.rb +11 -0
  193. data/lib/polars/utils/parse.rb +62 -9
  194. data/lib/polars/utils/reduce_balanced.rb +43 -0
  195. data/lib/polars/utils/serde.rb +22 -0
  196. data/lib/polars/utils/unstable.rb +19 -0
  197. data/lib/polars/utils/various.rb +86 -1
  198. data/lib/polars/utils.rb +63 -48
  199. data/lib/polars/version.rb +1 -1
  200. data/lib/polars.rb +85 -2
  201. metadata +80 -28
  202. data/ext/polars/src/allocator.rs +0 -13
  203. data/ext/polars/src/batched_csv.rs +0 -138
  204. data/ext/polars/src/functions/string_cache.rs +0 -25
  205. data/ext/polars/src/map/dataframe.rs +0 -338
  206. data/lib/polars/plot.rb +0 -109
@@ -0,0 +1,350 @@
1
+ module Polars
2
+ module Utils
3
+ def self.sequence_to_rbseries(name, values, dtype: nil, strict: true, nan_to_null: false)
4
+ ruby_dtype = nil
5
+
6
+ if values.is_a?(Range)
7
+ if values.begin.is_a?(::String)
8
+ values = values.to_a
9
+ else
10
+ return range_to_series(name, values, dtype: dtype)._s
11
+ end
12
+ end
13
+
14
+ if values.length == 0 && dtype.nil?
15
+ dtype = Null
16
+ elsif [List, Array].include?(dtype)
17
+ ruby_dtype = ::Array
18
+ end
19
+
20
+ rb_temporal_types = [::Date, ::DateTime, ::Time]
21
+ rb_temporal_types << ActiveSupport::TimeWithZone if defined?(ActiveSupport::TimeWithZone)
22
+
23
+ value = get_first_non_none(values)
24
+
25
+ if !dtype.nil? && is_polars_dtype(dtype) && !dtype.nested? && dtype != Unknown && ruby_dtype.nil?
26
+ constructor = polars_type_to_constructor(dtype)
27
+ rbseries = _construct_series_with_fallbacks(
28
+ constructor, name, values, dtype, strict: strict
29
+ )
30
+
31
+ if [Date, Datetime, Duration, Time, Boolean, Categorical, Enum].include?(dtype) || dtype.is_a?(Decimal) || dtype.is_a?(Categorical)
32
+ if rbseries.dtype != dtype
33
+ rbseries = rbseries.cast(dtype, true, false)
34
+ end
35
+ end
36
+
37
+ # Uninstanced Decimal is a bit special and has various inference paths
38
+ if dtype == Decimal
39
+ if rbseries.dtype == String
40
+ rbseries = rbseries.str_to_decimal_infer(0)
41
+ elsif rbseries.dtype.float?
42
+ # Go through string so we infer an appropriate scale.
43
+ rbseries = rbseries.cast(
44
+ String, strict, false
45
+ ).str_to_decimal_infer(0)
46
+ elsif rbseries.dtype.integer? || rbseries.dtype == Null
47
+ rbseries = rbseries.cast(
48
+ Decimal.new(nil, 0), strict, false
49
+ )
50
+ elsif !rbseries.dtype.is_a?(Decimal)
51
+ msg = "can't convert #{rbseries.dtype} to Decimal"
52
+ raise TypeError, msg
53
+ end
54
+ end
55
+
56
+ return rbseries
57
+
58
+ elsif dtype == Struct
59
+ struct_schema = dtype.is_a?(Struct) ? dtype.to_schema : nil
60
+ empty = {}
61
+
62
+ data = []
63
+ invalid = []
64
+ values.each_with_index do |v, i|
65
+ if v.nil?
66
+ invalid << i
67
+ data << empty
68
+ else
69
+ data << v
70
+ end
71
+ end
72
+
73
+ return sequence_to_rbdf(
74
+ data,
75
+ schema: struct_schema,
76
+ orient: "row",
77
+ ).to_struct(name, invalid)
78
+ end
79
+
80
+ if ruby_dtype.nil?
81
+ if value.nil?
82
+ constructor = polars_type_to_constructor(Null)
83
+ return constructor.(name, values, strict)
84
+ end
85
+
86
+ ruby_dtype = value.class
87
+ end
88
+
89
+ # temporal branch
90
+ if rb_temporal_types.include?(ruby_dtype)
91
+ if dtype.nil?
92
+ dtype = parse_into_dtype(ruby_dtype)
93
+ elsif rb_temporal_types.include?(dtype)
94
+ dtype = parse_into_dtype(dtype)
95
+ end
96
+
97
+ values_dtype = value.nil? ? nil : try_parse_into_dtype(value.class)
98
+ if !values_dtype.nil? && values_dtype.float?
99
+ msg = "'float' object cannot be interpreted as a #{ruby_dtype.name.inspect}"
100
+ raise TypeError, msg
101
+ end
102
+
103
+ rb_series = RbSeries.new_from_any_values(name, values, strict)
104
+
105
+ time_unit = dtype.respond_to?(:time_unit) ? dtype.time_unit : nil
106
+ time_zone = dtype.respond_to?(:time_zone) ? dtype.time_zone : nil
107
+
108
+ if dtype.temporal? && values_dtype == String && dtype != Duration
109
+ s = wrap_s(rb_series).str.strptime(dtype, strict: strict)
110
+ elsif !time_unit.nil? && values_dtype != Date
111
+ s = wrap_s(rb_series).dt.cast_time_unit(time_unit)
112
+ else
113
+ s = wrap_s(rb_series)
114
+ end
115
+
116
+ if dtype == Datetime && !time_zone.nil?
117
+ return s.dt.convert_time_zone(time_zone)._s
118
+ end
119
+ s._s
120
+
121
+ elsif defined?(Numo::NArray) && value.is_a?(Numo::NArray) && value.shape.length == 1
122
+ raise Todo
123
+
124
+ elsif ruby_dtype == ::Array
125
+ if dtype.nil?
126
+ RbSeries.new_from_any_values(name, values, strict)
127
+ elsif dtype.is_a?(Object)
128
+ RbSeries.new_object(name, values, strict)
129
+ else
130
+ inner_dtype = dtype.respond_to?(:inner) ? dtype.inner : nil
131
+ if !inner_dtype.nil?
132
+ rbseries_list =
133
+ values.map do |value|
134
+ if value.nil?
135
+ nil
136
+ else
137
+ sequence_to_rbseries(
138
+ "",
139
+ value,
140
+ dtype: inner_dtype,
141
+ strict: strict,
142
+ nan_to_null: nan_to_null,
143
+ )
144
+ end
145
+ end
146
+ rbseries = RbSeries.new_series_list(name, rbseries_list, strict)
147
+ else
148
+ # panics in Python
149
+ raise Todo if dtype.eql?(Array)
150
+
151
+ rbseries = RbSeries.new_from_any_values_and_dtype(
152
+ name, values, dtype, strict
153
+ )
154
+ end
155
+ if dtype != rbseries.dtype
156
+ rbseries = rbseries.cast(dtype, false, false)
157
+ end
158
+ rbseries
159
+ end
160
+
161
+ elsif ruby_dtype == Series
162
+ RbSeries.new_series_list(
163
+ name, values.map { |v| !v.nil? ? v._s : v }, strict
164
+ )
165
+
166
+ elsif ruby_dtype == RbSeries
167
+ RbSeries.new_series_list(name, values, strict)
168
+ else
169
+ constructor =
170
+ if value.is_a?(::String)
171
+ if value.encoding == Encoding::UTF_8
172
+ RbSeries.method(:new_str)
173
+ else
174
+ RbSeries.method(:new_binary)
175
+ end
176
+ elsif value.is_a?(Integer) && values.any? { |v| v.is_a?(Float) }
177
+ # TODO improve performance
178
+ RbSeries.method(:new_opt_f64)
179
+ else
180
+ rb_type_to_constructor(value.class)
181
+ end
182
+
183
+ if constructor == RbSeries.method(:new_object)
184
+ srs = RbSeries.new_from_any_values(name, values, strict)
185
+ return srs
186
+ end
187
+
188
+ _construct_series_with_fallbacks(constructor, name, values, dtype, strict: strict)
189
+ end
190
+ end
191
+
192
+ def self._construct_series_with_fallbacks(constructor, name, values, dtype, strict:)
193
+ begin
194
+ constructor.(name, values, strict)
195
+ rescue
196
+ if dtype.nil?
197
+ RbSeries.new_from_any_values(name, values, strict)
198
+ else
199
+ RbSeries.new_from_any_values_and_dtype(name, values, dtype, strict)
200
+ end
201
+ end
202
+ end
203
+
204
+ def self.numo_to_rbseries(name, values, strict: true, nan_to_null: false)
205
+ # not needed yet
206
+ # if !values.contiguous?
207
+ # end
208
+
209
+ if values.shape.length == 1
210
+ values, dtype = numo_values_and_dtype(values)
211
+ constructor = numo_type_to_constructor(dtype)
212
+ constructor.(
213
+ name, values.to_a, [Numo::SFloat, Numo::DFloat].include?(dtype) ? nan_to_null : strict
214
+ )
215
+ else
216
+ original_shape = values.shape
217
+ values_1d = values.reshape(original_shape.inject(&:*))
218
+
219
+ rb_s = numo_to_rbseries(
220
+ name,
221
+ values_1d,
222
+ strict: strict,
223
+ nan_to_null: nan_to_null
224
+ )
225
+ Utils.wrap_s(rb_s).reshape(original_shape)._s
226
+ end
227
+ end
228
+
229
+ def self.series_to_rbseries(name, values, dtype: nil, strict: true)
230
+ s = values.clone
231
+ if !dtype.nil? && dtype != s.dtype
232
+ s = s.cast(dtype, strict: strict)
233
+ end
234
+ if !name.nil?
235
+ s = s.alias(name)
236
+ end
237
+ s._s
238
+ end
239
+
240
+ def self.dataframe_to_rbseries(
241
+ name,
242
+ values,
243
+ dtype: nil,
244
+ strict: true
245
+ )
246
+ if values.width > 1
247
+ name ||= ""
248
+ s = values.to_struct(name)
249
+ elsif values.width == 1
250
+ s = values.to_series
251
+ if !name.nil?
252
+ s = s.alias(name)
253
+ end
254
+ else
255
+ msg = "cannot initialize Series from DataFrame without any columns"
256
+ raise TypeError, msg
257
+ end
258
+
259
+ if !dtype.nil? && dtype != s.dtype
260
+ s = s.cast(dtype, strict: strict)
261
+ end
262
+
263
+ s._s
264
+ end
265
+
266
+ # TODO move rest
267
+
268
+ POLARS_TYPE_TO_CONSTRUCTOR = {
269
+ Float16 => RbSeries.method(:new_opt_f16),
270
+ Float32 => RbSeries.method(:new_opt_f32),
271
+ Float64 => RbSeries.method(:new_opt_f64),
272
+ Int8 => RbSeries.method(:new_opt_i8),
273
+ Int16 => RbSeries.method(:new_opt_i16),
274
+ Int32 => RbSeries.method(:new_opt_i32),
275
+ Int64 => RbSeries.method(:new_opt_i64),
276
+ Int128 => RbSeries.method(:new_opt_i128),
277
+ UInt8 => RbSeries.method(:new_opt_u8),
278
+ UInt16 => RbSeries.method(:new_opt_u16),
279
+ UInt32 => RbSeries.method(:new_opt_u32),
280
+ UInt64 => RbSeries.method(:new_opt_u64),
281
+ UInt128 => RbSeries.method(:new_opt_u128),
282
+ Decimal => RbSeries.method(:new_decimal),
283
+ Date => RbSeries.method(:new_from_any_values),
284
+ Datetime => RbSeries.method(:new_from_any_values),
285
+ Duration => RbSeries.method(:new_from_any_values),
286
+ Time => RbSeries.method(:new_from_any_values),
287
+ Boolean => RbSeries.method(:new_opt_bool),
288
+ Utf8 => RbSeries.method(:new_str),
289
+ Object => RbSeries.method(:new_object),
290
+ Categorical => RbSeries.method(:new_str),
291
+ Enum => RbSeries.method(:new_str),
292
+ Binary => RbSeries.method(:new_binary),
293
+ Null => RbSeries.method(:new_null)
294
+ }
295
+
296
+ def self.polars_type_to_constructor(dtype)
297
+ if dtype.is_a?(Array)
298
+ return lambda do |name, values, strict|
299
+ RbSeries.new_array(name, values, strict, dtype)
300
+ end
301
+ end
302
+
303
+ begin
304
+ base_type = dtype.base_type
305
+ POLARS_TYPE_TO_CONSTRUCTOR.fetch(base_type)
306
+ rescue KeyError
307
+ raise ArgumentError, "Cannot construct RbSeries for type #{dtype}."
308
+ end
309
+ end
310
+
311
+ RB_TYPE_TO_CONSTRUCTOR = {
312
+ Float => RbSeries.method(:new_opt_f64),
313
+ TrueClass => RbSeries.method(:new_opt_bool),
314
+ FalseClass => RbSeries.method(:new_opt_bool),
315
+ Integer => RbSeries.method(:new_opt_i64),
316
+ ::String => RbSeries.method(:new_str)
317
+ }
318
+
319
+ def self.rb_type_to_constructor(dtype)
320
+ RB_TYPE_TO_CONSTRUCTOR.fetch(dtype) do
321
+ if defined?(::BigDecimal) && dtype == ::BigDecimal
322
+ RbSeries.method(:new_decimal)
323
+ else
324
+ RbSeries.method(:new_object)
325
+ end
326
+ end
327
+ end
328
+
329
+ def self.numo_values_and_dtype(values)
330
+ [values, values.class]
331
+ end
332
+
333
+ def self.numo_type_to_constructor(dtype)
334
+ {
335
+ Numo::Float32 => RbSeries.method(:new_opt_f32),
336
+ Numo::Float64 => RbSeries.method(:new_opt_f64),
337
+ Numo::Int8 => RbSeries.method(:new_opt_i8),
338
+ Numo::Int16 => RbSeries.method(:new_opt_i16),
339
+ Numo::Int32 => RbSeries.method(:new_opt_i32),
340
+ Numo::Int64 => RbSeries.method(:new_opt_i64),
341
+ Numo::UInt8 => RbSeries.method(:new_opt_u8),
342
+ Numo::UInt16 => RbSeries.method(:new_opt_u16),
343
+ Numo::UInt32 => RbSeries.method(:new_opt_u32),
344
+ Numo::UInt64 => RbSeries.method(:new_opt_u64)
345
+ }.fetch(dtype)
346
+ rescue KeyError
347
+ RbSeries.method(:new_object)
348
+ end
349
+ end
350
+ end
@@ -0,0 +1,9 @@
1
+ module Polars
2
+ module Utils
3
+ def self.get_first_non_none(values)
4
+ if !values.nil?
5
+ values.find { |v| !v.nil? }
6
+ end
7
+ end
8
+ end
9
+ end
@@ -63,19 +63,24 @@ module Polars
63
63
  end
64
64
 
65
65
  def self._to_ruby_datetime(value, time_unit = "ns", time_zone = nil)
66
- if time_zone.nil? || time_zone == "" || time_zone == "UTC"
66
+ utc_time =
67
67
  if time_unit == "ns"
68
- ::Time.at(value / 1000000000, value % 1000000000, :nsec).utc
68
+ ::Time.at(value / 1_000_000_000, value % 1_000_000_000, :nsec).utc
69
69
  elsif time_unit == "us"
70
- ::Time.at(value / 1000000, value % 1000000, :usec).utc
70
+ ::Time.at(value / 1_000_000, value % 1_000_000, :usec).utc
71
71
  elsif time_unit == "ms"
72
- ::Time.at(value / 1000, value % 1000, :millisecond).utc
72
+ ::Time.at(value / 1_000, value % 1_000, :millisecond).utc
73
73
  else
74
74
  raise ArgumentError, "time_unit must be one of {{'ns', 'us', 'ms'}}, got #{time_unit}"
75
75
  end
76
- else
77
- raise Todo
76
+ return utc_time if time_zone.nil?
77
+
78
+ unless defined?(::TZInfo)
79
+ # TODO improve error message
80
+ require "tzinfo"
78
81
  end
82
+
83
+ utc_time.getlocal(::TZInfo::Timezone.get(time_zone))
79
84
  end
80
85
 
81
86
  def self._to_ruby_duration(value, time_unit = "ns")
@@ -90,8 +95,13 @@ module Polars
90
95
  end
91
96
  end
92
97
 
93
- def self._to_ruby_decimal(digits, scale)
94
- BigDecimal("#{digits}e#{scale}")
98
+ def self._to_ruby_decimal(prec, value)
99
+ unless defined?(::BigDecimal)
100
+ # TODO improve error message
101
+ require "bigdecimal"
102
+ end
103
+
104
+ BigDecimal(value, prec)
95
105
  end
96
106
  end
97
107
  end
@@ -0,0 +1,11 @@
1
+ module Polars
2
+ module Utils
3
+ def self.issue_deprecation_warning(message)
4
+ warn message
5
+ end
6
+
7
+ def self.deprecated(message)
8
+ issue_deprecation_warning(message)
9
+ end
10
+ end
11
+ end
@@ -5,7 +5,8 @@ module Polars
5
5
  str_as_lit: false,
6
6
  list_as_series: false,
7
7
  structify: false,
8
- dtype: nil
8
+ dtype: nil,
9
+ require_selector: false
9
10
  )
10
11
  if input.is_a?(Expr)
11
12
  expr = input
@@ -14,17 +15,22 @@ module Polars
14
15
  end
15
16
  elsif (input.is_a?(::String) || input.is_a?(Symbol)) && !str_as_lit
16
17
  expr = F.col(input)
17
- elsif input.is_a?(::Array) && list_as_series
18
- expr = F.lit(Series.new(input), dtype: dtype)
19
18
  else
20
- expr = F.lit(input, dtype: dtype)
19
+ if require_selector
20
+ msg = "cannot turn #{input.inspect} into selector"
21
+ raise TypeError, msg
22
+ elsif input.is_a?(::Array) && list_as_series
23
+ expr = F.lit(Series.new(input), dtype: dtype)
24
+ else
25
+ expr = F.lit(input, dtype: dtype)
26
+ end
21
27
  end
22
28
 
23
29
  expr._rbexpr
24
30
  end
25
31
 
26
- def self.parse_into_list_of_expressions(*inputs, __structify: false, **named_inputs)
27
- exprs = _parse_positional_inputs(inputs, structify: __structify)
32
+ def self.parse_into_list_of_expressions(*inputs, __structify: false, __require_selectors: false, **named_inputs)
33
+ exprs = _parse_positional_inputs(inputs, structify: __structify, require_selectors: __require_selectors)
28
34
  if named_inputs.any?
29
35
  named_exprs = _parse_named_inputs(named_inputs, structify: __structify)
30
36
  exprs.concat(named_exprs)
@@ -33,9 +39,49 @@ module Polars
33
39
  exprs
34
40
  end
35
41
 
36
- def self._parse_positional_inputs(inputs, structify: false)
42
+ def self.parse_into_selector(i, strict: true)
43
+ if i.is_a?(::String)
44
+ cs = Selectors
45
+
46
+ cs.by_name([i], require_all: strict)
47
+ elsif i.is_a?(Selector)
48
+ i
49
+ elsif i.is_a?(Expr)
50
+ i.meta.as_selector
51
+ else
52
+ msg = "cannot turn #{i.inspect} into selector"
53
+ raise TypeError, msg
54
+ end
55
+ end
56
+
57
+ def self.parse_list_into_selector(inputs, strict: true)
58
+ if inputs.is_a?(::Array)
59
+ cs = Selectors
60
+
61
+ columns = inputs.select { |i| i.is_a?(::String) }
62
+ selector = cs.by_name(columns, require_all: strict)
63
+
64
+ if columns.length == inputs.length
65
+ return selector
66
+ end
67
+
68
+ # A bit cleaner
69
+ if columns.length == 0
70
+ selector = cs.empty
71
+ end
72
+
73
+ inputs.each do |i|
74
+ selector |= parse_into_selector(i, strict: strict)
75
+ end
76
+ selector
77
+ else
78
+ parse_into_selector(inputs, strict: strict)
79
+ end
80
+ end
81
+
82
+ def self._parse_positional_inputs(inputs, structify: false, require_selectors: false)
37
83
  inputs_iter = _parse_inputs_as_iterable(inputs)
38
- inputs_iter.map { |e| parse_into_expression(e, structify: structify) }
84
+ inputs_iter.map { |e| parse_into_expression(e, structify: structify, require_selector: require_selectors) }
39
85
  end
40
86
 
41
87
  def self._parse_inputs_as_iterable(inputs)
@@ -52,7 +98,7 @@ module Polars
52
98
 
53
99
  def self._parse_named_inputs(named_inputs, structify: false)
54
100
  named_inputs.map do |name, input|
55
- parse_into_expression(input, structify: structify)._alias(name.to_s)
101
+ parse_into_expression(input, structify: structify).alias(name.to_s)
56
102
  end
57
103
  end
58
104
 
@@ -85,5 +131,12 @@ module Polars
85
131
 
86
132
  Plr.all_horizontal(predicates)
87
133
  end
134
+
135
+ def self._holidays_to_expr(holidays)
136
+ if !(holidays.is_a?(Expr) || holidays.is_a?(Series))
137
+ holidays = Series.new("", [holidays], strict: false, dtype: List.new(Date))
138
+ end
139
+ parse_into_expression(holidays, dtype: Date)
140
+ end
88
141
  end
89
142
  end
@@ -0,0 +1,43 @@
1
+ module Polars
2
+ module Utils
3
+ def self.reduce_balanced(function, iterable)
4
+ values = iterable.to_a
5
+
6
+ if values.empty?
7
+ msg = "reduce_balanced() of empty iterable"
8
+ raise TypeError, msg
9
+ end
10
+
11
+ if values.length == 1
12
+ return values.shift
13
+ end
14
+
15
+ stack = [[0, values.length]]
16
+
17
+ i = 0
18
+
19
+ while i < stack.length
20
+ offset, length = stack[i]
21
+ half = -(length / -2)
22
+
23
+ if length > 3
24
+ stack << [offset + half, length - half]
25
+ end
26
+
27
+ if length > 2
28
+ stack << [offset, half]
29
+ end
30
+
31
+ stack[i] = [offset, offset + half]
32
+
33
+ i += 1
34
+ end
35
+
36
+ stack.reverse.each do |idx_l, idx_r|
37
+ values[idx_l] = function.(values[idx_l], values[idx_r])
38
+ end
39
+
40
+ values[0]
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,22 @@
1
+ module Polars
2
+ module Utils
3
+ def self.serialize_polars_object(serializer, file)
4
+ serialize_to_bytes = lambda do
5
+ buf = StringIO.new
6
+ serializer.(buf)
7
+ buf.string
8
+ end
9
+
10
+ if file.nil?
11
+ serialize_to_bytes.call
12
+ elsif pathlike?(file)
13
+ file = normalize_filepath(file)
14
+ serializer.(file)
15
+ nil
16
+ else
17
+ serializer.(file)
18
+ nil
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ module Polars
2
+ module Utils
3
+ def self.issue_unstable_warning(message = nil)
4
+ warnings_enabled = ENV.fetch("POLARS_WARN_UNSTABLE", 0).to_i != 0
5
+ if !warnings_enabled
6
+ return
7
+ end
8
+
9
+ if message.nil?
10
+ message = "this functionality is considered unstable."
11
+ end
12
+ message += (
13
+ " It may be changed at any point without it being considered a breaking change."
14
+ )
15
+
16
+ warn message
17
+ end
18
+ end
19
+ end