polars-df 0.25.1 → 0.26.1
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 +32 -1
- data/Cargo.lock +278 -106
- data/Cargo.toml +0 -3
- data/LICENSE.txt +1 -1
- data/README.md +7 -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 +205 -142
- 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/arrow/to_rb.rs +17 -12
- 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 +32 -16
- data/ext/polars/src/lazyframe/optflags.rs +2 -1
- data/ext/polars/src/lib.rs +21 -37
- 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/capsule.rs +27 -0
- data/ext/polars/src/ruby/mod.rs +1 -0
- 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 +38 -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 +83 -11
- 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/aggregation/horizontal.rb +4 -4
- 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/iceberg_dataset.rb +81 -14
- data/lib/polars/io/csv.rb +27 -5
- data/lib/polars/io/delta.rb +4 -0
- 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/schema.rb +9 -0
- data/lib/polars/selectors.rb +1 -1
- data/lib/polars/series.rb +106 -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 +5 -1
- metadata +4 -17
- data/ext/polars/src/functions/string_cache.rs +0 -24
|
@@ -16,6 +16,7 @@ use magnus::{
|
|
|
16
16
|
use polars::chunked_array::object::PolarsObjectSafe;
|
|
17
17
|
use polars::chunked_array::ops::{FillNullLimit, FillNullStrategy};
|
|
18
18
|
use polars::datatypes::AnyValue;
|
|
19
|
+
use polars::frame::PivotColumnNaming;
|
|
19
20
|
use polars::frame::row::Row;
|
|
20
21
|
use polars::io::avro::AvroCompression;
|
|
21
22
|
use polars::prelude::default_values::{
|
|
@@ -36,6 +37,9 @@ use polars_utils::total_ord::{TotalEq, TotalHash};
|
|
|
36
37
|
use crate::file::{RubyScanSourceInput, get_ruby_scan_source_input};
|
|
37
38
|
use crate::object::OBJECT_NAME;
|
|
38
39
|
use crate::rb_modules::pl_series;
|
|
40
|
+
use crate::ruby::gvl::GvlExt;
|
|
41
|
+
use crate::ruby::utils::TryIntoValue;
|
|
42
|
+
use crate::series::import_schema_rbcapsule;
|
|
39
43
|
use crate::utils::to_rb_err;
|
|
40
44
|
use crate::{
|
|
41
45
|
RbDataFrame, RbExpr, RbLazyFrame, RbPolarsErr, RbResult, RbSeries, RbTypeError, RbValueError,
|
|
@@ -80,11 +84,9 @@ pub(crate) fn get_series(obj: Value) -> RbResult<Series> {
|
|
|
80
84
|
Ok(rbs.series.read().clone())
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
pub(crate) fn to_series(rb: &Ruby, s: RbSeries) -> Value {
|
|
87
|
+
pub(crate) fn to_series(rb: &Ruby, s: RbSeries) -> RbResult<Value> {
|
|
84
88
|
let series = pl_series(rb);
|
|
85
|
-
series
|
|
86
|
-
.funcall::<_, _, Value>("_from_rbseries", (s,))
|
|
87
|
-
.unwrap()
|
|
89
|
+
series.funcall::<_, _, Value>("_from_rbseries", (s,))
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
impl TryConvert for Wrap<PlSmallStr> {
|
|
@@ -116,140 +118,135 @@ impl TryConvert for Wrap<NullValues> {
|
|
|
116
118
|
}
|
|
117
119
|
}
|
|
118
120
|
|
|
119
|
-
fn struct_dict<'a>(
|
|
121
|
+
fn struct_dict<'a>(
|
|
122
|
+
ruby: &Ruby,
|
|
123
|
+
vals: impl Iterator<Item = AnyValue<'a>>,
|
|
124
|
+
flds: &[Field],
|
|
125
|
+
) -> RbResult<Value> {
|
|
120
126
|
let dict = ruby.hash_new();
|
|
121
127
|
for (fld, val) in flds.iter().zip(vals) {
|
|
122
|
-
dict.aset(fld.name().as_str(), Wrap(val)
|
|
128
|
+
dict.aset(fld.name().as_str(), Wrap(val).try_into_value_with(ruby)?)?;
|
|
123
129
|
}
|
|
124
|
-
dict.as_value()
|
|
130
|
+
Ok(dict.as_value())
|
|
125
131
|
}
|
|
126
132
|
|
|
127
|
-
impl
|
|
128
|
-
fn
|
|
133
|
+
impl TryIntoValue for Wrap<Series> {
|
|
134
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
129
135
|
to_series(ruby, RbSeries::new(self.0))
|
|
130
136
|
}
|
|
131
137
|
}
|
|
132
138
|
|
|
133
|
-
impl
|
|
134
|
-
fn
|
|
139
|
+
impl TryIntoValue for Wrap<DataType> {
|
|
140
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
135
141
|
let pl = crate::rb_modules::polars(ruby);
|
|
136
142
|
|
|
137
143
|
match self.0 {
|
|
138
144
|
DataType::Int8 => {
|
|
139
|
-
let class = pl.const_get::<_, Value>("Int8")
|
|
140
|
-
class.funcall("new", ())
|
|
145
|
+
let class = pl.const_get::<_, Value>("Int8")?;
|
|
146
|
+
class.funcall("new", ())
|
|
141
147
|
}
|
|
142
148
|
DataType::Int16 => {
|
|
143
|
-
let class = pl.const_get::<_, Value>("Int16")
|
|
144
|
-
class.funcall("new", ())
|
|
149
|
+
let class = pl.const_get::<_, Value>("Int16")?;
|
|
150
|
+
class.funcall("new", ())
|
|
145
151
|
}
|
|
146
152
|
DataType::Int32 => {
|
|
147
|
-
let class = pl.const_get::<_, Value>("Int32")
|
|
148
|
-
class.funcall("new", ())
|
|
153
|
+
let class = pl.const_get::<_, Value>("Int32")?;
|
|
154
|
+
class.funcall("new", ())
|
|
149
155
|
}
|
|
150
156
|
DataType::Int64 => {
|
|
151
|
-
let class = pl.const_get::<_, Value>("Int64")
|
|
152
|
-
class.funcall("new", ())
|
|
157
|
+
let class = pl.const_get::<_, Value>("Int64")?;
|
|
158
|
+
class.funcall("new", ())
|
|
153
159
|
}
|
|
154
160
|
DataType::Int128 => {
|
|
155
|
-
let class = pl.const_get::<_, Value>("Int128")
|
|
156
|
-
class.funcall("new", ())
|
|
161
|
+
let class = pl.const_get::<_, Value>("Int128")?;
|
|
162
|
+
class.funcall("new", ())
|
|
157
163
|
}
|
|
158
164
|
DataType::UInt8 => {
|
|
159
|
-
let class = pl.const_get::<_, Value>("UInt8")
|
|
160
|
-
class.funcall("new", ())
|
|
165
|
+
let class = pl.const_get::<_, Value>("UInt8")?;
|
|
166
|
+
class.funcall("new", ())
|
|
161
167
|
}
|
|
162
168
|
DataType::UInt16 => {
|
|
163
|
-
let class = pl.const_get::<_, Value>("UInt16")
|
|
164
|
-
class.funcall("new", ())
|
|
169
|
+
let class = pl.const_get::<_, Value>("UInt16")?;
|
|
170
|
+
class.funcall("new", ())
|
|
165
171
|
}
|
|
166
172
|
DataType::UInt32 => {
|
|
167
|
-
let class = pl.const_get::<_, Value>("UInt32")
|
|
168
|
-
class.funcall("new", ())
|
|
173
|
+
let class = pl.const_get::<_, Value>("UInt32")?;
|
|
174
|
+
class.funcall("new", ())
|
|
169
175
|
}
|
|
170
176
|
DataType::UInt64 => {
|
|
171
|
-
let class = pl.const_get::<_, Value>("UInt64")
|
|
172
|
-
class.funcall("new", ())
|
|
177
|
+
let class = pl.const_get::<_, Value>("UInt64")?;
|
|
178
|
+
class.funcall("new", ())
|
|
173
179
|
}
|
|
174
180
|
DataType::UInt128 => {
|
|
175
|
-
let class = pl.const_get::<_, Value>("UInt128")
|
|
176
|
-
class.funcall("new", ())
|
|
181
|
+
let class = pl.const_get::<_, Value>("UInt128")?;
|
|
182
|
+
class.funcall("new", ())
|
|
177
183
|
}
|
|
178
184
|
DataType::Float16 => {
|
|
179
|
-
let class = pl.const_get::<_, Value>("Float16")
|
|
180
|
-
class.funcall("new", ())
|
|
185
|
+
let class = pl.const_get::<_, Value>("Float16")?;
|
|
186
|
+
class.funcall("new", ())
|
|
181
187
|
}
|
|
182
188
|
DataType::Float32 => {
|
|
183
|
-
let class = pl.const_get::<_, Value>("Float32")
|
|
184
|
-
class.funcall("new", ())
|
|
189
|
+
let class = pl.const_get::<_, Value>("Float32")?;
|
|
190
|
+
class.funcall("new", ())
|
|
185
191
|
}
|
|
186
192
|
DataType::Float64 | DataType::Unknown(UnknownKind::Float) => {
|
|
187
|
-
let class = pl.const_get::<_, Value>("Float64")
|
|
188
|
-
class.funcall("new", ())
|
|
193
|
+
let class = pl.const_get::<_, Value>("Float64")?;
|
|
194
|
+
class.funcall("new", ())
|
|
189
195
|
}
|
|
190
196
|
DataType::Decimal(precision, scale) => {
|
|
191
|
-
let class = pl.const_get::<_, Value>("Decimal")
|
|
192
|
-
class
|
|
193
|
-
.funcall::<_, _, Value>("new", (precision, scale))
|
|
194
|
-
.unwrap()
|
|
197
|
+
let class = pl.const_get::<_, Value>("Decimal")?;
|
|
198
|
+
class.funcall::<_, _, Value>("new", (precision, scale))
|
|
195
199
|
}
|
|
196
200
|
DataType::Boolean => {
|
|
197
|
-
let class = pl.const_get::<_, Value>("Boolean")
|
|
198
|
-
class.funcall("new", ())
|
|
201
|
+
let class = pl.const_get::<_, Value>("Boolean")?;
|
|
202
|
+
class.funcall("new", ())
|
|
199
203
|
}
|
|
200
204
|
DataType::String | DataType::Unknown(UnknownKind::Str) => {
|
|
201
|
-
let class = pl.const_get::<_, Value>("String")
|
|
202
|
-
class.funcall("new", ())
|
|
205
|
+
let class = pl.const_get::<_, Value>("String")?;
|
|
206
|
+
class.funcall("new", ())
|
|
203
207
|
}
|
|
204
208
|
DataType::Binary => {
|
|
205
|
-
let class = pl.const_get::<_, Value>("Binary")
|
|
206
|
-
class.funcall("new", ())
|
|
209
|
+
let class = pl.const_get::<_, Value>("Binary")?;
|
|
210
|
+
class.funcall("new", ())
|
|
207
211
|
}
|
|
208
212
|
DataType::Array(inner, size) => {
|
|
209
|
-
let class = pl.const_get::<_, Value>("Array")
|
|
210
|
-
let inner = Wrap(*inner)
|
|
213
|
+
let class = pl.const_get::<_, Value>("Array")?;
|
|
214
|
+
let inner = Wrap(*inner).try_into_value_with(ruby)?;
|
|
211
215
|
let args = (inner, size);
|
|
212
|
-
class.funcall::<_, _, Value>("new", args)
|
|
216
|
+
class.funcall::<_, _, Value>("new", args)
|
|
213
217
|
}
|
|
214
218
|
DataType::List(inner) => {
|
|
215
|
-
let class = pl.const_get::<_, Value>("List")
|
|
216
|
-
let inner = Wrap(*inner)
|
|
217
|
-
class.funcall::<_, _, Value>("new", (inner,))
|
|
219
|
+
let class = pl.const_get::<_, Value>("List")?;
|
|
220
|
+
let inner = Wrap(*inner).try_into_value_with(ruby)?;
|
|
221
|
+
class.funcall::<_, _, Value>("new", (inner,))
|
|
218
222
|
}
|
|
219
223
|
DataType::Date => {
|
|
220
|
-
let class = pl.const_get::<_, Value>("Date")
|
|
221
|
-
class.funcall("new", ())
|
|
224
|
+
let class = pl.const_get::<_, Value>("Date")?;
|
|
225
|
+
class.funcall("new", ())
|
|
222
226
|
}
|
|
223
227
|
DataType::Datetime(tu, tz) => {
|
|
224
|
-
let datetime_class = pl.const_get::<_, Value>("Datetime")
|
|
225
|
-
datetime_class
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
)
|
|
230
|
-
.unwrap()
|
|
228
|
+
let datetime_class = pl.const_get::<_, Value>("Datetime")?;
|
|
229
|
+
datetime_class.funcall::<_, _, Value>(
|
|
230
|
+
"new",
|
|
231
|
+
(tu.to_ascii(), tz.as_deref().map(|x| x.as_str())),
|
|
232
|
+
)
|
|
231
233
|
}
|
|
232
234
|
DataType::Duration(tu) => {
|
|
233
|
-
let duration_class = pl.const_get::<_, Value>("Duration")
|
|
234
|
-
duration_class
|
|
235
|
-
.funcall::<_, _, Value>("new", (tu.to_ascii(),))
|
|
236
|
-
.unwrap()
|
|
235
|
+
let duration_class = pl.const_get::<_, Value>("Duration")?;
|
|
236
|
+
duration_class.funcall::<_, _, Value>("new", (tu.to_ascii(),))
|
|
237
237
|
}
|
|
238
238
|
DataType::Object(_) => {
|
|
239
|
-
let class = pl.const_get::<_, Value>("Object")
|
|
240
|
-
class.funcall("new", ())
|
|
239
|
+
let class = pl.const_get::<_, Value>("Object")?;
|
|
240
|
+
class.funcall("new", ())
|
|
241
241
|
}
|
|
242
242
|
DataType::Categorical(cats, _) => {
|
|
243
|
-
let categories_class = pl.const_get::<_, Value>("Categories")
|
|
244
|
-
let categorical_class = pl.const_get::<_, Value>("Categorical")
|
|
243
|
+
let categories_class = pl.const_get::<_, Value>("Categories")?;
|
|
244
|
+
let categorical_class = pl.const_get::<_, Value>("Categorical")?;
|
|
245
245
|
let categories: Value = categories_class
|
|
246
|
-
.funcall("_from_rb_categories", (RbCategories::from(cats.clone()),))
|
|
247
|
-
.unwrap();
|
|
246
|
+
.funcall("_from_rb_categories", (RbCategories::from(cats.clone()),))?;
|
|
248
247
|
let kwargs = ruby.hash_new();
|
|
249
|
-
kwargs
|
|
250
|
-
|
|
251
|
-
.unwrap();
|
|
252
|
-
categorical_class.funcall("new", (kwargs,)).unwrap()
|
|
248
|
+
kwargs.aset(ruby.to_symbol("categories"), categories)?;
|
|
249
|
+
categorical_class.funcall("new", (kwargs,))
|
|
253
250
|
}
|
|
254
251
|
DataType::Enum(_, mapping) => {
|
|
255
252
|
let categories = unsafe {
|
|
@@ -258,42 +255,38 @@ impl IntoValue for Wrap<DataType> {
|
|
|
258
255
|
vec![mapping.to_arrow(true)],
|
|
259
256
|
)
|
|
260
257
|
};
|
|
261
|
-
let class = pl.const_get::<_, Value>("Enum")
|
|
262
|
-
let series = to_series(ruby, categories.into_series().into())
|
|
263
|
-
class.funcall::<_, _, Value>("new", (series,))
|
|
258
|
+
let class = pl.const_get::<_, Value>("Enum")?;
|
|
259
|
+
let series = to_series(ruby, categories.into_series().into())?;
|
|
260
|
+
class.funcall::<_, _, Value>("new", (series,))
|
|
264
261
|
}
|
|
265
262
|
DataType::Time => {
|
|
266
|
-
let class = pl.const_get::<_, Value>("Time")
|
|
267
|
-
class.funcall("new", ())
|
|
263
|
+
let class = pl.const_get::<_, Value>("Time")?;
|
|
264
|
+
class.funcall("new", ())
|
|
268
265
|
}
|
|
269
266
|
DataType::Struct(fields) => {
|
|
270
|
-
let field_class = pl.const_get::<_, Value>("Field")
|
|
267
|
+
let field_class = pl.const_get::<_, Value>("Field")?;
|
|
271
268
|
let iter = fields.iter().map(|fld| {
|
|
272
269
|
let name = fld.name().as_str();
|
|
273
|
-
let dtype = Wrap(fld.dtype().clone());
|
|
274
|
-
field_class
|
|
275
|
-
.funcall::<_, _, Value>("new", (name, dtype))
|
|
276
|
-
.unwrap()
|
|
270
|
+
let dtype = Wrap(fld.dtype().clone()).try_into_value_with(ruby);
|
|
271
|
+
dtype.and_then(|dt| field_class.funcall::<_, _, Value>("new", (name, dt)))
|
|
277
272
|
});
|
|
278
|
-
let fields = ruby.
|
|
279
|
-
let struct_class = pl.const_get::<_, Value>("Struct")
|
|
280
|
-
struct_class
|
|
281
|
-
.funcall::<_, _, Value>("new", (fields,))
|
|
282
|
-
.unwrap()
|
|
273
|
+
let fields = ruby.ary_try_from_iter(iter)?;
|
|
274
|
+
let struct_class = pl.const_get::<_, Value>("Struct")?;
|
|
275
|
+
struct_class.funcall::<_, _, Value>("new", (fields,))
|
|
283
276
|
}
|
|
284
277
|
DataType::Null => {
|
|
285
|
-
let class = pl.const_get::<_, Value>("Null")
|
|
286
|
-
class.funcall("new", ())
|
|
278
|
+
let class = pl.const_get::<_, Value>("Null")?;
|
|
279
|
+
class.funcall("new", ())
|
|
287
280
|
}
|
|
288
281
|
DataType::Extension(_typ, _storage) => {
|
|
289
282
|
todo!();
|
|
290
283
|
}
|
|
291
284
|
DataType::Unknown(UnknownKind::Int(v)) => {
|
|
292
|
-
Wrap(materialize_dyn_int(v).dtype()).
|
|
285
|
+
Wrap(materialize_dyn_int(v).dtype()).try_into_value_with(ruby)
|
|
293
286
|
}
|
|
294
287
|
DataType::Unknown(_) => {
|
|
295
|
-
let class = pl.const_get::<_, Value>("Unknown")
|
|
296
|
-
class.funcall("new", ())
|
|
288
|
+
let class = pl.const_get::<_, Value>("Unknown")?;
|
|
289
|
+
class.funcall("new", ())
|
|
297
290
|
}
|
|
298
291
|
DataType::BinaryOffset => {
|
|
299
292
|
unimplemented!()
|
|
@@ -399,13 +392,12 @@ impl TryConvert for Wrap<DataType> {
|
|
|
399
392
|
"Polars::String" => DataType::String,
|
|
400
393
|
"Polars::Binary" => DataType::Binary,
|
|
401
394
|
"Polars::Categorical" => {
|
|
402
|
-
let categories: Value = ob.funcall("categories", ())
|
|
403
|
-
let rb_categories: &RbCategories =
|
|
404
|
-
categories.funcall("_categories", ()).unwrap();
|
|
395
|
+
let categories: Value = ob.funcall("categories", ())?;
|
|
396
|
+
let rb_categories: &RbCategories = categories.funcall("_categories", ())?;
|
|
405
397
|
DataType::from_categories(rb_categories.categories().clone())
|
|
406
398
|
}
|
|
407
399
|
"Polars::Enum" => {
|
|
408
|
-
let categories: Value = ob.funcall("categories", ())
|
|
400
|
+
let categories: Value = ob.funcall("categories", ())?;
|
|
409
401
|
let s = get_series(categories)?;
|
|
410
402
|
let ca = s.str().map_err(RbPolarsErr::from)?;
|
|
411
403
|
let categories = ca.downcast_iter().next().unwrap().clone();
|
|
@@ -417,7 +409,7 @@ impl TryConvert for Wrap<DataType> {
|
|
|
417
409
|
"Polars::Date" => DataType::Date,
|
|
418
410
|
"Polars::Time" => DataType::Time,
|
|
419
411
|
"Polars::Datetime" => {
|
|
420
|
-
let time_unit: Value = ob.funcall("time_unit", ())
|
|
412
|
+
let time_unit: Value = ob.funcall("time_unit", ())?;
|
|
421
413
|
let time_unit = Wrap::<TimeUnit>::try_convert(time_unit)?.0;
|
|
422
414
|
let time_zone: Option<String> = ob.funcall("time_zone", ())?;
|
|
423
415
|
DataType::Datetime(
|
|
@@ -426,7 +418,7 @@ impl TryConvert for Wrap<DataType> {
|
|
|
426
418
|
)
|
|
427
419
|
}
|
|
428
420
|
"Polars::Duration" => {
|
|
429
|
-
let time_unit: Value = ob.funcall("time_unit", ())
|
|
421
|
+
let time_unit: Value = ob.funcall("time_unit", ())?;
|
|
430
422
|
let time_unit = Wrap::<TimeUnit>::try_convert(time_unit)?.0;
|
|
431
423
|
DataType::Duration(time_unit)
|
|
432
424
|
}
|
|
@@ -437,13 +429,13 @@ impl TryConvert for Wrap<DataType> {
|
|
|
437
429
|
DataType::Decimal(precision, scale)
|
|
438
430
|
}
|
|
439
431
|
"Polars::List" => {
|
|
440
|
-
let inner: Value = ob.funcall("inner", ())
|
|
432
|
+
let inner: Value = ob.funcall("inner", ())?;
|
|
441
433
|
let inner = Wrap::<DataType>::try_convert(inner)?;
|
|
442
434
|
DataType::List(Box::new(inner.0))
|
|
443
435
|
}
|
|
444
436
|
"Polars::Array" => {
|
|
445
|
-
let inner: Value = ob.funcall("inner", ())
|
|
446
|
-
let size: Value = ob.funcall("size", ())
|
|
437
|
+
let inner: Value = ob.funcall("inner", ())?;
|
|
438
|
+
let size: Value = ob.funcall("size", ())?;
|
|
447
439
|
let inner = Wrap::<DataType>::try_convert(inner)?;
|
|
448
440
|
let size = usize::try_convert(size)?;
|
|
449
441
|
DataType::Array(Box::new(inner.0), size)
|
|
@@ -524,7 +516,28 @@ impl TryConvert for Wrap<Schema> {
|
|
|
524
516
|
impl TryConvert for Wrap<ArrowSchema> {
|
|
525
517
|
fn try_convert(ob: Value) -> RbResult<Self> {
|
|
526
518
|
let ruby = Ruby::get_with(ob);
|
|
527
|
-
|
|
519
|
+
|
|
520
|
+
if let Ok(schema_capsule) = ob.funcall::<_, _, Value>("arrow_c_schema", ()) {
|
|
521
|
+
let field = import_schema_rbcapsule(schema_capsule)?;
|
|
522
|
+
|
|
523
|
+
let ArrowDataType::Struct(fields) = field.dtype else {
|
|
524
|
+
return Err(RbValueError::new_err(format!(
|
|
525
|
+
"arrow_c_schema of object did not return struct dtype: \
|
|
526
|
+
object: {:?}, dtype: {:?}",
|
|
527
|
+
ob, &field.dtype
|
|
528
|
+
)));
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
let mut schema = ArrowSchema::from_iter_check_duplicates(fields).unwrap();
|
|
532
|
+
|
|
533
|
+
if let Some(md) = field.metadata {
|
|
534
|
+
*schema.metadata_mut() = Arc::unwrap_or_clone(md);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return Ok(Wrap(schema));
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// TODO remove in 0.27.0
|
|
528
541
|
let ob = RHash::try_convert(ob)?;
|
|
529
542
|
let fields: RArray = ob.aref(ruby.to_symbol("fields"))?;
|
|
530
543
|
let mut arrow_schema = ArrowSchema::with_capacity(fields.len());
|
|
@@ -555,6 +568,33 @@ impl TryConvert for Wrap<ArrowSchema> {
|
|
|
555
568
|
"binary_view" => ArrowDataType::BinaryView,
|
|
556
569
|
"string_view" => ArrowDataType::Utf8View,
|
|
557
570
|
"unknown" => ArrowDataType::Unknown,
|
|
571
|
+
"timestamp" => {
|
|
572
|
+
let time_unit: String = f.aref(ruby.to_symbol("time_unit"))?;
|
|
573
|
+
let time_zone: Option<String> = f.aref(ruby.to_symbol("time_zone"))?;
|
|
574
|
+
let arrow_time_unit = match time_unit.as_str() {
|
|
575
|
+
"us" => ArrowTimeUnit::Microsecond,
|
|
576
|
+
"ns" => ArrowTimeUnit::Nanosecond,
|
|
577
|
+
_ => todo!(),
|
|
578
|
+
};
|
|
579
|
+
ArrowDataType::Timestamp(arrow_time_unit, time_zone.map(|v| v.into()))
|
|
580
|
+
}
|
|
581
|
+
"time64" => {
|
|
582
|
+
let time_unit: String = f.aref(ruby.to_symbol("time_unit"))?;
|
|
583
|
+
let arrow_time_unit = match time_unit.as_str() {
|
|
584
|
+
"us" => ArrowTimeUnit::Microsecond,
|
|
585
|
+
_ => todo!(),
|
|
586
|
+
};
|
|
587
|
+
ArrowDataType::Time64(arrow_time_unit)
|
|
588
|
+
}
|
|
589
|
+
"decimal" => {
|
|
590
|
+
let precision: usize = f.aref(ruby.to_symbol("precision"))?;
|
|
591
|
+
let scale: usize = f.aref(ruby.to_symbol("scale"))?;
|
|
592
|
+
ArrowDataType::Decimal(precision, scale)
|
|
593
|
+
}
|
|
594
|
+
"fixed_size_binary" => {
|
|
595
|
+
let limit: usize = f.aref(ruby.to_symbol("limit"))?;
|
|
596
|
+
ArrowDataType::FixedSizeBinary(limit)
|
|
597
|
+
}
|
|
558
598
|
_ => todo!(),
|
|
559
599
|
};
|
|
560
600
|
let is_nullable = f.aref(ruby.to_symbol("nullable"))?;
|
|
@@ -638,13 +678,13 @@ impl TryConvert for Wrap<ScanSources> {
|
|
|
638
678
|
}
|
|
639
679
|
}
|
|
640
680
|
|
|
641
|
-
impl
|
|
642
|
-
fn
|
|
681
|
+
impl TryIntoValue for Wrap<Schema> {
|
|
682
|
+
fn try_into_value_with(self, ruby: &Ruby) -> RbResult<Value> {
|
|
643
683
|
let dict = ruby.hash_new();
|
|
644
684
|
for (k, v) in self.0.iter() {
|
|
645
|
-
dict.aset(k.as_str(), Wrap(v.clone())
|
|
685
|
+
dict.aset(k.as_str(), Wrap(v.clone()).try_into_value_with(ruby)?)?;
|
|
646
686
|
}
|
|
647
|
-
dict.as_value()
|
|
687
|
+
Ok(dict.as_value())
|
|
648
688
|
}
|
|
649
689
|
}
|
|
650
690
|
|
|
@@ -655,18 +695,17 @@ pub struct ObjectValue {
|
|
|
655
695
|
|
|
656
696
|
impl Debug for ObjectValue {
|
|
657
697
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
658
|
-
f
|
|
659
|
-
.field("inner", &self.to_value())
|
|
660
|
-
.finish()
|
|
698
|
+
write!(f, "{}", self)
|
|
661
699
|
}
|
|
662
700
|
}
|
|
663
701
|
|
|
664
702
|
impl Hash for ObjectValue {
|
|
665
703
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
666
|
-
let h =
|
|
667
|
-
.
|
|
668
|
-
|
|
669
|
-
|
|
704
|
+
let h = Ruby::attach(|rb| {
|
|
705
|
+
rb.get_inner(self.inner)
|
|
706
|
+
.funcall::<_, _, isize>("hash", ())
|
|
707
|
+
.expect("should be hashable")
|
|
708
|
+
});
|
|
670
709
|
state.write_isize(h)
|
|
671
710
|
}
|
|
672
711
|
}
|
|
@@ -675,7 +714,11 @@ impl Eq for ObjectValue {}
|
|
|
675
714
|
|
|
676
715
|
impl PartialEq for ObjectValue {
|
|
677
716
|
fn eq(&self, other: &Self) -> bool {
|
|
678
|
-
|
|
717
|
+
Ruby::attach(|ruby| {
|
|
718
|
+
ruby.get_inner(self.inner)
|
|
719
|
+
.eql(ruby.get_inner(other.inner))
|
|
720
|
+
.unwrap_or(false)
|
|
721
|
+
})
|
|
679
722
|
}
|
|
680
723
|
}
|
|
681
724
|
|
|
@@ -696,7 +739,10 @@ impl TotalHash for ObjectValue {
|
|
|
696
739
|
|
|
697
740
|
impl Display for ObjectValue {
|
|
698
741
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
699
|
-
|
|
742
|
+
Ruby::attach(|rb| {
|
|
743
|
+
let v = rb.get_inner(self.inner);
|
|
744
|
+
write!(f, "{}", v)
|
|
745
|
+
})
|
|
700
746
|
}
|
|
701
747
|
}
|
|
702
748
|
|
|
@@ -724,12 +770,6 @@ impl From<&dyn PolarsObjectSafe> for &ObjectValue {
|
|
|
724
770
|
}
|
|
725
771
|
}
|
|
726
772
|
|
|
727
|
-
impl ObjectValue {
|
|
728
|
-
pub fn to_value(&self) -> Value {
|
|
729
|
-
self.clone().into_value_with(&Ruby::get().unwrap())
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
|
|
733
773
|
impl IntoValue for ObjectValue {
|
|
734
774
|
fn into_value_with(self, ruby: &Ruby) -> Value {
|
|
735
775
|
ruby.get_inner(self.inner)
|
|
@@ -738,9 +778,9 @@ impl IntoValue for ObjectValue {
|
|
|
738
778
|
|
|
739
779
|
impl Default for ObjectValue {
|
|
740
780
|
fn default() -> Self {
|
|
741
|
-
ObjectValue {
|
|
742
|
-
inner:
|
|
743
|
-
}
|
|
781
|
+
Ruby::attach(|rb| ObjectValue {
|
|
782
|
+
inner: rb.qnil().as_value().into(),
|
|
783
|
+
})
|
|
744
784
|
}
|
|
745
785
|
}
|
|
746
786
|
|
|
@@ -1125,6 +1165,21 @@ impl TryConvert for Wrap<SearchSortedSide> {
|
|
|
1125
1165
|
}
|
|
1126
1166
|
}
|
|
1127
1167
|
|
|
1168
|
+
impl TryConvert for Wrap<PivotColumnNaming> {
|
|
1169
|
+
fn try_convert(ob: Value) -> RbResult<Self> {
|
|
1170
|
+
let parsed = match String::try_convert(ob)?.as_str() {
|
|
1171
|
+
"auto" => PivotColumnNaming::Auto,
|
|
1172
|
+
"combine" => PivotColumnNaming::Combine,
|
|
1173
|
+
v => {
|
|
1174
|
+
return Err(RbValueError::new_err(format!(
|
|
1175
|
+
"`column_naming` must be one of {{'auto', 'combine'}}, got {v}",
|
|
1176
|
+
)));
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
Ok(Wrap(parsed))
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1128
1183
|
impl TryConvert for Wrap<ClosedInterval> {
|
|
1129
1184
|
fn try_convert(ob: Value) -> RbResult<Self> {
|
|
1130
1185
|
let parsed = match String::try_convert(ob)?.as_str() {
|
|
@@ -1234,15 +1289,25 @@ impl TryConvert for Wrap<CastColumnsPolicy> {
|
|
|
1234
1289
|
return Ok(out);
|
|
1235
1290
|
}
|
|
1236
1291
|
|
|
1237
|
-
let integer_upcast =
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1292
|
+
let mut integer_upcast = false;
|
|
1293
|
+
let mut integer_to_float_cast = false;
|
|
1294
|
+
|
|
1295
|
+
let integer_cast_object: Value = ob.funcall("integer_cast", ())?;
|
|
1296
|
+
|
|
1297
|
+
parse_multiple_options("integer_cast", integer_cast_object, |v| {
|
|
1298
|
+
match v {
|
|
1299
|
+
"upcast" => integer_upcast = true,
|
|
1300
|
+
"allow-float" => integer_to_float_cast = true,
|
|
1301
|
+
"forbid" => {}
|
|
1302
|
+
v => {
|
|
1303
|
+
return Err(RbValueError::new_err(format!(
|
|
1304
|
+
"unknown option for integer_cast: {v}"
|
|
1305
|
+
)));
|
|
1306
|
+
}
|
|
1244
1307
|
}
|
|
1245
|
-
|
|
1308
|
+
|
|
1309
|
+
Ok(())
|
|
1310
|
+
})?;
|
|
1246
1311
|
|
|
1247
1312
|
let mut float_upcast = false;
|
|
1248
1313
|
let mut float_downcast = false;
|
|
@@ -1318,6 +1383,7 @@ impl TryConvert for Wrap<CastColumnsPolicy> {
|
|
|
1318
1383
|
|
|
1319
1384
|
return Ok(Wrap(CastColumnsPolicy {
|
|
1320
1385
|
integer_upcast,
|
|
1386
|
+
integer_to_float_cast,
|
|
1321
1387
|
float_upcast,
|
|
1322
1388
|
float_downcast,
|
|
1323
1389
|
datetime_nanoseconds_downcast,
|
|
@@ -1364,12 +1430,9 @@ pub fn parse_fill_null_strategy(
|
|
|
1364
1430
|
"zero" => FillNullStrategy::Zero,
|
|
1365
1431
|
"one" => FillNullStrategy::One,
|
|
1366
1432
|
e => {
|
|
1367
|
-
return Err(
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
"strategy must be one of {{'forward', 'backward', 'min', 'max', 'mean', 'zero', 'one'}}, got {e}",
|
|
1371
|
-
),
|
|
1372
|
-
));
|
|
1433
|
+
return Err(RbValueError::new_err(format!(
|
|
1434
|
+
"`strategy` must be one of {{'forward', 'backward', 'min', 'max', 'mean', 'zero', 'one'}}, got {e}",
|
|
1435
|
+
)));
|
|
1373
1436
|
}
|
|
1374
1437
|
};
|
|
1375
1438
|
Ok(parsed)
|
|
@@ -1,39 +1,42 @@
|
|
|
1
|
-
use magnus::{
|
|
1
|
+
use magnus::{Ruby, Value, prelude::*};
|
|
2
2
|
|
|
3
3
|
use super::*;
|
|
4
4
|
use crate::RbResult;
|
|
5
5
|
use crate::conversion::{ObjectValue, Wrap};
|
|
6
6
|
use crate::interop::arrow::to_rb::dataframe_to_stream;
|
|
7
|
+
use crate::ruby::utils::TryIntoValue;
|
|
7
8
|
|
|
8
9
|
impl RbDataFrame {
|
|
9
|
-
pub fn row_tuple(ruby: &Ruby, self_: &Self, idx: i64) -> Value {
|
|
10
|
+
pub fn row_tuple(ruby: &Ruby, self_: &Self, idx: i64) -> RbResult<Value> {
|
|
10
11
|
let idx = if idx < 0 {
|
|
11
12
|
(self_.df.read().height() as i64 + idx) as usize
|
|
12
13
|
} else {
|
|
13
14
|
idx as usize
|
|
14
15
|
};
|
|
15
|
-
ruby.
|
|
16
|
+
ruby.ary_try_from_iter(self_.df.read().columns().iter().map(|s| match s.dtype() {
|
|
16
17
|
DataType::Object(_) => {
|
|
17
18
|
let obj: Option<&ObjectValue> = s.get_object(idx).map(|any| any.into());
|
|
18
|
-
|
|
19
|
+
// TODO remove unwrap and clone
|
|
20
|
+
obj.unwrap().clone().try_into_value_with(ruby)
|
|
19
21
|
}
|
|
20
|
-
_ => Wrap(s.get(idx).unwrap()).
|
|
22
|
+
_ => Wrap(s.get(idx).unwrap()).try_into_value_with(ruby),
|
|
21
23
|
}))
|
|
22
|
-
.as_value()
|
|
24
|
+
.map(|v| v.as_value())
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
pub fn row_tuples(ruby: &Ruby, self_: &Self) -> Value {
|
|
27
|
+
pub fn row_tuples(ruby: &Ruby, self_: &Self) -> RbResult<Value> {
|
|
26
28
|
let df = &self_.df;
|
|
27
|
-
ruby.
|
|
28
|
-
ruby.
|
|
29
|
+
ruby.ary_try_from_iter((0..df.read().height()).map(|idx| {
|
|
30
|
+
ruby.ary_try_from_iter(self_.df.read().columns().iter().map(|s| match s.dtype() {
|
|
29
31
|
DataType::Object(_) => {
|
|
30
32
|
let obj: Option<&ObjectValue> = s.get_object(idx).map(|any| any.into());
|
|
31
|
-
|
|
33
|
+
// TODO remove unwrap and clone
|
|
34
|
+
obj.unwrap().clone().try_into_value_with(ruby)
|
|
32
35
|
}
|
|
33
|
-
_ => Wrap(s.get(idx).unwrap()).
|
|
36
|
+
_ => Wrap(s.get(idx).unwrap()).try_into_value_with(ruby),
|
|
34
37
|
}))
|
|
35
38
|
}))
|
|
36
|
-
.as_value()
|
|
39
|
+
.map(|v| v.as_value())
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
pub fn __arrow_c_stream__(ruby: &Ruby, self_: &Self) -> RbResult<Value> {
|