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
|
@@ -3,7 +3,7 @@ use polars_core::datatypes::{TimeUnit, TimeZone};
|
|
|
3
3
|
|
|
4
4
|
use crate::conversion::Wrap;
|
|
5
5
|
use crate::prelude::*;
|
|
6
|
-
use crate::RbExpr;
|
|
6
|
+
use crate::{RbDataTypeExpr, RbExpr, RbPolarsErr, RbResult};
|
|
7
7
|
|
|
8
8
|
pub fn int_range(start: &RbExpr, end: &RbExpr, step: i64, dtype: Wrap<DataType>) -> RbExpr {
|
|
9
9
|
let start = start.inner.clone();
|
|
@@ -12,16 +12,20 @@ pub fn int_range(start: &RbExpr, end: &RbExpr, step: i64, dtype: Wrap<DataType>)
|
|
|
12
12
|
dsl::int_range(start, end, step, dtype).into()
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
pub fn int_ranges(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
pub fn int_ranges(
|
|
16
|
+
start: &RbExpr,
|
|
17
|
+
end: &RbExpr,
|
|
18
|
+
step: &RbExpr,
|
|
19
|
+
dtype: &RbDataTypeExpr,
|
|
20
|
+
) -> RbResult<RbExpr> {
|
|
21
|
+
let dtype = dtype.inner.clone();
|
|
22
|
+
Ok(dsl::int_ranges(
|
|
23
|
+
start.inner.clone(),
|
|
24
|
+
end.inner.clone(),
|
|
25
|
+
step.inner.clone(),
|
|
26
|
+
dtype,
|
|
27
|
+
)
|
|
28
|
+
.into())
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
pub fn date_range(
|
|
@@ -29,12 +33,14 @@ pub fn date_range(
|
|
|
29
33
|
end: &RbExpr,
|
|
30
34
|
interval: String,
|
|
31
35
|
closed: Wrap<ClosedWindow>,
|
|
32
|
-
) -> RbExpr {
|
|
36
|
+
) -> RbResult<RbExpr> {
|
|
33
37
|
let start = start.inner.clone();
|
|
34
38
|
let end = end.inner.clone();
|
|
35
39
|
let interval = Duration::parse(&interval);
|
|
36
40
|
let closed = closed.0;
|
|
37
|
-
dsl::date_range(start, end, interval, closed)
|
|
41
|
+
let out = dsl::date_range(Some(start), Some(end), Some(interval), None, closed)
|
|
42
|
+
.map_err(RbPolarsErr::from)?;
|
|
43
|
+
Ok(out.into())
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
pub fn date_ranges(
|
|
@@ -42,46 +48,68 @@ pub fn date_ranges(
|
|
|
42
48
|
end: &RbExpr,
|
|
43
49
|
interval: String,
|
|
44
50
|
closed: Wrap<ClosedWindow>,
|
|
45
|
-
) -> RbExpr {
|
|
51
|
+
) -> RbResult<RbExpr> {
|
|
46
52
|
let start = start.inner.clone();
|
|
47
53
|
let end = end.inner.clone();
|
|
48
54
|
let interval = Duration::parse(&interval);
|
|
49
55
|
let closed = closed.0;
|
|
50
|
-
dsl::date_ranges(start, end, interval, closed)
|
|
56
|
+
let out = dsl::date_ranges(Some(start), Some(end), Some(interval), None, closed)
|
|
57
|
+
.map_err(RbPolarsErr::from)?;
|
|
58
|
+
Ok(out.into())
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
pub fn datetime_range(
|
|
54
62
|
start: &RbExpr,
|
|
55
63
|
end: &RbExpr,
|
|
56
|
-
|
|
64
|
+
interval: String,
|
|
57
65
|
closed: Wrap<ClosedWindow>,
|
|
58
66
|
time_unit: Option<Wrap<TimeUnit>>,
|
|
59
|
-
time_zone: Option<
|
|
60
|
-
) -> RbExpr {
|
|
67
|
+
time_zone: Wrap<Option<TimeZone>>,
|
|
68
|
+
) -> RbResult<RbExpr> {
|
|
61
69
|
let start = start.inner.clone();
|
|
62
70
|
let end = end.inner.clone();
|
|
63
|
-
let
|
|
71
|
+
let interval = Duration::try_parse(&interval).map_err(RbPolarsErr::from)?;
|
|
64
72
|
let closed = closed.0;
|
|
65
73
|
let time_unit = time_unit.map(|x| x.0);
|
|
66
|
-
let time_zone = time_zone.
|
|
67
|
-
dsl::datetime_range(
|
|
74
|
+
let time_zone = time_zone.0;
|
|
75
|
+
let out = dsl::datetime_range(
|
|
76
|
+
Some(start),
|
|
77
|
+
Some(end),
|
|
78
|
+
Some(interval),
|
|
79
|
+
None,
|
|
80
|
+
closed,
|
|
81
|
+
time_unit,
|
|
82
|
+
time_zone,
|
|
83
|
+
)
|
|
84
|
+
.map_err(RbPolarsErr::from)?;
|
|
85
|
+
Ok(out.into())
|
|
68
86
|
}
|
|
69
87
|
|
|
70
88
|
pub fn datetime_ranges(
|
|
71
89
|
start: &RbExpr,
|
|
72
90
|
end: &RbExpr,
|
|
73
|
-
|
|
91
|
+
interval: String,
|
|
74
92
|
closed: Wrap<ClosedWindow>,
|
|
75
93
|
time_unit: Option<Wrap<TimeUnit>>,
|
|
76
|
-
time_zone: Option<
|
|
77
|
-
) -> RbExpr {
|
|
94
|
+
time_zone: Wrap<Option<TimeZone>>,
|
|
95
|
+
) -> RbResult<RbExpr> {
|
|
78
96
|
let start = start.inner.clone();
|
|
79
97
|
let end = end.inner.clone();
|
|
80
|
-
let
|
|
98
|
+
let interval = Duration::try_parse(&interval).map_err(RbPolarsErr::from)?;
|
|
81
99
|
let closed = closed.0;
|
|
82
100
|
let time_unit = time_unit.map(|x| x.0);
|
|
83
|
-
let time_zone = time_zone.
|
|
84
|
-
dsl::datetime_ranges(
|
|
101
|
+
let time_zone = time_zone.0;
|
|
102
|
+
let out = dsl::datetime_ranges(
|
|
103
|
+
Some(start),
|
|
104
|
+
Some(end),
|
|
105
|
+
Some(interval),
|
|
106
|
+
None,
|
|
107
|
+
closed,
|
|
108
|
+
time_unit,
|
|
109
|
+
time_zone,
|
|
110
|
+
)
|
|
111
|
+
.map_err(RbPolarsErr::from)?;
|
|
112
|
+
Ok(out.into())
|
|
85
113
|
}
|
|
86
114
|
|
|
87
115
|
pub fn time_range(
|
|
@@ -89,12 +117,12 @@ pub fn time_range(
|
|
|
89
117
|
end: &RbExpr,
|
|
90
118
|
every: String,
|
|
91
119
|
closed: Wrap<ClosedWindow>,
|
|
92
|
-
) -> RbExpr {
|
|
120
|
+
) -> RbResult<RbExpr> {
|
|
93
121
|
let start = start.inner.clone();
|
|
94
122
|
let end = end.inner.clone();
|
|
95
123
|
let every = Duration::parse(&every);
|
|
96
124
|
let closed = closed.0;
|
|
97
|
-
dsl::time_range(start, end, every, closed).into()
|
|
125
|
+
Ok(dsl::time_range(start, end, every, closed).into())
|
|
98
126
|
}
|
|
99
127
|
|
|
100
128
|
pub fn time_ranges(
|
|
@@ -102,10 +130,39 @@ pub fn time_ranges(
|
|
|
102
130
|
end: &RbExpr,
|
|
103
131
|
every: String,
|
|
104
132
|
closed: Wrap<ClosedWindow>,
|
|
105
|
-
) -> RbExpr {
|
|
133
|
+
) -> RbResult<RbExpr> {
|
|
106
134
|
let start = start.inner.clone();
|
|
107
135
|
let end = end.inner.clone();
|
|
108
136
|
let every = Duration::parse(&every);
|
|
109
137
|
let closed = closed.0;
|
|
110
|
-
dsl::time_ranges(start, end, every, closed).into()
|
|
138
|
+
Ok(dsl::time_ranges(start, end, every, closed).into())
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
pub fn linear_space(
|
|
142
|
+
start: &RbExpr,
|
|
143
|
+
end: &RbExpr,
|
|
144
|
+
num_samples: &RbExpr,
|
|
145
|
+
closed: Wrap<ClosedInterval>,
|
|
146
|
+
) -> RbResult<RbExpr> {
|
|
147
|
+
let start = start.inner.clone();
|
|
148
|
+
let end = end.inner.clone();
|
|
149
|
+
let num_samples = num_samples.inner.clone();
|
|
150
|
+
let closed = closed.0;
|
|
151
|
+
Ok(dsl::linear_space(start, end, num_samples, closed).into())
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
pub fn linear_spaces(
|
|
155
|
+
start: &RbExpr,
|
|
156
|
+
end: &RbExpr,
|
|
157
|
+
num_samples: &RbExpr,
|
|
158
|
+
closed: Wrap<ClosedInterval>,
|
|
159
|
+
as_array: bool,
|
|
160
|
+
) -> RbResult<RbExpr> {
|
|
161
|
+
let start = start.inner.clone();
|
|
162
|
+
let end = end.inner.clone();
|
|
163
|
+
let num_samples = num_samples.inner.clone();
|
|
164
|
+
let closed = closed.0;
|
|
165
|
+
let out =
|
|
166
|
+
dsl::linear_spaces(start, end, num_samples, closed, as_array).map_err(RbPolarsErr::from)?;
|
|
167
|
+
Ok(out.into())
|
|
111
168
|
}
|
|
@@ -1 +1,52 @@
|
|
|
1
|
-
|
|
1
|
+
use magnus::prelude::*;
|
|
2
|
+
use magnus::{RHash, Ruby, Value};
|
|
3
|
+
use polars::prelude::{ArrowDataType, DataType};
|
|
4
|
+
use polars_error::polars_err;
|
|
5
|
+
|
|
6
|
+
use crate::interop::arrow::to_rust::normalize_arrow_fields;
|
|
7
|
+
use crate::prelude::Wrap;
|
|
8
|
+
use crate::ruby::utils::TryIntoValue;
|
|
9
|
+
use crate::series::import_schema_rbcapsule;
|
|
10
|
+
use crate::utils::to_rb_err;
|
|
11
|
+
use crate::{RbResult, RbValueError};
|
|
12
|
+
|
|
13
|
+
pub mod to_rb;
|
|
14
|
+
pub mod to_rust;
|
|
15
|
+
|
|
16
|
+
pub fn init_polars_schema_from_arrow_c_schema(
|
|
17
|
+
polars_schema: RHash,
|
|
18
|
+
schema_object: Value,
|
|
19
|
+
) -> RbResult<()> {
|
|
20
|
+
let ruby = &Ruby::get_with(polars_schema);
|
|
21
|
+
let schema_capsule = schema_object.funcall("arrow_c_schema", ())?;
|
|
22
|
+
|
|
23
|
+
let field = import_schema_rbcapsule(schema_capsule)?;
|
|
24
|
+
let field = normalize_arrow_fields(&field);
|
|
25
|
+
|
|
26
|
+
let ArrowDataType::Struct(fields) = field.dtype else {
|
|
27
|
+
return Err(RbValueError::new_err(format!(
|
|
28
|
+
"arrow_c_schema of object passed to Polars::Schema did not return struct dtype: \
|
|
29
|
+
object: {}, dtype: {:?}",
|
|
30
|
+
schema_object, &field.dtype
|
|
31
|
+
)));
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
for field in fields {
|
|
35
|
+
let dtype = DataType::from_arrow_field(&field);
|
|
36
|
+
|
|
37
|
+
let name = field.name.as_str();
|
|
38
|
+
let dtype = Wrap(dtype).try_into_value_with(ruby)?;
|
|
39
|
+
|
|
40
|
+
if polars_schema.get(name).is_some() {
|
|
41
|
+
return Err(to_rb_err(polars_err!(
|
|
42
|
+
Duplicate:
|
|
43
|
+
"arrow schema contained duplicate name: {}",
|
|
44
|
+
name
|
|
45
|
+
)));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
polars_schema.aset(name, dtype)?;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Ok(())
|
|
52
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
use arrow::datatypes::ArrowDataType;
|
|
2
2
|
use arrow::ffi;
|
|
3
|
-
use magnus::{IntoValue, Value};
|
|
3
|
+
use magnus::{IntoValue, Ruby, Value};
|
|
4
4
|
use polars::datatypes::CompatLevel;
|
|
5
5
|
use polars::frame::DataFrame;
|
|
6
6
|
use polars::prelude::{ArrayRef, ArrowField, PlSmallStr, PolarsResult, SchemaExt};
|
|
@@ -9,9 +9,9 @@ use polars_core::utils::arrow;
|
|
|
9
9
|
|
|
10
10
|
use crate::RbResult;
|
|
11
11
|
|
|
12
|
-
#[magnus::wrap(class = "Polars::
|
|
12
|
+
#[magnus::wrap(class = "Polars::ArrowArrayStream")]
|
|
13
13
|
pub struct RbArrowArrayStream {
|
|
14
|
-
stream: ffi::ArrowArrayStream,
|
|
14
|
+
pub(crate) stream: ffi::ArrowArrayStream,
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
impl RbArrowArrayStream {
|
|
@@ -20,11 +20,41 @@ impl RbArrowArrayStream {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
#[magnus::wrap(class = "Polars::ArrowSchema")]
|
|
24
|
+
pub struct RbArrowSchema {
|
|
25
|
+
pub(crate) schema: ffi::ArrowSchema,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
impl RbArrowSchema {
|
|
29
|
+
pub fn to_i(&self) -> usize {
|
|
30
|
+
(&self.schema as *const _) as usize
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
pub(crate) fn dataframe_to_stream(df: &DataFrame, ruby: &Ruby) -> RbResult<Value> {
|
|
24
35
|
let iter = Box::new(DataFrameStreamIterator::new(df));
|
|
25
36
|
let field = iter.field();
|
|
26
37
|
let stream = ffi::export_iterator(iter, field);
|
|
27
|
-
Ok(RbArrowArrayStream { stream }.
|
|
38
|
+
Ok(RbArrowArrayStream { stream }.into_value_with(ruby))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
pub(crate) fn polars_schema_to_rbcapsule(
|
|
42
|
+
ruby: &Ruby,
|
|
43
|
+
schema: crate::prelude::Wrap<polars::prelude::Schema>,
|
|
44
|
+
) -> RbResult<Value> {
|
|
45
|
+
let schema: arrow::ffi::ArrowSchema = arrow::ffi::export_field_to_c(&ArrowField::new(
|
|
46
|
+
PlSmallStr::EMPTY,
|
|
47
|
+
ArrowDataType::Struct(
|
|
48
|
+
schema
|
|
49
|
+
.0
|
|
50
|
+
.iter_fields()
|
|
51
|
+
.map(|x| x.to_arrow(CompatLevel::newest()))
|
|
52
|
+
.collect(),
|
|
53
|
+
),
|
|
54
|
+
false,
|
|
55
|
+
));
|
|
56
|
+
|
|
57
|
+
Ok(RbArrowSchema { schema }.into_value_with(ruby))
|
|
28
58
|
}
|
|
29
59
|
|
|
30
60
|
pub struct DataFrameStreamIterator {
|
|
@@ -41,13 +71,13 @@ impl DataFrameStreamIterator {
|
|
|
41
71
|
|
|
42
72
|
Self {
|
|
43
73
|
columns: df
|
|
44
|
-
.
|
|
74
|
+
.columns()
|
|
45
75
|
.iter()
|
|
46
76
|
.map(|v| v.as_materialized_series().clone())
|
|
47
77
|
.collect(),
|
|
48
78
|
dtype,
|
|
49
79
|
idx: 0,
|
|
50
|
-
n_chunks: df.
|
|
80
|
+
n_chunks: df.first_col_n_chunks(),
|
|
51
81
|
}
|
|
52
82
|
}
|
|
53
83
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
use polars_core::prelude::*;
|
|
2
|
+
|
|
3
|
+
pub(crate) fn normalize_arrow_fields(field: &ArrowField) -> ArrowField {
|
|
4
|
+
// normalize fields with extension dtypes that are otherwise standard dtypes associated
|
|
5
|
+
// with (for us) irrelevant metadata; recreate the field using the inner (standard) dtype
|
|
6
|
+
match field {
|
|
7
|
+
ArrowField {
|
|
8
|
+
dtype: ArrowDataType::Struct(fields),
|
|
9
|
+
..
|
|
10
|
+
} => {
|
|
11
|
+
let mut normalized = false;
|
|
12
|
+
let normalized_fields: Vec<_> = fields
|
|
13
|
+
.iter()
|
|
14
|
+
.map(|f| {
|
|
15
|
+
// note: google bigquery column data is returned as a standard arrow dtype, but the
|
|
16
|
+
// sql type it was loaded from is associated as metadata (resulting in an extension dtype)
|
|
17
|
+
if let ArrowDataType::Extension(ext_type) = &f.dtype
|
|
18
|
+
&& ext_type.name.starts_with("google:sqlType:")
|
|
19
|
+
{
|
|
20
|
+
normalized = true;
|
|
21
|
+
return ArrowField::new(
|
|
22
|
+
f.name.clone(),
|
|
23
|
+
ext_type.inner.clone(),
|
|
24
|
+
f.is_nullable,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
f.clone()
|
|
28
|
+
})
|
|
29
|
+
.collect();
|
|
30
|
+
|
|
31
|
+
if normalized {
|
|
32
|
+
ArrowField::new(
|
|
33
|
+
field.name.clone(),
|
|
34
|
+
ArrowDataType::Struct(normalized_fields),
|
|
35
|
+
field.is_nullable,
|
|
36
|
+
)
|
|
37
|
+
} else {
|
|
38
|
+
field.clone()
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
_ => field.clone(),
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -6,7 +6,7 @@ use crate::dataframe::RbDataFrame;
|
|
|
6
6
|
impl RbDataFrame {
|
|
7
7
|
pub fn to_numo(&self) -> Option<Value> {
|
|
8
8
|
let mut st = None;
|
|
9
|
-
for s in self.df.
|
|
9
|
+
for s in self.df.read().columns().iter() {
|
|
10
10
|
let dt_i = s.dtype();
|
|
11
11
|
match st {
|
|
12
12
|
None => st = Some(dt_i.clone()),
|
|
@@ -1,61 +1,83 @@
|
|
|
1
|
-
use magnus::{
|
|
2
|
-
use
|
|
1
|
+
use magnus::{Ruby, Value};
|
|
2
|
+
use num_traits::{Float, NumCast};
|
|
3
3
|
use polars_core::prelude::*;
|
|
4
4
|
|
|
5
|
+
use crate::RbResult;
|
|
5
6
|
use crate::error::RbPolarsErr;
|
|
6
7
|
use crate::raise_err;
|
|
8
|
+
use crate::ruby::numo::{Element, RbArray1};
|
|
7
9
|
use crate::series::RbSeries;
|
|
8
|
-
use crate::RbResult;
|
|
9
10
|
|
|
10
11
|
impl RbSeries {
|
|
11
|
-
///
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
DataType::String => {
|
|
17
|
-
let ca = s.str().unwrap();
|
|
12
|
+
/// Convert this Series to a Numo array.
|
|
13
|
+
pub fn to_numo(rb: &Ruby, self_: &Self) -> RbResult<Value> {
|
|
14
|
+
series_to_numo(rb, &self_.series.read())
|
|
15
|
+
}
|
|
16
|
+
}
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
.const_get::<_, RClass>("SFloat")?
|
|
50
|
-
.funcall("cast", (np_arr,))
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
dt => {
|
|
54
|
-
raise_err!(
|
|
55
|
-
format!("'to_numo' not supported for dtype: {dt:?}"),
|
|
56
|
-
ComputeError
|
|
57
|
-
);
|
|
58
|
-
}
|
|
18
|
+
/// Convert a Series to a Numo array.
|
|
19
|
+
fn series_to_numo(rb: &Ruby, s: &Series) -> RbResult<Value> {
|
|
20
|
+
series_to_numo_with_copy(rb, s)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// Convert a Series to a Numo array, copying data in the process.
|
|
24
|
+
fn series_to_numo_with_copy(rb: &Ruby, s: &Series) -> RbResult<Value> {
|
|
25
|
+
use DataType::*;
|
|
26
|
+
match s.dtype() {
|
|
27
|
+
Int8 => numeric_series_to_numo::<Int8Type, f32>(rb, s),
|
|
28
|
+
Int16 => numeric_series_to_numo::<Int16Type, f32>(rb, s),
|
|
29
|
+
Int32 => numeric_series_to_numo::<Int32Type, f64>(rb, s),
|
|
30
|
+
Int64 => numeric_series_to_numo::<Int64Type, f64>(rb, s),
|
|
31
|
+
UInt8 => numeric_series_to_numo::<UInt8Type, f32>(rb, s),
|
|
32
|
+
UInt16 => numeric_series_to_numo::<UInt16Type, f32>(rb, s),
|
|
33
|
+
UInt32 => numeric_series_to_numo::<UInt32Type, f64>(rb, s),
|
|
34
|
+
UInt64 => numeric_series_to_numo::<UInt64Type, f64>(rb, s),
|
|
35
|
+
Float32 => numeric_series_to_numo::<Float32Type, f32>(rb, s),
|
|
36
|
+
Float64 => numeric_series_to_numo::<Float64Type, f64>(rb, s),
|
|
37
|
+
Boolean => boolean_series_to_numo(rb, s),
|
|
38
|
+
String => {
|
|
39
|
+
let ca = s.str().unwrap();
|
|
40
|
+
let values = ca.iter();
|
|
41
|
+
RbArray1::from_iter(rb, values)
|
|
42
|
+
}
|
|
43
|
+
dt => {
|
|
44
|
+
raise_err!(
|
|
45
|
+
format!("'to_numo' not supported for dtype: {dt:?}"),
|
|
46
|
+
ComputeError
|
|
47
|
+
);
|
|
59
48
|
}
|
|
60
49
|
}
|
|
61
50
|
}
|
|
51
|
+
|
|
52
|
+
/// Convert numeric types to f32 or f64 with NaN representing a null value.
|
|
53
|
+
fn numeric_series_to_numo<T, U>(rb: &Ruby, s: &Series) -> RbResult<Value>
|
|
54
|
+
where
|
|
55
|
+
T: PolarsNumericType,
|
|
56
|
+
T::Native: Element,
|
|
57
|
+
U: Float + Element,
|
|
58
|
+
{
|
|
59
|
+
let ca: &ChunkedArray<T> = s.as_ref().as_ref();
|
|
60
|
+
if s.null_count() == 0 {
|
|
61
|
+
let values = ca.into_no_null_iter();
|
|
62
|
+
RbArray1::<T::Native>::from_iter(rb, values)
|
|
63
|
+
} else {
|
|
64
|
+
let mapper = |opt_v: Option<T::Native>| match opt_v {
|
|
65
|
+
Some(v) => NumCast::from(v).unwrap(),
|
|
66
|
+
None => U::nan(),
|
|
67
|
+
};
|
|
68
|
+
let values = ca.iter().map(mapper);
|
|
69
|
+
RbArray1::from_iter(rb, values)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// Convert booleans to bit if no nulls are present, otherwise convert to objects.
|
|
74
|
+
fn boolean_series_to_numo(rb: &Ruby, s: &Series) -> RbResult<Value> {
|
|
75
|
+
let ca = s.bool().unwrap();
|
|
76
|
+
if s.null_count() == 0 {
|
|
77
|
+
let values = ca.no_null_iter();
|
|
78
|
+
RbArray1::<bool>::from_iter(rb, values)
|
|
79
|
+
} else {
|
|
80
|
+
let values = ca.iter();
|
|
81
|
+
RbArray1::from_iter(rb, values)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
use std::time::Duration;
|
|
2
|
+
|
|
3
|
+
use magnus::{RHash, TryConvert, Value, r_hash::ForEach, value::ReprValue};
|
|
4
|
+
use polars::prelude::CloudScheme;
|
|
5
|
+
use polars_io::cloud::{CloudOptions, CloudRetryConfig};
|
|
6
|
+
use polars_utils::total_ord::TotalOrdWrap;
|
|
7
|
+
|
|
8
|
+
use crate::utils::to_rb_err;
|
|
9
|
+
use crate::{RbResult, RbValueError};
|
|
10
|
+
|
|
11
|
+
pub struct OptRbCloudOptions(Value);
|
|
12
|
+
|
|
13
|
+
impl TryConvert for OptRbCloudOptions {
|
|
14
|
+
fn try_convert(ob: Value) -> RbResult<Self> {
|
|
15
|
+
Ok(Self(ob))
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
impl OptRbCloudOptions {
|
|
20
|
+
pub fn extract_opt_cloud_options(
|
|
21
|
+
&self,
|
|
22
|
+
cloud_scheme: Option<CloudScheme>,
|
|
23
|
+
credential_provider: Option<Value>,
|
|
24
|
+
) -> RbResult<Option<CloudOptions>> {
|
|
25
|
+
if self.0.is_nil() && credential_provider.is_none() {
|
|
26
|
+
return Ok(None);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let mut storage_options: Vec<(String, String)> = vec![];
|
|
30
|
+
let mut file_cache_ttl: u64 = 2;
|
|
31
|
+
let mut retry_config = CloudRetryConfig::default();
|
|
32
|
+
|
|
33
|
+
let storage_options_dict = Option::<RHash>::try_convert(self.0)?;
|
|
34
|
+
|
|
35
|
+
if let Some(storage_options_dict) = storage_options_dict {
|
|
36
|
+
storage_options.reserve(storage_options_dict.len());
|
|
37
|
+
|
|
38
|
+
storage_options_dict.foreach(|key: Value, value: Value| {
|
|
39
|
+
let key: String = key.funcall("to_s", ())?;
|
|
40
|
+
|
|
41
|
+
macro_rules! expected_type {
|
|
42
|
+
($key_name:expr, $type_name:expr) => {{
|
|
43
|
+
|_| {
|
|
44
|
+
let key_name = $key_name;
|
|
45
|
+
let type_name = $type_name;
|
|
46
|
+
RbValueError::new_err(format!(
|
|
47
|
+
"invalid value for '{key_name}': '{value}' (expected {type_name})"
|
|
48
|
+
))
|
|
49
|
+
}
|
|
50
|
+
}};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
match &*key {
|
|
54
|
+
"file_cache_ttl" => {
|
|
55
|
+
file_cache_ttl = u64::try_convert(value)
|
|
56
|
+
.map_err(expected_type!("file_cache_ttl", "int"))?;
|
|
57
|
+
}
|
|
58
|
+
"max_retries" => {
|
|
59
|
+
retry_config.max_retries = Option::<usize>::try_convert(value)
|
|
60
|
+
.map_err(expected_type!("max_retries", "int"))?;
|
|
61
|
+
}
|
|
62
|
+
"retry_timeout_ms" => {
|
|
63
|
+
retry_config.retry_timeout = Some(Duration::from_millis(
|
|
64
|
+
u64::try_convert(value)
|
|
65
|
+
.map_err(expected_type!("retry_timeout", "int"))?,
|
|
66
|
+
));
|
|
67
|
+
}
|
|
68
|
+
"retry_init_backoff_ms" => {
|
|
69
|
+
retry_config.retry_init_backoff = Some(Duration::from_millis(
|
|
70
|
+
u64::try_convert(value)
|
|
71
|
+
.map_err(expected_type!("retry_init_backoff", "int"))?,
|
|
72
|
+
));
|
|
73
|
+
}
|
|
74
|
+
"retry_max_backoff_ms" => {
|
|
75
|
+
retry_config.retry_max_backoff = Some(Duration::from_millis(
|
|
76
|
+
u64::try_convert(value)
|
|
77
|
+
.map_err(expected_type!("retry_max_backoff", "int"))?,
|
|
78
|
+
));
|
|
79
|
+
}
|
|
80
|
+
"retry_base_multiplier" => {
|
|
81
|
+
retry_config.retry_base_multiplier = Some(TotalOrdWrap(
|
|
82
|
+
f64::try_convert(value)
|
|
83
|
+
.map_err(expected_type!("retry_base_multiplier", "float"))?,
|
|
84
|
+
));
|
|
85
|
+
}
|
|
86
|
+
_ => {
|
|
87
|
+
let value =
|
|
88
|
+
String::try_convert(value).map_err(expected_type!(&key, "str"))?;
|
|
89
|
+
storage_options.push((key, value))
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
Ok(ForEach::Continue)
|
|
94
|
+
})?;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let mut cloud_options = CloudOptions::from_untyped_config(cloud_scheme, storage_options)
|
|
98
|
+
.map_err(to_rb_err)?
|
|
99
|
+
.with_retry_config(retry_config);
|
|
100
|
+
|
|
101
|
+
if file_cache_ttl > 0 {
|
|
102
|
+
cloud_options.file_cache_ttl = file_cache_ttl;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
Ok(Some(cloud_options))
|
|
106
|
+
}
|
|
107
|
+
}
|