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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +274 -0
- data/Cargo.lock +1465 -867
- data/Cargo.toml +3 -0
- data/LICENSE.txt +2 -2
- data/README.md +87 -37
- data/ext/polars/Cargo.toml +47 -16
- data/ext/polars/src/c_api/allocator.rs +7 -0
- data/ext/polars/src/c_api/mod.rs +1 -0
- data/ext/polars/src/catalog/mod.rs +1 -0
- data/ext/polars/src/catalog/unity.rs +470 -0
- data/ext/polars/src/conversion/any_value.rs +99 -84
- data/ext/polars/src/conversion/categorical.rs +30 -0
- data/ext/polars/src/conversion/chunked_array.rs +71 -62
- data/ext/polars/src/conversion/datetime.rs +63 -0
- data/ext/polars/src/conversion/mod.rs +796 -312
- data/ext/polars/src/dataframe/construction.rs +6 -18
- data/ext/polars/src/dataframe/export.rs +30 -39
- data/ext/polars/src/dataframe/general.rs +294 -362
- data/ext/polars/src/dataframe/io.rs +33 -150
- data/ext/polars/src/dataframe/map.rs +175 -0
- data/ext/polars/src/dataframe/mod.rs +37 -5
- data/ext/polars/src/dataframe/serde.rs +23 -8
- data/ext/polars/src/error.rs +44 -7
- data/ext/polars/src/exceptions.rs +21 -8
- data/ext/polars/src/expr/array.rs +86 -22
- data/ext/polars/src/expr/binary.rs +50 -1
- data/ext/polars/src/expr/bitwise.rs +39 -0
- data/ext/polars/src/expr/categorical.rs +20 -0
- data/ext/polars/src/expr/datatype.rs +51 -0
- data/ext/polars/src/expr/datetime.rs +99 -41
- data/ext/polars/src/expr/extension.rs +12 -0
- data/ext/polars/src/expr/general.rs +252 -128
- data/ext/polars/src/expr/list.rs +56 -60
- data/ext/polars/src/expr/meta.rs +30 -35
- data/ext/polars/src/expr/mod.rs +28 -6
- data/ext/polars/src/expr/name.rs +29 -14
- data/ext/polars/src/expr/rolling.rs +111 -3
- data/ext/polars/src/expr/selector.rs +219 -0
- data/ext/polars/src/expr/serde.rs +28 -0
- data/ext/polars/src/expr/string.rs +118 -20
- data/ext/polars/src/expr/struct.rs +14 -1
- data/ext/polars/src/file.rs +194 -86
- data/ext/polars/src/functions/aggregation.rs +13 -12
- data/ext/polars/src/functions/business.rs +2 -3
- data/ext/polars/src/functions/eager.rs +3 -2
- data/ext/polars/src/functions/io.rs +90 -18
- data/ext/polars/src/functions/lazy.rs +267 -118
- data/ext/polars/src/functions/meta.rs +8 -7
- data/ext/polars/src/functions/misc.rs +1 -1
- data/ext/polars/src/functions/mod.rs +2 -1
- data/ext/polars/src/functions/range.rs +88 -31
- data/ext/polars/src/functions/strings.rs +6 -0
- data/ext/polars/src/functions/utils.rs +8 -0
- data/ext/polars/src/interop/arrow/mod.rs +52 -1
- data/ext/polars/src/interop/arrow/{to_ruby.rs → to_rb.rs} +37 -7
- data/ext/polars/src/interop/arrow/to_rust.rs +43 -0
- data/ext/polars/src/interop/numo/to_numo_df.rs +1 -1
- data/ext/polars/src/interop/numo/to_numo_series.rs +72 -50
- data/ext/polars/src/io/cloud_options.rs +107 -0
- data/ext/polars/src/io/mod.rs +4 -0
- data/ext/polars/src/io/scan_options.rs +113 -0
- data/ext/polars/src/io/sink_options.rs +46 -0
- data/ext/polars/src/io/sink_output.rs +21 -0
- data/ext/polars/src/lazyframe/exitable.rs +39 -0
- data/ext/polars/src/lazyframe/general.rs +846 -368
- data/ext/polars/src/lazyframe/mod.rs +58 -5
- data/ext/polars/src/lazyframe/optflags.rs +59 -0
- data/ext/polars/src/lazyframe/serde.rs +36 -4
- data/ext/polars/src/lazyframe/sink.rs +46 -0
- data/ext/polars/src/lazygroupby.rs +38 -9
- data/ext/polars/src/lib.rs +574 -165
- data/ext/polars/src/map/lazy.rs +44 -74
- data/ext/polars/src/map/mod.rs +18 -254
- data/ext/polars/src/map/series.rs +241 -1087
- data/ext/polars/src/on_startup.rs +192 -9
- data/ext/polars/src/prelude.rs +1 -0
- data/ext/polars/src/rb_modules.rs +10 -57
- data/ext/polars/src/ruby/exceptions.rs +26 -0
- data/ext/polars/src/ruby/gvl.rs +104 -0
- data/ext/polars/src/ruby/lazy.rs +46 -0
- data/ext/polars/src/ruby/mod.rs +11 -0
- data/ext/polars/src/ruby/numo.rs +52 -0
- data/ext/polars/src/ruby/plan_callback.rs +198 -0
- data/ext/polars/src/ruby/rb_modules.rs +16 -0
- data/ext/polars/src/ruby/ruby_convert_registry.rs +51 -0
- data/ext/polars/src/ruby/ruby_function.rs +11 -0
- data/ext/polars/src/ruby/ruby_udf.rs +164 -0
- data/ext/polars/src/ruby/thread.rs +65 -0
- data/ext/polars/src/ruby/utils.rs +39 -0
- data/ext/polars/src/series/aggregation.rs +116 -91
- data/ext/polars/src/series/arithmetic.rs +16 -22
- data/ext/polars/src/series/comparison.rs +101 -222
- data/ext/polars/src/series/construction.rs +80 -70
- data/ext/polars/src/series/export.rs +98 -56
- data/ext/polars/src/series/general.rs +323 -440
- data/ext/polars/src/series/import.rs +22 -5
- data/ext/polars/src/series/map.rs +103 -0
- data/ext/polars/src/series/mod.rs +57 -15
- data/ext/polars/src/series/scatter.rs +139 -82
- data/ext/polars/src/sql.rs +16 -9
- data/ext/polars/src/testing/frame.rs +31 -0
- data/ext/polars/src/testing/mod.rs +5 -0
- data/ext/polars/src/testing/series.rs +31 -0
- data/ext/polars/src/timeout.rs +105 -0
- data/ext/polars/src/utils.rs +105 -4
- data/lib/polars/array_expr.rb +500 -22
- data/lib/polars/array_name_space.rb +384 -10
- data/lib/polars/batched_csv_reader.rb +48 -66
- data/lib/polars/binary_expr.rb +217 -0
- data/lib/polars/binary_name_space.rb +155 -1
- data/lib/polars/cat_expr.rb +224 -0
- data/lib/polars/cat_name_space.rb +132 -32
- data/lib/polars/catalog/unity/catalog_info.rb +20 -0
- data/lib/polars/catalog/unity/column_info.rb +31 -0
- data/lib/polars/catalog/unity/namespace_info.rb +21 -0
- data/lib/polars/catalog/unity/table_info.rb +50 -0
- data/lib/polars/catalog.rb +448 -0
- data/lib/polars/collect_batches.rb +22 -0
- data/lib/polars/config.rb +3 -3
- data/lib/polars/convert.rb +201 -36
- data/lib/polars/data_frame.rb +2851 -1017
- data/lib/polars/data_frame_plot.rb +173 -0
- data/lib/polars/data_type_expr.rb +52 -0
- data/lib/polars/data_type_group.rb +6 -0
- data/lib/polars/data_types.rb +118 -18
- data/lib/polars/date_time_expr.rb +426 -84
- data/lib/polars/date_time_name_space.rb +384 -111
- data/lib/polars/dynamic_group_by.rb +102 -10
- data/lib/polars/exceptions.rb +50 -5
- data/lib/polars/expr.rb +2159 -915
- data/lib/polars/extension_expr.rb +39 -0
- data/lib/polars/extension_name_space.rb +39 -0
- data/lib/polars/functions/aggregation/horizontal.rb +11 -6
- data/lib/polars/functions/aggregation/vertical.rb +2 -3
- data/lib/polars/functions/as_datatype.rb +290 -8
- data/lib/polars/functions/business.rb +95 -0
- data/lib/polars/functions/col.rb +6 -5
- data/lib/polars/functions/datatype.rb +62 -0
- data/lib/polars/functions/eager.rb +426 -24
- data/lib/polars/functions/escape_regex.rb +21 -0
- data/lib/polars/functions/lazy.rb +813 -195
- data/lib/polars/functions/lit.rb +21 -10
- data/lib/polars/functions/range/int_range.rb +74 -2
- data/lib/polars/functions/range/linear_space.rb +195 -0
- data/lib/polars/functions/range/time_range.rb +1 -1
- data/lib/polars/functions/repeat.rb +7 -12
- data/lib/polars/functions/whenthen.rb +2 -2
- data/lib/polars/group_by.rb +188 -58
- data/lib/polars/iceberg_dataset.rb +108 -0
- data/lib/polars/in_process_query.rb +37 -0
- data/lib/polars/io/cloud.rb +18 -0
- data/lib/polars/io/csv.rb +336 -128
- data/lib/polars/io/database.rb +19 -4
- data/lib/polars/io/delta.rb +134 -0
- data/lib/polars/io/iceberg.rb +34 -0
- data/lib/polars/io/ipc.rb +63 -63
- data/lib/polars/io/json.rb +16 -0
- data/lib/polars/io/lines.rb +172 -0
- data/lib/polars/io/ndjson.rb +176 -20
- data/lib/polars/io/parquet.rb +173 -95
- data/lib/polars/io/scan_options.rb +55 -0
- data/lib/polars/io/sink_options.rb +27 -0
- data/lib/polars/io/utils.rb +17 -0
- data/lib/polars/lazy_frame.rb +3017 -622
- data/lib/polars/lazy_group_by.rb +436 -2
- data/lib/polars/list_expr.rb +551 -59
- data/lib/polars/list_name_space.rb +465 -51
- data/lib/polars/meta_expr.rb +146 -24
- data/lib/polars/name_expr.rb +87 -2
- data/lib/polars/query_opt_flags.rb +264 -0
- data/lib/polars/rolling_group_by.rb +90 -5
- data/lib/polars/scan_cast_options.rb +86 -0
- data/lib/polars/schema.rb +128 -0
- data/lib/polars/selector.rb +245 -0
- data/lib/polars/selectors.rb +1048 -201
- data/lib/polars/series.rb +2522 -774
- data/lib/polars/series_plot.rb +72 -0
- data/lib/polars/slice.rb +1 -1
- data/lib/polars/sql_context.rb +13 -6
- data/lib/polars/string_cache.rb +19 -72
- data/lib/polars/string_expr.rb +561 -107
- data/lib/polars/string_name_space.rb +781 -109
- data/lib/polars/struct_expr.rb +139 -18
- data/lib/polars/struct_name_space.rb +19 -1
- data/lib/polars/testing.rb +24 -273
- data/lib/polars/utils/constants.rb +2 -0
- data/lib/polars/utils/construction/data_frame.rb +410 -0
- data/lib/polars/utils/construction/series.rb +350 -0
- data/lib/polars/utils/construction/utils.rb +9 -0
- data/lib/polars/utils/convert.rb +18 -8
- data/lib/polars/utils/deprecation.rb +11 -0
- data/lib/polars/utils/parse.rb +62 -9
- data/lib/polars/utils/reduce_balanced.rb +43 -0
- data/lib/polars/utils/serde.rb +22 -0
- data/lib/polars/utils/unstable.rb +19 -0
- data/lib/polars/utils/various.rb +86 -1
- data/lib/polars/utils.rb +63 -48
- data/lib/polars/version.rb +1 -1
- data/lib/polars.rb +85 -2
- metadata +80 -28
- data/ext/polars/src/allocator.rs +0 -13
- data/ext/polars/src/batched_csv.rs +0 -138
- data/ext/polars/src/functions/string_cache.rs +0 -25
- data/ext/polars/src/map/dataframe.rs +0 -338
- data/lib/polars/plot.rb +0 -109
|
@@ -1,101 +1,98 @@
|
|
|
1
|
-
use magnus::encoding::
|
|
1
|
+
use magnus::encoding::EncodingCapable;
|
|
2
2
|
use magnus::{
|
|
3
|
-
|
|
3
|
+
IntoValue, RArray, RHash, RString, Ruby, Symbol, TryConvert, Value, prelude::*,
|
|
4
|
+
r_hash::ForEach, value::Opaque,
|
|
4
5
|
};
|
|
6
|
+
use num_traits::ToPrimitive;
|
|
7
|
+
use polars::chunked_array::object::PolarsObjectSafe;
|
|
5
8
|
use polars::prelude::*;
|
|
9
|
+
use polars_compute::decimal::{DEC128_MAX_PREC, DecimalFmtBuffer, dec128_fits};
|
|
6
10
|
use polars_core::utils::any_values_to_supertype_and_n_dtypes;
|
|
7
11
|
|
|
8
|
-
use super::
|
|
12
|
+
use super::datetime::datetime_to_rb_object;
|
|
13
|
+
use super::{ObjectValue, Wrap, struct_dict};
|
|
9
14
|
|
|
10
|
-
use crate::
|
|
11
|
-
use crate::
|
|
12
|
-
use crate::
|
|
15
|
+
use crate::rb_modules::pl_utils;
|
|
16
|
+
use crate::ruby::exceptions::RbOverflowError;
|
|
17
|
+
use crate::ruby::utils::TryIntoValue;
|
|
18
|
+
use crate::{RbErr, RbPolarsErr, RbResult, RbSeries, RbValueError};
|
|
13
19
|
|
|
14
|
-
impl
|
|
15
|
-
fn
|
|
20
|
+
impl TryIntoValue for Wrap<AnyValue<'_>> {
|
|
21
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
16
22
|
any_value_into_rb_object(self.0, ruby)
|
|
17
23
|
}
|
|
18
24
|
}
|
|
19
25
|
|
|
20
|
-
impl
|
|
26
|
+
impl TryConvert for Wrap<AnyValue<'_>> {
|
|
21
27
|
fn try_convert(ob: Value) -> RbResult<Self> {
|
|
22
|
-
rb_object_to_any_value(ob, true).map(Wrap)
|
|
28
|
+
rb_object_to_any_value(ob, true, true).map(Wrap)
|
|
23
29
|
}
|
|
24
30
|
}
|
|
25
31
|
|
|
26
|
-
pub(crate) fn any_value_into_rb_object(av: AnyValue, ruby: &Ruby) -> Value {
|
|
27
|
-
match av {
|
|
32
|
+
pub(crate) fn any_value_into_rb_object(av: AnyValue, ruby: &Ruby) -> RbResult<Value> {
|
|
33
|
+
let rb_object = match av {
|
|
28
34
|
AnyValue::UInt8(v) => ruby.into_value(v),
|
|
29
35
|
AnyValue::UInt16(v) => ruby.into_value(v),
|
|
30
36
|
AnyValue::UInt32(v) => ruby.into_value(v),
|
|
31
37
|
AnyValue::UInt64(v) => ruby.into_value(v),
|
|
38
|
+
AnyValue::UInt128(v) => ruby.into_value(v),
|
|
32
39
|
AnyValue::Int8(v) => ruby.into_value(v),
|
|
33
40
|
AnyValue::Int16(v) => ruby.into_value(v),
|
|
34
41
|
AnyValue::Int32(v) => ruby.into_value(v),
|
|
35
42
|
AnyValue::Int64(v) => ruby.into_value(v),
|
|
43
|
+
AnyValue::Int128(v) => ruby.into_value(v),
|
|
44
|
+
AnyValue::Float16(v) => ruby.into_value(v.to_f32()),
|
|
36
45
|
AnyValue::Float32(v) => ruby.into_value(v),
|
|
37
46
|
AnyValue::Float64(v) => ruby.into_value(v),
|
|
38
47
|
AnyValue::Null => ruby.qnil().as_value(),
|
|
39
48
|
AnyValue::Boolean(v) => ruby.into_value(v),
|
|
40
49
|
AnyValue::String(v) => ruby.into_value(v),
|
|
41
50
|
AnyValue::StringOwned(v) => ruby.into_value(v.as_str()),
|
|
42
|
-
AnyValue::Categorical(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
AnyValue::CategoricalOwned(idx, rev, arr) | AnyValue::EnumOwned(idx, rev, arr) => {
|
|
51
|
-
let s = if arr.is_null() {
|
|
52
|
-
rev.get(idx)
|
|
53
|
-
} else {
|
|
54
|
-
unsafe { arr.deref_unchecked().value(idx as usize) }
|
|
55
|
-
};
|
|
56
|
-
s.into_value()
|
|
57
|
-
}
|
|
58
|
-
AnyValue::Date(v) => utils().funcall("_to_ruby_date", (v,)).unwrap(),
|
|
51
|
+
AnyValue::Categorical(cat, map) | AnyValue::Enum(cat, map) => unsafe {
|
|
52
|
+
map.cat_to_str_unchecked(cat).into_value_with(ruby)
|
|
53
|
+
},
|
|
54
|
+
AnyValue::CategoricalOwned(cat, map) | AnyValue::EnumOwned(cat, map) => unsafe {
|
|
55
|
+
map.cat_to_str_unchecked(cat).into_value_with(ruby)
|
|
56
|
+
},
|
|
57
|
+
AnyValue::Date(v) => pl_utils(ruby).funcall("_to_ruby_date", (v,))?,
|
|
59
58
|
AnyValue::Datetime(v, time_unit, time_zone) => {
|
|
60
|
-
datetime_to_rb_object(v, time_unit, time_zone)
|
|
59
|
+
datetime_to_rb_object(ruby, v, time_unit, time_zone)?
|
|
61
60
|
}
|
|
62
61
|
AnyValue::DatetimeOwned(v, time_unit, time_zone) => {
|
|
63
|
-
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))?
|
|
64
63
|
}
|
|
65
64
|
AnyValue::Duration(v, time_unit) => {
|
|
66
65
|
let time_unit = time_unit.to_ascii();
|
|
67
|
-
|
|
68
|
-
.funcall("_to_ruby_duration", (v, time_unit))
|
|
69
|
-
.unwrap()
|
|
66
|
+
pl_utils(ruby).funcall("_to_ruby_duration", (v, time_unit))?
|
|
70
67
|
}
|
|
71
|
-
AnyValue::Time(v) =>
|
|
72
|
-
AnyValue::Array(v, _) | AnyValue::List(v) => RbSeries::new(v)
|
|
73
|
-
ref av @ AnyValue::Struct(_, _, flds) => struct_dict(av._iter_struct_av(), flds)
|
|
74
|
-
AnyValue::StructOwned(payload) => struct_dict(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)?,
|
|
75
72
|
AnyValue::Object(v) => {
|
|
76
73
|
let object = v.as_any().downcast_ref::<ObjectValue>().unwrap();
|
|
77
|
-
object.
|
|
74
|
+
object.clone().into_value_with(ruby)
|
|
78
75
|
}
|
|
79
76
|
AnyValue::ObjectOwned(v) => {
|
|
80
77
|
let object = v.0.as_any().downcast_ref::<ObjectValue>().unwrap();
|
|
81
|
-
object.
|
|
78
|
+
object.clone().into_value_with(ruby)
|
|
82
79
|
}
|
|
83
|
-
AnyValue::Binary(v) =>
|
|
84
|
-
AnyValue::BinaryOwned(v) =>
|
|
85
|
-
AnyValue::Decimal(v, scale) =>
|
|
86
|
-
|
|
87
|
-
.
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
let tu = tu.to_ascii();
|
|
93
|
-
utils()
|
|
94
|
-
.funcall("_to_ruby_datetime", (v, tu, tz.map(|v| v.to_string())))
|
|
95
|
-
.unwrap()
|
|
80
|
+
AnyValue::Binary(v) => ruby.str_from_slice(v).as_value(),
|
|
81
|
+
AnyValue::BinaryOwned(v) => ruby.str_from_slice(&v).as_value(),
|
|
82
|
+
AnyValue::Decimal(v, prec, scale) => {
|
|
83
|
+
let mut buf = DecimalFmtBuffer::new();
|
|
84
|
+
let s = buf.format_dec128(v, scale, false, false);
|
|
85
|
+
pl_utils(ruby).funcall("_to_ruby_decimal", (prec, s))?
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
Ok(rb_object)
|
|
96
89
|
}
|
|
97
90
|
|
|
98
|
-
pub(crate) fn rb_object_to_any_value<'s>(
|
|
91
|
+
pub(crate) fn rb_object_to_any_value<'s>(
|
|
92
|
+
ob: Value,
|
|
93
|
+
strict: bool,
|
|
94
|
+
allow_object: bool,
|
|
95
|
+
) -> RbResult<AnyValue<'s>> {
|
|
99
96
|
// Conversion functions.
|
|
100
97
|
fn get_null(_ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
|
|
101
98
|
Ok(AnyValue::Null)
|
|
@@ -109,8 +106,12 @@ pub(crate) fn rb_object_to_any_value<'s>(ob: Value, strict: bool) -> RbResult<An
|
|
|
109
106
|
fn get_int(ob: Value, strict: bool) -> RbResult<AnyValue<'static>> {
|
|
110
107
|
if let Ok(v) = i64::try_convert(ob) {
|
|
111
108
|
Ok(AnyValue::Int64(v))
|
|
109
|
+
} else if let Ok(v) = i128::try_convert(ob) {
|
|
110
|
+
Ok(AnyValue::Int128(v))
|
|
112
111
|
} else if let Ok(v) = u64::try_convert(ob) {
|
|
113
112
|
Ok(AnyValue::UInt64(v))
|
|
113
|
+
} else if let Ok(v) = u128::try_convert(ob) {
|
|
114
|
+
Ok(AnyValue::UInt128(v))
|
|
114
115
|
} else if !strict {
|
|
115
116
|
let f = f64::try_convert(ob)?;
|
|
116
117
|
Ok(AnyValue::Float64(f))
|
|
@@ -126,8 +127,9 @@ pub(crate) fn rb_object_to_any_value<'s>(ob: Value, strict: bool) -> RbResult<An
|
|
|
126
127
|
}
|
|
127
128
|
|
|
128
129
|
fn get_str(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
|
|
129
|
-
let
|
|
130
|
-
|
|
130
|
+
let ruby = Ruby::get_with(ob);
|
|
131
|
+
let v = RString::try_convert(ob)?;
|
|
132
|
+
if v.enc_get() == ruby.utf8_encindex() {
|
|
131
133
|
Ok(AnyValue::StringOwned(v.to_string()?.into()))
|
|
132
134
|
} else {
|
|
133
135
|
Ok(AnyValue::BinaryOwned(unsafe { v.as_slice() }.to_vec()))
|
|
@@ -135,7 +137,7 @@ pub(crate) fn rb_object_to_any_value<'s>(ob: Value, strict: bool) -> RbResult<An
|
|
|
135
137
|
}
|
|
136
138
|
|
|
137
139
|
fn get_list(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
|
|
138
|
-
let v = RArray::
|
|
140
|
+
let v = RArray::try_convert(ob)?;
|
|
139
141
|
if v.is_empty() {
|
|
140
142
|
Ok(AnyValue::List(Series::new_empty(
|
|
141
143
|
PlSmallStr::EMPTY,
|
|
@@ -172,11 +174,16 @@ pub(crate) fn rb_object_to_any_value<'s>(ob: Value, strict: bool) -> RbResult<An
|
|
|
172
174
|
}
|
|
173
175
|
|
|
174
176
|
fn get_struct(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
|
|
175
|
-
let dict = RHash::
|
|
177
|
+
let dict = RHash::try_convert(ob)?;
|
|
176
178
|
let len = dict.len();
|
|
177
179
|
let mut keys = Vec::with_capacity(len);
|
|
178
180
|
let mut vals = Vec::with_capacity(len);
|
|
179
|
-
dict.foreach(|key:
|
|
181
|
+
dict.foreach(|key: Value, val: Wrap<AnyValue>| {
|
|
182
|
+
let key = if let Some(s) = Symbol::from_value(key) {
|
|
183
|
+
s.name()?.to_string()
|
|
184
|
+
} else {
|
|
185
|
+
String::try_convert(key)?
|
|
186
|
+
};
|
|
180
187
|
let val = val.0;
|
|
181
188
|
let dtype = DataType::from(&val);
|
|
182
189
|
keys.push(Field::new(key.into(), dtype));
|
|
@@ -186,6 +193,14 @@ pub(crate) fn rb_object_to_any_value<'s>(ob: Value, strict: bool) -> RbResult<An
|
|
|
186
193
|
Ok(AnyValue::StructOwned(Box::new((vals, keys))))
|
|
187
194
|
}
|
|
188
195
|
|
|
196
|
+
fn get_object(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
|
|
197
|
+
// This is slow, but hey don't use objects.
|
|
198
|
+
let v = &ObjectValue {
|
|
199
|
+
inner: Opaque::from(ob),
|
|
200
|
+
};
|
|
201
|
+
Ok(AnyValue::ObjectOwned(OwnedObject(v.to_boxed())))
|
|
202
|
+
}
|
|
203
|
+
|
|
189
204
|
fn get_date(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
|
|
190
205
|
// convert to DateTime for UTC
|
|
191
206
|
let v = ob
|
|
@@ -204,14 +219,8 @@ pub(crate) fn rb_object_to_any_value<'s>(ob: Value, strict: bool) -> RbResult<An
|
|
|
204
219
|
Ok(AnyValue::Datetime(v, TimeUnit::Nanoseconds, None))
|
|
205
220
|
}
|
|
206
221
|
|
|
207
|
-
fn get_datetime(ob: Value,
|
|
208
|
-
|
|
209
|
-
let nsec: i64 = ob.funcall("nsec", ())?;
|
|
210
|
-
Ok(AnyValue::Datetime(
|
|
211
|
-
sec * 1_000_000_000 + nsec,
|
|
212
|
-
TimeUnit::Nanoseconds,
|
|
213
|
-
None,
|
|
214
|
-
))
|
|
222
|
+
fn get_datetime(ob: Value, strict: bool) -> RbResult<AnyValue<'static>> {
|
|
223
|
+
get_time(ob.funcall("to_time", ())?, strict)
|
|
215
224
|
}
|
|
216
225
|
|
|
217
226
|
fn get_decimal(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
|
|
@@ -220,20 +229,18 @@ pub(crate) fn rb_object_to_any_value<'s>(ob: Value, strict: bool) -> RbResult<An
|
|
|
220
229
|
match digits.parse::<i128>() {
|
|
221
230
|
Ok(mut v) => {
|
|
222
231
|
let scale = if exp > 0 {
|
|
223
|
-
v = 10_i128
|
|
224
|
-
.checked_pow(exp as u32)
|
|
225
|
-
.and_then(|factor| v.checked_mul(factor))?;
|
|
232
|
+
v = 10_i128.checked_pow(exp as u32)?.checked_mul(v)?;
|
|
226
233
|
0
|
|
227
234
|
} else {
|
|
228
235
|
(-exp) as usize
|
|
229
236
|
};
|
|
230
|
-
|
|
237
|
+
dec128_fits(v, DEC128_MAX_PREC).then_some((v, scale))
|
|
231
238
|
}
|
|
232
239
|
Err(_) => None,
|
|
233
240
|
}
|
|
234
241
|
}
|
|
235
242
|
|
|
236
|
-
let (sign, digits, _, exp): (i8, String, i32, i32) = ob.funcall("split", ())
|
|
243
|
+
let (sign, digits, _, exp): (i8, String, i32, i32) = ob.funcall("split", ())?;
|
|
237
244
|
let (mut v, scale) = abs_decimal_from_digits(digits, exp).ok_or_else(|| {
|
|
238
245
|
RbErr::from(RbPolarsErr::Other(
|
|
239
246
|
"BigDecimal is too large to fit in Decimal128".into(),
|
|
@@ -243,35 +250,43 @@ pub(crate) fn rb_object_to_any_value<'s>(ob: Value, strict: bool) -> RbResult<An
|
|
|
243
250
|
// TODO better error
|
|
244
251
|
v = v.checked_neg().unwrap();
|
|
245
252
|
}
|
|
246
|
-
Ok(AnyValue::Decimal(v, scale))
|
|
253
|
+
Ok(AnyValue::Decimal(v, DEC128_MAX_PREC, scale))
|
|
247
254
|
}
|
|
248
255
|
|
|
256
|
+
let ruby = Ruby::get_with(ob);
|
|
249
257
|
if ob.is_nil() {
|
|
250
258
|
get_null(ob, strict)
|
|
251
|
-
} else if ob.is_kind_of(
|
|
259
|
+
} else if ob.is_kind_of(ruby.class_true_class()) || ob.is_kind_of(ruby.class_false_class()) {
|
|
252
260
|
get_bool(ob, strict)
|
|
253
|
-
} else if ob.is_kind_of(
|
|
261
|
+
} else if ob.is_kind_of(ruby.class_integer()) {
|
|
254
262
|
get_int(ob, strict)
|
|
255
|
-
} else if ob.is_kind_of(
|
|
263
|
+
} else if ob.is_kind_of(ruby.class_float()) {
|
|
256
264
|
get_float(ob, strict)
|
|
257
|
-
} else if ob.is_kind_of(
|
|
265
|
+
} else if ob.is_kind_of(ruby.class_string()) {
|
|
258
266
|
get_str(ob, strict)
|
|
259
|
-
} else if ob.is_kind_of(
|
|
267
|
+
} else if ob.is_kind_of(ruby.class_array()) {
|
|
260
268
|
get_list(ob, strict)
|
|
261
|
-
} else if ob.is_kind_of(
|
|
269
|
+
} else if ob.is_kind_of(ruby.class_hash()) {
|
|
262
270
|
get_struct(ob, strict)
|
|
263
271
|
} else if ob.respond_to("_s", true)? {
|
|
264
272
|
get_list_from_series(ob, strict)
|
|
265
273
|
// call is_a? for ActiveSupport::TimeWithZone
|
|
266
|
-
} else if ob.funcall::<_, _, bool>("is_a?", (
|
|
274
|
+
} else if ob.funcall::<_, _, bool>("is_a?", (ruby.class_time(),))? {
|
|
267
275
|
get_time(ob, strict)
|
|
268
|
-
} else if ob.is_kind_of(crate::rb_modules::datetime()) {
|
|
276
|
+
} else if ob.is_kind_of(crate::ruby::rb_modules::datetime(&ruby)) {
|
|
269
277
|
get_datetime(ob, strict)
|
|
270
|
-
} else if ob.is_kind_of(crate::rb_modules::date()) {
|
|
278
|
+
} else if ob.is_kind_of(crate::ruby::rb_modules::date(&ruby)) {
|
|
271
279
|
get_date(ob, strict)
|
|
272
|
-
} else if
|
|
280
|
+
} else if crate::ruby::rb_modules::bigdecimal(&ruby)
|
|
281
|
+
.map(|cls| ob.is_kind_of(cls))
|
|
282
|
+
.unwrap_or(false)
|
|
283
|
+
{
|
|
273
284
|
get_decimal(ob, strict)
|
|
274
285
|
} else {
|
|
275
|
-
|
|
286
|
+
if allow_object {
|
|
287
|
+
get_object(ob, strict)
|
|
288
|
+
} else {
|
|
289
|
+
Err(RbValueError::new_err(format!("Cannot convert {ob}")))
|
|
290
|
+
}
|
|
276
291
|
}
|
|
277
292
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
use std::sync::Arc;
|
|
2
|
+
|
|
3
|
+
use polars_dtype::categorical::Categories;
|
|
4
|
+
|
|
5
|
+
#[magnus::wrap(class = "Polars::RbCategories")]
|
|
6
|
+
#[repr(transparent)]
|
|
7
|
+
#[derive(Clone)]
|
|
8
|
+
pub struct RbCategories {
|
|
9
|
+
categories: Arc<Categories>,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
impl RbCategories {
|
|
13
|
+
pub fn categories(&self) -> &Arc<Categories> {
|
|
14
|
+
&self.categories
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
impl RbCategories {
|
|
19
|
+
pub fn global_categories() -> Self {
|
|
20
|
+
Self {
|
|
21
|
+
categories: Categories::global(),
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
impl From<Arc<Categories>> for RbCategories {
|
|
27
|
+
fn from(categories: Arc<Categories>) -> Self {
|
|
28
|
+
Self { categories }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
use magnus::{
|
|
1
|
+
use magnus::{IntoValue, RArray, RString, Ruby, TryConvert, Value, prelude::*};
|
|
2
2
|
use polars::prelude::*;
|
|
3
|
+
use polars_compute::decimal::DecimalFmtBuffer;
|
|
3
4
|
|
|
4
|
-
use super::
|
|
5
|
+
use super::datetime::datetime_to_rb_object;
|
|
6
|
+
use super::{Wrap, get_rbseq, struct_dict};
|
|
5
7
|
|
|
6
|
-
use crate::rb_modules::utils;
|
|
7
8
|
use crate::RbResult;
|
|
9
|
+
use crate::rb_modules::pl_utils;
|
|
10
|
+
use crate::ruby::utils::TryIntoValue;
|
|
8
11
|
|
|
9
12
|
impl TryConvert for Wrap<StringChunked> {
|
|
10
13
|
fn try_convert(obj: Value) -> RbResult<Self> {
|
|
@@ -39,100 +42,106 @@ impl TryConvert for Wrap<BinaryChunked> {
|
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
impl IntoValue for Wrap<&StringChunked> {
|
|
42
|
-
fn into_value_with(self,
|
|
43
|
-
let iter = self.0.
|
|
44
|
-
|
|
45
|
+
fn into_value_with(self, ruby: &Ruby) -> Value {
|
|
46
|
+
let iter = self.0.iter();
|
|
47
|
+
ruby.ary_from_iter(iter).as_value()
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
impl IntoValue for Wrap<&BinaryChunked> {
|
|
49
|
-
fn into_value_with(self,
|
|
52
|
+
fn into_value_with(self, ruby: &Ruby) -> Value {
|
|
50
53
|
let iter = self
|
|
51
54
|
.0
|
|
52
|
-
.
|
|
53
|
-
.map(|opt_bytes| opt_bytes.map(
|
|
54
|
-
|
|
55
|
+
.iter()
|
|
56
|
+
.map(|opt_bytes| opt_bytes.map(|v| ruby.str_from_slice(v)));
|
|
57
|
+
ruby.ary_from_iter(iter).as_value()
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
impl
|
|
59
|
-
fn
|
|
61
|
+
impl TryIntoValue for Wrap<&StructChunked> {
|
|
62
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
60
63
|
let s = self.0.clone().into_series();
|
|
61
64
|
// todo! iterate its chunks and flatten.
|
|
62
65
|
// make series::iter() accept a chunk index.
|
|
63
66
|
let s = s.rechunk();
|
|
64
67
|
let iter = s.iter().map(|av| match av {
|
|
65
|
-
AnyValue::Struct(_, _, flds) => struct_dict(av._iter_struct_av(), flds),
|
|
66
|
-
AnyValue::Null => ruby.qnil().as_value(),
|
|
68
|
+
AnyValue::Struct(_, _, flds) => struct_dict(ruby, av._iter_struct_av(), flds),
|
|
69
|
+
AnyValue::Null => Ok(ruby.qnil().as_value()),
|
|
67
70
|
_ => unreachable!(),
|
|
68
71
|
});
|
|
69
72
|
|
|
70
|
-
|
|
73
|
+
ruby.ary_try_from_iter(iter).map(|v| v.as_value())
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
|
|
74
|
-
impl
|
|
75
|
-
fn
|
|
76
|
-
let utils =
|
|
77
|
-
let time_unit = Wrap(self.0.time_unit()).
|
|
78
|
-
let iter = self.0.
|
|
79
|
-
opt_v
|
|
80
|
-
utils
|
|
81
|
-
|
|
82
|
-
.unwrap()
|
|
83
|
-
})
|
|
77
|
+
impl TryIntoValue for Wrap<&DurationChunked> {
|
|
78
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
79
|
+
let utils = pl_utils(ruby);
|
|
80
|
+
let time_unit = Wrap(self.0.time_unit()).into_value_with(ruby);
|
|
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()
|
|
84
85
|
});
|
|
85
|
-
|
|
86
|
+
ruby.ary_try_from_iter(iter).map(|v| v.as_value())
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
impl
|
|
90
|
-
fn
|
|
91
|
-
let
|
|
92
|
-
let time_unit =
|
|
93
|
-
let
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
.funcall::<_, _, Value>("_to_ruby_datetime", (v, time_unit, time_zone))
|
|
98
|
-
.unwrap()
|
|
99
|
-
})
|
|
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()
|
|
100
98
|
});
|
|
101
|
-
|
|
99
|
+
ruby.ary_try_from_iter(iter).map(|v| v.as_value())
|
|
102
100
|
}
|
|
103
101
|
}
|
|
104
102
|
|
|
105
|
-
impl
|
|
106
|
-
fn
|
|
107
|
-
let utils =
|
|
108
|
-
let iter = self.0.
|
|
109
|
-
opt_v
|
|
103
|
+
impl TryIntoValue for Wrap<&TimeChunked> {
|
|
104
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
105
|
+
let utils = pl_utils(ruby);
|
|
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()
|
|
110
110
|
});
|
|
111
|
-
|
|
111
|
+
ruby.ary_try_from_iter(iter).map(|v| v.as_value())
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
impl
|
|
116
|
-
fn
|
|
117
|
-
let utils =
|
|
118
|
-
let iter = self.0.
|
|
119
|
-
opt_v
|
|
115
|
+
impl TryIntoValue for Wrap<&DateChunked> {
|
|
116
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
117
|
+
let utils = pl_utils(ruby);
|
|
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()
|
|
120
122
|
});
|
|
121
|
-
|
|
123
|
+
ruby.ary_try_from_iter(iter).map(|v| v.as_value())
|
|
122
124
|
}
|
|
123
125
|
}
|
|
124
126
|
|
|
125
|
-
impl
|
|
126
|
-
fn
|
|
127
|
-
let
|
|
128
|
-
|
|
129
|
-
let iter = self.0.into_iter().map(|opt_v| {
|
|
130
|
-
opt_v.map(|v| {
|
|
131
|
-
utils
|
|
132
|
-
.funcall::<_, _, Value>("_to_ruby_decimal", (v.to_string(), rb_scale))
|
|
133
|
-
.unwrap()
|
|
134
|
-
})
|
|
135
|
-
});
|
|
136
|
-
RArray::from_iter(iter).into_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())
|
|
137
131
|
}
|
|
138
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
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
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::*};
|
|
8
|
+
use polars::prelude::*;
|
|
9
|
+
|
|
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
|
+
}
|
|
33
|
+
|
|
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
|
+
}
|
|
63
|
+
}
|