polars-df 0.25.1 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -1
- data/Cargo.lock +268 -95
- data/LICENSE.txt +1 -1
- data/README.md +1 -3
- data/ext/polars/Cargo.toml +18 -18
- data/ext/polars/src/catalog/unity.rs +15 -20
- data/ext/polars/src/conversion/any_value.rs +25 -24
- data/ext/polars/src/conversion/chunked_array.rs +58 -56
- data/ext/polars/src/conversion/datetime.rs +58 -7
- data/ext/polars/src/conversion/mod.rs +155 -141
- data/ext/polars/src/dataframe/export.rs +15 -12
- data/ext/polars/src/dataframe/general.rs +5 -4
- data/ext/polars/src/dataframe/map.rs +6 -4
- data/ext/polars/src/error.rs +1 -1
- data/ext/polars/src/expr/array.rs +0 -24
- data/ext/polars/src/expr/datatype.rs +3 -2
- data/ext/polars/src/expr/datetime.rs +4 -4
- data/ext/polars/src/expr/general.rs +27 -15
- data/ext/polars/src/expr/list.rs +0 -26
- data/ext/polars/src/functions/business.rs +2 -2
- data/ext/polars/src/functions/io.rs +4 -3
- data/ext/polars/src/functions/lazy.rs +58 -46
- data/ext/polars/src/functions/meta.rs +6 -5
- data/ext/polars/src/functions/mod.rs +0 -1
- data/ext/polars/src/functions/utils.rs +4 -2
- data/ext/polars/src/interop/arrow/mod.rs +4 -2
- data/ext/polars/src/interop/numo/to_numo_series.rs +26 -25
- data/ext/polars/src/io/scan_options.rs +6 -3
- data/ext/polars/src/io/sink_options.rs +2 -0
- data/ext/polars/src/lazyframe/general.rs +28 -13
- data/ext/polars/src/lazyframe/optflags.rs +2 -1
- data/ext/polars/src/lib.rs +14 -33
- data/ext/polars/src/map/lazy.rs +5 -2
- data/ext/polars/src/map/series.rs +19 -18
- data/ext/polars/src/on_startup.rs +16 -7
- data/ext/polars/src/ruby/numo.rs +3 -4
- data/ext/polars/src/ruby/rb_modules.rs +2 -4
- data/ext/polars/src/ruby/ruby_udf.rs +7 -9
- data/ext/polars/src/ruby/utils.rs +12 -1
- data/ext/polars/src/series/aggregation.rs +13 -1
- data/ext/polars/src/series/export.rs +33 -38
- data/ext/polars/src/series/general.rs +4 -3
- data/ext/polars/src/series/map.rs +3 -2
- data/ext/polars/src/series/scatter.rs +4 -4
- data/ext/polars/src/utils.rs +31 -7
- data/lib/polars/array_expr.rb +23 -7
- data/lib/polars/array_name_space.rb +16 -2
- data/lib/polars/binary_name_space.rb +32 -0
- data/lib/polars/data_frame.rb +73 -10
- data/lib/polars/date_time_expr.rb +91 -3
- data/lib/polars/date_time_name_space.rb +7 -1
- data/lib/polars/expr.rb +122 -44
- data/lib/polars/functions/business.rb +2 -2
- data/lib/polars/functions/eager.rb +80 -7
- data/lib/polars/functions/lazy.rb +5 -2
- data/lib/polars/io/csv.rb +27 -5
- data/lib/polars/io/ipc.rb +1 -1
- data/lib/polars/io/lines.rb +4 -4
- data/lib/polars/io/sink_options.rb +4 -2
- data/lib/polars/lazy_frame.rb +97 -14
- data/lib/polars/list_expr.rb +21 -7
- data/lib/polars/list_name_space.rb +16 -2
- data/lib/polars/query_opt_flags.rb +22 -5
- data/lib/polars/selectors.rb +1 -1
- data/lib/polars/series.rb +88 -19
- data/lib/polars/sql_context.rb +2 -2
- data/lib/polars/string_cache.rb +19 -72
- data/lib/polars/string_expr.rb +1 -7
- data/lib/polars/string_name_space.rb +1 -7
- data/lib/polars/utils/construction/series.rb +8 -3
- data/lib/polars/utils/convert.rb +16 -6
- data/lib/polars/utils/parse.rb +7 -0
- data/lib/polars/utils/reduce_balanced.rb +43 -0
- data/lib/polars/utils/various.rb +5 -0
- data/lib/polars/version.rb +1 -1
- data/lib/polars.rb +1 -1
- metadata +3 -17
- data/ext/polars/src/functions/string_cache.rs +0 -24
data/LICENSE.txt
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Copyright (c) 2025 Ritchie Vink
|
|
2
|
-
Copyright (c) 2022-
|
|
2
|
+
Copyright (c) 2022-2026 Andrew Kane
|
|
3
3
|
Some portions Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
data/README.md
CHANGED
|
@@ -32,9 +32,7 @@ You can follow [Polars tutorials](https://docs.pola.rs/user-guide/getting-starte
|
|
|
32
32
|
- [DataFrame](https://www.rubydoc.info/gems/polars-df/Polars/DataFrame)
|
|
33
33
|
- [LazyFrame](https://www.rubydoc.info/gems/polars-df/Polars/LazyFrame)
|
|
34
34
|
|
|
35
|
-
##
|
|
36
|
-
|
|
37
|
-
### Creating DataFrames
|
|
35
|
+
## Creating DataFrames
|
|
38
36
|
|
|
39
37
|
From a CSV
|
|
40
38
|
|
data/ext/polars/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "polars-ruby"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.26.0"
|
|
4
4
|
license = "MIT"
|
|
5
5
|
authors = ["Andrew Kane <andrew@ankane.org>"]
|
|
6
6
|
edition = "2024"
|
|
@@ -13,37 +13,38 @@ crate-type = ["cdylib"]
|
|
|
13
13
|
|
|
14
14
|
[dependencies]
|
|
15
15
|
ahash = "0.8"
|
|
16
|
-
arrow = { package = "polars-arrow", version = "=0.
|
|
16
|
+
arrow = { package = "polars-arrow", version = "=0.54.4" }
|
|
17
17
|
bytes = "1"
|
|
18
18
|
chrono = "0.4"
|
|
19
|
+
chrono-tz = "0.10"
|
|
19
20
|
either = "1.8"
|
|
20
21
|
magnus = { version = "0.8", features = ["chrono"] }
|
|
21
22
|
num-traits = "0.2"
|
|
22
23
|
parking_lot = "0.12"
|
|
23
|
-
polars-buffer = "=0.
|
|
24
|
-
polars-compute = "=0.
|
|
25
|
-
polars-
|
|
26
|
-
polars-
|
|
27
|
-
polars-
|
|
28
|
-
polars-
|
|
29
|
-
polars-
|
|
30
|
-
polars-
|
|
31
|
-
polars-
|
|
32
|
-
polars-
|
|
33
|
-
polars-
|
|
34
|
-
polars-
|
|
24
|
+
polars-buffer = "=0.54.4"
|
|
25
|
+
polars-compute = "=0.54.4"
|
|
26
|
+
polars-config = "=0.54.4"
|
|
27
|
+
polars-core = "=0.54.4"
|
|
28
|
+
polars-dtype = "=0.54.4"
|
|
29
|
+
polars-error = "=0.54.4"
|
|
30
|
+
polars-io = "=0.54.4"
|
|
31
|
+
polars-lazy = { version = "=0.54.4", features = ["catalog"] }
|
|
32
|
+
polars-ops = "=0.54.4"
|
|
33
|
+
polars-plan = "=0.54.4"
|
|
34
|
+
polars-parquet = "=0.54.4"
|
|
35
|
+
polars-testing = "=0.54.4"
|
|
36
|
+
polars-utils = "=0.54.4"
|
|
35
37
|
rayon = "1.9"
|
|
36
38
|
rb-sys = "0.9"
|
|
37
39
|
regex = "1"
|
|
38
40
|
serde_json = "1"
|
|
39
41
|
|
|
40
42
|
[dependencies.polars]
|
|
41
|
-
version = "=0.
|
|
43
|
+
version = "=0.54.4"
|
|
42
44
|
features = [
|
|
43
45
|
"abs",
|
|
44
46
|
"approx_unique",
|
|
45
47
|
"arg_where",
|
|
46
|
-
"array_any_all",
|
|
47
48
|
"array_count",
|
|
48
49
|
"array_to_struct",
|
|
49
50
|
"asof_join",
|
|
@@ -93,7 +94,6 @@ features = [
|
|
|
93
94
|
"is_unique",
|
|
94
95
|
"json",
|
|
95
96
|
"lazy",
|
|
96
|
-
"list_any_all",
|
|
97
97
|
"list_count",
|
|
98
98
|
"list_drop_nulls",
|
|
99
99
|
"list_eval",
|
|
@@ -154,7 +154,7 @@ features = [
|
|
|
154
154
|
mimalloc = { version = "0.1", default-features = false }
|
|
155
155
|
|
|
156
156
|
[target.'cfg(target_os = "linux")'.dependencies]
|
|
157
|
-
tikv-jemallocator = { version = "0.
|
|
157
|
+
tikv-jemallocator = { version = "0.7", features = ["disable_initial_exec_tls"] }
|
|
158
158
|
|
|
159
159
|
[features]
|
|
160
160
|
default = []
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
use std::str::FromStr;
|
|
2
2
|
|
|
3
3
|
use magnus::value::{Lazy, ReprValue};
|
|
4
|
-
use magnus::{
|
|
4
|
+
use magnus::{Module, RClass, RHash, RModule, Ruby, Value};
|
|
5
5
|
use polars::prelude::{PlHashMap, PlSmallStr, PolarsError, Schema};
|
|
6
|
+
use polars_core::runtime::ASYNC;
|
|
6
7
|
use polars_io::catalog::unity::client::{CatalogClient, CatalogClientBuilder};
|
|
7
8
|
use polars_io::catalog::unity::models::{
|
|
8
9
|
CatalogInfo, ColumnInfo, DataSourceFormat, NamespaceInfo, TableInfo, TableType,
|
|
9
10
|
};
|
|
10
11
|
use polars_io::catalog::unity::schema::parse_type_json_str;
|
|
11
|
-
use polars_io::pl_async;
|
|
12
12
|
|
|
13
13
|
use crate::rb_modules::polars;
|
|
14
14
|
use crate::ruby::gvl::GvlExt;
|
|
15
|
+
use crate::ruby::utils::TryIntoValue;
|
|
15
16
|
use crate::utils::EnterPolarsExt;
|
|
16
17
|
use crate::utils::to_rb_err;
|
|
17
18
|
use crate::{RbResult, Wrap};
|
|
@@ -88,9 +89,7 @@ impl RbCatalogClient {
|
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
pub fn list_catalogs(rb: &Ruby, self_: &Self) -> RbResult<Value> {
|
|
91
|
-
let v = rb.enter_polars(||
|
|
92
|
-
pl_async::get_runtime().block_in_place_on(self_.client().list_catalogs())
|
|
93
|
-
})?;
|
|
92
|
+
let v = rb.enter_polars(|| ASYNC.block_in_place_on(self_.client().list_catalogs()))?;
|
|
94
93
|
|
|
95
94
|
let mut opt_err = None;
|
|
96
95
|
|
|
@@ -111,7 +110,7 @@ impl RbCatalogClient {
|
|
|
111
110
|
|
|
112
111
|
pub fn list_namespaces(rb: &Ruby, self_: &Self, catalog_name: String) -> RbResult<Value> {
|
|
113
112
|
let v = rb.enter_polars(|| {
|
|
114
|
-
|
|
113
|
+
ASYNC.block_in_place_on(self_.client().list_namespaces(&catalog_name))
|
|
115
114
|
})?;
|
|
116
115
|
|
|
117
116
|
let mut opt_err = None;
|
|
@@ -139,8 +138,7 @@ impl RbCatalogClient {
|
|
|
139
138
|
namespace: String,
|
|
140
139
|
) -> RbResult<Value> {
|
|
141
140
|
let v = rb.enter_polars(|| {
|
|
142
|
-
|
|
143
|
-
.block_in_place_on(self_.client().list_tables(&catalog_name, &namespace))
|
|
141
|
+
ASYNC.block_in_place_on(self_.client().list_tables(&catalog_name, &namespace))
|
|
144
142
|
})?;
|
|
145
143
|
|
|
146
144
|
let mut opt_err = None;
|
|
@@ -172,7 +170,7 @@ impl RbCatalogClient {
|
|
|
172
170
|
) -> RbResult<Value> {
|
|
173
171
|
let table_info = rb
|
|
174
172
|
.enter_polars(|| {
|
|
175
|
-
|
|
173
|
+
ASYNC.block_in_place_on(self_.client().get_table_info(
|
|
176
174
|
&table_name,
|
|
177
175
|
&catalog_name,
|
|
178
176
|
&namespace,
|
|
@@ -192,7 +190,7 @@ impl RbCatalogClient {
|
|
|
192
190
|
) -> RbResult<Value> {
|
|
193
191
|
let catalog_info = rb
|
|
194
192
|
.detach(|| {
|
|
195
|
-
|
|
193
|
+
ASYNC.block_in_place_on(self_.client().create_catalog(
|
|
196
194
|
&catalog_name,
|
|
197
195
|
comment.as_deref(),
|
|
198
196
|
storage_root.as_deref(),
|
|
@@ -209,11 +207,8 @@ impl RbCatalogClient {
|
|
|
209
207
|
catalog_name: String,
|
|
210
208
|
force: bool,
|
|
211
209
|
) -> RbResult<()> {
|
|
212
|
-
rb.detach(||
|
|
213
|
-
|
|
214
|
-
.block_in_place_on(self_.client().delete_catalog(&catalog_name, force))
|
|
215
|
-
})
|
|
216
|
-
.map_err(to_rb_err)
|
|
210
|
+
rb.detach(|| ASYNC.block_in_place_on(self_.client().delete_catalog(&catalog_name, force)))
|
|
211
|
+
.map_err(to_rb_err)
|
|
217
212
|
}
|
|
218
213
|
|
|
219
214
|
pub fn create_namespace(
|
|
@@ -226,7 +221,7 @@ impl RbCatalogClient {
|
|
|
226
221
|
) -> RbResult<Value> {
|
|
227
222
|
let namespace_info = rb
|
|
228
223
|
.detach(|| {
|
|
229
|
-
|
|
224
|
+
ASYNC.block_in_place_on(self_.client().create_namespace(
|
|
230
225
|
&catalog_name,
|
|
231
226
|
&namespace,
|
|
232
227
|
comment.as_deref(),
|
|
@@ -246,7 +241,7 @@ impl RbCatalogClient {
|
|
|
246
241
|
force: bool,
|
|
247
242
|
) -> RbResult<()> {
|
|
248
243
|
rb.detach(|| {
|
|
249
|
-
|
|
244
|
+
ASYNC.block_in_place_on(self_.client().delete_namespace(
|
|
250
245
|
&catalog_name,
|
|
251
246
|
&namespace,
|
|
252
247
|
force,
|
|
@@ -270,7 +265,7 @@ impl RbCatalogClient {
|
|
|
270
265
|
) -> RbResult<Value> {
|
|
271
266
|
let table_info = rb
|
|
272
267
|
.detach(|| {
|
|
273
|
-
|
|
268
|
+
ASYNC.block_in_place_on(
|
|
274
269
|
self_.client().create_table(
|
|
275
270
|
&catalog_name,
|
|
276
271
|
&namespace,
|
|
@@ -303,7 +298,7 @@ impl RbCatalogClient {
|
|
|
303
298
|
table_name: String,
|
|
304
299
|
) -> RbResult<()> {
|
|
305
300
|
rb.detach(|| {
|
|
306
|
-
|
|
301
|
+
ASYNC.block_in_place_on(self_.client().delete_table(
|
|
307
302
|
&catalog_name,
|
|
308
303
|
&namespace,
|
|
309
304
|
&table_name,
|
|
@@ -313,7 +308,7 @@ impl RbCatalogClient {
|
|
|
313
308
|
}
|
|
314
309
|
|
|
315
310
|
pub fn type_json_to_polars_type(rb: &Ruby, type_json: String) -> RbResult<Value> {
|
|
316
|
-
|
|
311
|
+
Wrap(parse_type_json_str(&type_json).map_err(to_rb_err)?).try_into_value_with(rb)
|
|
317
312
|
}
|
|
318
313
|
}
|
|
319
314
|
|
|
@@ -14,10 +14,11 @@ use super::{ObjectValue, Wrap, struct_dict};
|
|
|
14
14
|
|
|
15
15
|
use crate::rb_modules::pl_utils;
|
|
16
16
|
use crate::ruby::exceptions::RbOverflowError;
|
|
17
|
+
use crate::ruby::utils::TryIntoValue;
|
|
17
18
|
use crate::{RbErr, RbPolarsErr, RbResult, RbSeries, RbValueError};
|
|
18
19
|
|
|
19
|
-
impl
|
|
20
|
-
fn
|
|
20
|
+
impl TryIntoValue for Wrap<AnyValue<'_>> {
|
|
21
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
21
22
|
any_value_into_rb_object(self.0, ruby)
|
|
22
23
|
}
|
|
23
24
|
}
|
|
@@ -28,8 +29,8 @@ impl TryConvert for Wrap<AnyValue<'_>> {
|
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
pub(crate) fn any_value_into_rb_object(av: AnyValue, ruby: &Ruby) -> Value {
|
|
32
|
-
match av {
|
|
32
|
+
pub(crate) fn any_value_into_rb_object(av: AnyValue, ruby: &Ruby) -> RbResult<Value> {
|
|
33
|
+
let rb_object = match av {
|
|
33
34
|
AnyValue::UInt8(v) => ruby.into_value(v),
|
|
34
35
|
AnyValue::UInt16(v) => ruby.into_value(v),
|
|
35
36
|
AnyValue::UInt32(v) => ruby.into_value(v),
|
|
@@ -53,41 +54,38 @@ pub(crate) fn any_value_into_rb_object(av: AnyValue, ruby: &Ruby) -> Value {
|
|
|
53
54
|
AnyValue::CategoricalOwned(cat, map) | AnyValue::EnumOwned(cat, map) => unsafe {
|
|
54
55
|
map.cat_to_str_unchecked(cat).into_value_with(ruby)
|
|
55
56
|
},
|
|
56
|
-
AnyValue::Date(v) => pl_utils(ruby).funcall("_to_ruby_date", (v,))
|
|
57
|
+
AnyValue::Date(v) => pl_utils(ruby).funcall("_to_ruby_date", (v,))?,
|
|
57
58
|
AnyValue::Datetime(v, time_unit, time_zone) => {
|
|
58
|
-
datetime_to_rb_object(v, time_unit, time_zone)
|
|
59
|
+
datetime_to_rb_object(ruby, v, time_unit, time_zone)?
|
|
59
60
|
}
|
|
60
61
|
AnyValue::DatetimeOwned(v, time_unit, time_zone) => {
|
|
61
|
-
datetime_to_rb_object(v, time_unit, time_zone.as_ref().map(AsRef::as_ref))
|
|
62
|
+
datetime_to_rb_object(ruby, v, time_unit, time_zone.as_ref().map(AsRef::as_ref))?
|
|
62
63
|
}
|
|
63
64
|
AnyValue::Duration(v, time_unit) => {
|
|
64
65
|
let time_unit = time_unit.to_ascii();
|
|
65
|
-
pl_utils(ruby)
|
|
66
|
-
.funcall("_to_ruby_duration", (v, time_unit))
|
|
67
|
-
.unwrap()
|
|
66
|
+
pl_utils(ruby).funcall("_to_ruby_duration", (v, time_unit))?
|
|
68
67
|
}
|
|
69
|
-
AnyValue::Time(v) => pl_utils(ruby).funcall("_to_ruby_time", (v,))
|
|
70
|
-
AnyValue::Array(v, _) | AnyValue::List(v) => RbSeries::new(v)
|
|
71
|
-
ref av @ AnyValue::Struct(_, _, flds) => struct_dict(ruby, av._iter_struct_av(), flds)
|
|
72
|
-
AnyValue::StructOwned(payload) => struct_dict(ruby, payload.0.into_iter(), &payload.1)
|
|
68
|
+
AnyValue::Time(v) => pl_utils(ruby).funcall("_to_ruby_time", (v,))?,
|
|
69
|
+
AnyValue::Array(v, _) | AnyValue::List(v) => RbSeries::to_a(ruby, &RbSeries::new(v))?,
|
|
70
|
+
ref av @ AnyValue::Struct(_, _, flds) => struct_dict(ruby, av._iter_struct_av(), flds)?,
|
|
71
|
+
AnyValue::StructOwned(payload) => struct_dict(ruby, payload.0.into_iter(), &payload.1)?,
|
|
73
72
|
AnyValue::Object(v) => {
|
|
74
73
|
let object = v.as_any().downcast_ref::<ObjectValue>().unwrap();
|
|
75
|
-
object.
|
|
74
|
+
object.clone().into_value_with(ruby)
|
|
76
75
|
}
|
|
77
76
|
AnyValue::ObjectOwned(v) => {
|
|
78
77
|
let object = v.0.as_any().downcast_ref::<ObjectValue>().unwrap();
|
|
79
|
-
object.
|
|
78
|
+
object.clone().into_value_with(ruby)
|
|
80
79
|
}
|
|
81
80
|
AnyValue::Binary(v) => ruby.str_from_slice(v).as_value(),
|
|
82
81
|
AnyValue::BinaryOwned(v) => ruby.str_from_slice(&v).as_value(),
|
|
83
82
|
AnyValue::Decimal(v, prec, scale) => {
|
|
84
83
|
let mut buf = DecimalFmtBuffer::new();
|
|
85
84
|
let s = buf.format_dec128(v, scale, false, false);
|
|
86
|
-
pl_utils(ruby)
|
|
87
|
-
.funcall("_to_ruby_decimal", (prec, s))
|
|
88
|
-
.unwrap()
|
|
85
|
+
pl_utils(ruby).funcall("_to_ruby_decimal", (prec, s))?
|
|
89
86
|
}
|
|
90
|
-
}
|
|
87
|
+
};
|
|
88
|
+
Ok(rb_object)
|
|
91
89
|
}
|
|
92
90
|
|
|
93
91
|
pub(crate) fn rb_object_to_any_value<'s>(
|
|
@@ -130,7 +128,7 @@ pub(crate) fn rb_object_to_any_value<'s>(
|
|
|
130
128
|
|
|
131
129
|
fn get_str(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
|
|
132
130
|
let ruby = Ruby::get_with(ob);
|
|
133
|
-
let v = RString::
|
|
131
|
+
let v = RString::try_convert(ob)?;
|
|
134
132
|
if v.enc_get() == ruby.utf8_encindex() {
|
|
135
133
|
Ok(AnyValue::StringOwned(v.to_string()?.into()))
|
|
136
134
|
} else {
|
|
@@ -139,7 +137,7 @@ pub(crate) fn rb_object_to_any_value<'s>(
|
|
|
139
137
|
}
|
|
140
138
|
|
|
141
139
|
fn get_list(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
|
|
142
|
-
let v = RArray::
|
|
140
|
+
let v = RArray::try_convert(ob)?;
|
|
143
141
|
if v.is_empty() {
|
|
144
142
|
Ok(AnyValue::List(Series::new_empty(
|
|
145
143
|
PlSmallStr::EMPTY,
|
|
@@ -242,7 +240,7 @@ pub(crate) fn rb_object_to_any_value<'s>(
|
|
|
242
240
|
}
|
|
243
241
|
}
|
|
244
242
|
|
|
245
|
-
let (sign, digits, _, exp): (i8, String, i32, i32) = ob.funcall("split", ())
|
|
243
|
+
let (sign, digits, _, exp): (i8, String, i32, i32) = ob.funcall("split", ())?;
|
|
246
244
|
let (mut v, scale) = abs_decimal_from_digits(digits, exp).ok_or_else(|| {
|
|
247
245
|
RbErr::from(RbPolarsErr::Other(
|
|
248
246
|
"BigDecimal is too large to fit in Decimal128".into(),
|
|
@@ -279,7 +277,10 @@ pub(crate) fn rb_object_to_any_value<'s>(
|
|
|
279
277
|
get_datetime(ob, strict)
|
|
280
278
|
} else if ob.is_kind_of(crate::ruby::rb_modules::date(&ruby)) {
|
|
281
279
|
get_date(ob, strict)
|
|
282
|
-
} else if
|
|
280
|
+
} else if crate::ruby::rb_modules::bigdecimal(&ruby)
|
|
281
|
+
.map(|cls| ob.is_kind_of(cls))
|
|
282
|
+
.unwrap_or(false)
|
|
283
|
+
{
|
|
283
284
|
get_decimal(ob, strict)
|
|
284
285
|
} else {
|
|
285
286
|
if allow_object {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
use magnus::{IntoValue, RString, Ruby, TryConvert, Value, prelude::*};
|
|
1
|
+
use magnus::{IntoValue, RArray, RString, Ruby, TryConvert, Value, prelude::*};
|
|
2
2
|
use polars::prelude::*;
|
|
3
3
|
use polars_compute::decimal::DecimalFmtBuffer;
|
|
4
4
|
|
|
5
|
+
use super::datetime::datetime_to_rb_object;
|
|
5
6
|
use super::{Wrap, get_rbseq, struct_dict};
|
|
6
7
|
|
|
7
8
|
use crate::RbResult;
|
|
8
9
|
use crate::rb_modules::pl_utils;
|
|
10
|
+
use crate::ruby::utils::TryIntoValue;
|
|
9
11
|
|
|
10
12
|
impl TryConvert for Wrap<StringChunked> {
|
|
11
13
|
fn try_convert(obj: Value) -> RbResult<Self> {
|
|
@@ -41,7 +43,7 @@ impl TryConvert for Wrap<BinaryChunked> {
|
|
|
41
43
|
|
|
42
44
|
impl IntoValue for Wrap<&StringChunked> {
|
|
43
45
|
fn into_value_with(self, ruby: &Ruby) -> Value {
|
|
44
|
-
let iter = self.0.
|
|
46
|
+
let iter = self.0.iter();
|
|
45
47
|
ruby.ary_from_iter(iter).as_value()
|
|
46
48
|
}
|
|
47
49
|
}
|
|
@@ -50,96 +52,96 @@ impl IntoValue for Wrap<&BinaryChunked> {
|
|
|
50
52
|
fn into_value_with(self, ruby: &Ruby) -> Value {
|
|
51
53
|
let iter = self
|
|
52
54
|
.0
|
|
53
|
-
.
|
|
55
|
+
.iter()
|
|
54
56
|
.map(|opt_bytes| opt_bytes.map(|v| ruby.str_from_slice(v)));
|
|
55
57
|
ruby.ary_from_iter(iter).as_value()
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
impl
|
|
60
|
-
fn
|
|
61
|
+
impl TryIntoValue for Wrap<&StructChunked> {
|
|
62
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
61
63
|
let s = self.0.clone().into_series();
|
|
62
64
|
// todo! iterate its chunks and flatten.
|
|
63
65
|
// make series::iter() accept a chunk index.
|
|
64
66
|
let s = s.rechunk();
|
|
65
67
|
let iter = s.iter().map(|av| match av {
|
|
66
68
|
AnyValue::Struct(_, _, flds) => struct_dict(ruby, av._iter_struct_av(), flds),
|
|
67
|
-
AnyValue::Null => ruby.qnil().as_value(),
|
|
69
|
+
AnyValue::Null => Ok(ruby.qnil().as_value()),
|
|
68
70
|
_ => unreachable!(),
|
|
69
71
|
});
|
|
70
72
|
|
|
71
|
-
ruby.
|
|
73
|
+
ruby.ary_try_from_iter(iter).map(|v| v.as_value())
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
impl
|
|
76
|
-
fn
|
|
77
|
+
impl TryIntoValue for Wrap<&DurationChunked> {
|
|
78
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
77
79
|
let utils = pl_utils(ruby);
|
|
78
80
|
let time_unit = Wrap(self.0.time_unit()).into_value_with(ruby);
|
|
79
|
-
let iter = self.0.physical().
|
|
80
|
-
opt_v
|
|
81
|
-
utils
|
|
82
|
-
|
|
83
|
-
.unwrap()
|
|
84
|
-
})
|
|
81
|
+
let iter = self.0.physical().iter().map(|opt_v| {
|
|
82
|
+
opt_v
|
|
83
|
+
.map(|v| utils.funcall::<_, _, Value>("_to_ruby_duration", (v, time_unit)))
|
|
84
|
+
.transpose()
|
|
85
85
|
});
|
|
86
|
-
ruby.
|
|
86
|
+
ruby.ary_try_from_iter(iter).map(|v| v.as_value())
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
impl
|
|
91
|
-
fn
|
|
92
|
-
let
|
|
93
|
-
let time_unit =
|
|
94
|
-
let
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
.map(|v| v.into_value_with(ruby));
|
|
99
|
-
let iter = self.0.physical().into_iter().map(|opt_v| {
|
|
100
|
-
opt_v.map(|v| {
|
|
101
|
-
utils
|
|
102
|
-
.funcall::<_, _, Value>("_to_ruby_datetime", (v, time_unit, time_zone))
|
|
103
|
-
.unwrap()
|
|
104
|
-
})
|
|
90
|
+
impl TryIntoValue for Wrap<&DatetimeChunked> {
|
|
91
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
92
|
+
let time_zone = self.0.time_zone().as_ref();
|
|
93
|
+
let time_unit = self.0.time_unit();
|
|
94
|
+
let iter = self.0.physical().iter().map(|opt_v| {
|
|
95
|
+
opt_v
|
|
96
|
+
.map(|v| datetime_to_rb_object(ruby, v, time_unit, time_zone))
|
|
97
|
+
.transpose()
|
|
105
98
|
});
|
|
106
|
-
ruby.
|
|
99
|
+
ruby.ary_try_from_iter(iter).map(|v| v.as_value())
|
|
107
100
|
}
|
|
108
101
|
}
|
|
109
102
|
|
|
110
|
-
impl
|
|
111
|
-
fn
|
|
103
|
+
impl TryIntoValue for Wrap<&TimeChunked> {
|
|
104
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
112
105
|
let utils = pl_utils(ruby);
|
|
113
|
-
let iter = self.0.physical().
|
|
114
|
-
opt_v
|
|
106
|
+
let iter = self.0.physical().iter().map(|opt_v| {
|
|
107
|
+
opt_v
|
|
108
|
+
.map(|v| utils.funcall::<_, _, Value>("_to_ruby_time", (v,)))
|
|
109
|
+
.transpose()
|
|
115
110
|
});
|
|
116
|
-
ruby.
|
|
111
|
+
ruby.ary_try_from_iter(iter).map(|v| v.as_value())
|
|
117
112
|
}
|
|
118
113
|
}
|
|
119
114
|
|
|
120
|
-
impl
|
|
121
|
-
fn
|
|
115
|
+
impl TryIntoValue for Wrap<&DateChunked> {
|
|
116
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
122
117
|
let utils = pl_utils(ruby);
|
|
123
|
-
let iter = self.0.physical().
|
|
124
|
-
opt_v
|
|
118
|
+
let iter = self.0.physical().iter().map(|opt_v| {
|
|
119
|
+
opt_v
|
|
120
|
+
.map(|v| utils.funcall::<_, _, Value>("_to_ruby_date", (v,)))
|
|
121
|
+
.transpose()
|
|
125
122
|
});
|
|
126
|
-
ruby.
|
|
123
|
+
ruby.ary_try_from_iter(iter).map(|v| v.as_value())
|
|
127
124
|
}
|
|
128
125
|
}
|
|
129
126
|
|
|
130
|
-
impl
|
|
131
|
-
fn
|
|
132
|
-
let
|
|
133
|
-
|
|
134
|
-
let mut buf = DecimalFmtBuffer::new();
|
|
135
|
-
let iter = self.0.physical().into_iter().map(|opt_v| {
|
|
136
|
-
opt_v.map(|v| {
|
|
137
|
-
let s = buf.format_dec128(v, self.0.scale(), false, false);
|
|
138
|
-
utils
|
|
139
|
-
.funcall::<_, _, Value>("_to_ruby_decimal", (rb_precision, s))
|
|
140
|
-
.unwrap()
|
|
141
|
-
})
|
|
142
|
-
});
|
|
143
|
-
ruby.ary_from_iter(iter).as_value()
|
|
127
|
+
impl TryIntoValue for Wrap<&DecimalChunked> {
|
|
128
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
129
|
+
let iter = decimal_to_rbobject_iter(ruby, self.0)?;
|
|
130
|
+
Ok(iter.as_value())
|
|
144
131
|
}
|
|
145
132
|
}
|
|
133
|
+
|
|
134
|
+
pub(crate) fn decimal_to_rbobject_iter(ruby: &Ruby, ca: &DecimalChunked) -> RbResult<RArray> {
|
|
135
|
+
let utils = pl_utils(ruby);
|
|
136
|
+
let rb_precision = ca.precision().into_value_with(ruby);
|
|
137
|
+
let mut buf = DecimalFmtBuffer::new();
|
|
138
|
+
let iter = ca.physical().iter().map(move |opt_v| {
|
|
139
|
+
opt_v
|
|
140
|
+
.map(|v| {
|
|
141
|
+
let s = buf.format_dec128(v, ca.scale(), false, false);
|
|
142
|
+
utils.funcall::<_, _, Value>("_to_ruby_decimal", (rb_precision, s))
|
|
143
|
+
})
|
|
144
|
+
.transpose()
|
|
145
|
+
});
|
|
146
|
+
ruby.ary_try_from_iter(iter)
|
|
147
|
+
}
|
|
@@ -1,12 +1,63 @@
|
|
|
1
|
-
|
|
1
|
+
//! Utilities for converting dates, times, datetimes, and so on.
|
|
2
|
+
|
|
3
|
+
use std::str::FromStr;
|
|
4
|
+
|
|
5
|
+
use chrono::{DateTime, Datelike, FixedOffset, NaiveDateTime, TimeDelta, TimeZone as _};
|
|
6
|
+
use chrono_tz::Tz;
|
|
7
|
+
use magnus::{IntoValue, Ruby, Value, prelude::*};
|
|
2
8
|
use polars::prelude::*;
|
|
3
9
|
|
|
4
10
|
use crate::rb_modules::pl_utils;
|
|
11
|
+
use crate::{RbPolarsErr, RbResult};
|
|
12
|
+
|
|
13
|
+
pub fn elapsed_offset_to_timedelta(elapsed: i64, time_unit: TimeUnit) -> TimeDelta {
|
|
14
|
+
let (in_second, nano_multiplier) = match time_unit {
|
|
15
|
+
TimeUnit::Nanoseconds => (1_000_000_000, 1),
|
|
16
|
+
TimeUnit::Microseconds => (1_000_000, 1_000),
|
|
17
|
+
TimeUnit::Milliseconds => (1_000, 1_000_000),
|
|
18
|
+
};
|
|
19
|
+
let mut elapsed_sec = elapsed / in_second;
|
|
20
|
+
let mut elapsed_nanos = nano_multiplier * (elapsed % in_second);
|
|
21
|
+
if elapsed_nanos < 0 {
|
|
22
|
+
// TimeDelta expects nanos to always be positive.
|
|
23
|
+
elapsed_sec -= 1;
|
|
24
|
+
elapsed_nanos += 1_000_000_000;
|
|
25
|
+
}
|
|
26
|
+
TimeDelta::new(elapsed_sec, elapsed_nanos as u32).unwrap()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// Convert time-units-since-epoch to a more structured object.
|
|
30
|
+
pub fn timestamp_to_naive_datetime(since_epoch: i64, time_unit: TimeUnit) -> NaiveDateTime {
|
|
31
|
+
DateTime::UNIX_EPOCH.naive_utc() + elapsed_offset_to_timedelta(since_epoch, time_unit)
|
|
32
|
+
}
|
|
5
33
|
|
|
6
|
-
pub fn datetime_to_rb_object(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
34
|
+
pub fn datetime_to_rb_object(
|
|
35
|
+
ruby: &Ruby,
|
|
36
|
+
v: i64,
|
|
37
|
+
tu: TimeUnit,
|
|
38
|
+
tz: Option<&TimeZone>,
|
|
39
|
+
) -> RbResult<Value> {
|
|
40
|
+
if let Some(time_zone) = tz {
|
|
41
|
+
if let Ok(tz) = Tz::from_str(time_zone) {
|
|
42
|
+
let utc_datetime = DateTime::UNIX_EPOCH + elapsed_offset_to_timedelta(v, tu);
|
|
43
|
+
if utc_datetime.year() >= 2100 {
|
|
44
|
+
// chrono-tz does not support dates after 2100
|
|
45
|
+
// https://github.com/chronotope/chrono-tz/issues/135
|
|
46
|
+
pl_utils(ruby).funcall("_to_ruby_datetime", (v, tu.to_ascii(), time_zone.as_str()))
|
|
47
|
+
} else {
|
|
48
|
+
let datetime = utc_datetime.with_timezone(&tz);
|
|
49
|
+
Ok(datetime.fixed_offset().into_value_with(ruby))
|
|
50
|
+
}
|
|
51
|
+
} else if let Ok(tz) = FixedOffset::from_str(time_zone) {
|
|
52
|
+
let naive_datetime = timestamp_to_naive_datetime(v, tu);
|
|
53
|
+
let datetime = tz.from_utc_datetime(&naive_datetime);
|
|
54
|
+
Ok(datetime.into_value_with(ruby))
|
|
55
|
+
} else {
|
|
56
|
+
Err(RbPolarsErr::Other(format!("Could not parse timezone: {time_zone}")).into())
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
Ok(timestamp_to_naive_datetime(v, tu)
|
|
60
|
+
.and_utc()
|
|
61
|
+
.into_value_with(ruby))
|
|
62
|
+
}
|
|
12
63
|
}
|