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,16 +1,14 @@
|
|
|
1
|
-
use magnus::{prelude
|
|
2
|
-
use polars::io::avro::AvroCompression;
|
|
1
|
+
use magnus::{Value, prelude::*};
|
|
3
2
|
use polars::io::RowIndex;
|
|
3
|
+
use polars::io::avro::AvroCompression;
|
|
4
4
|
use polars::prelude::*;
|
|
5
|
-
use polars_utils::mmap::ensure_not_mapped;
|
|
6
5
|
use std::io::BufWriter;
|
|
7
6
|
use std::num::NonZeroUsize;
|
|
8
7
|
|
|
9
8
|
use super::*;
|
|
10
9
|
use crate::conversion::*;
|
|
11
10
|
use crate::file::{
|
|
12
|
-
|
|
13
|
-
read_if_bytesio, EitherRustRubyFile,
|
|
11
|
+
get_file_like, get_mmap_bytes_reader, get_mmap_bytes_reader_and_path, read_if_bytesio,
|
|
14
12
|
};
|
|
15
13
|
use crate::{RbPolarsErr, RbResult};
|
|
16
14
|
|
|
@@ -25,28 +23,29 @@ impl RbDataFrame {
|
|
|
25
23
|
let ignore_errors = bool::try_convert(arguments[4])?;
|
|
26
24
|
let n_rows = Option::<usize>::try_convert(arguments[5])?;
|
|
27
25
|
let skip_rows = usize::try_convert(arguments[6])?;
|
|
28
|
-
let
|
|
29
|
-
let
|
|
30
|
-
let
|
|
31
|
-
let
|
|
32
|
-
let
|
|
33
|
-
let
|
|
34
|
-
let
|
|
35
|
-
let
|
|
36
|
-
let
|
|
37
|
-
let
|
|
38
|
-
let
|
|
39
|
-
let
|
|
40
|
-
let
|
|
41
|
-
let
|
|
42
|
-
let
|
|
43
|
-
let
|
|
44
|
-
let
|
|
45
|
-
let
|
|
46
|
-
let
|
|
47
|
-
let
|
|
48
|
-
let
|
|
49
|
-
let
|
|
26
|
+
let skip_lines = usize::try_convert(arguments[7])?;
|
|
27
|
+
let projection = Option::<Vec<usize>>::try_convert(arguments[8])?;
|
|
28
|
+
let separator = String::try_convert(arguments[9])?;
|
|
29
|
+
let rechunk = bool::try_convert(arguments[10])?;
|
|
30
|
+
let columns = Option::<Vec<String>>::try_convert(arguments[11])?;
|
|
31
|
+
let encoding = Wrap::<CsvEncoding>::try_convert(arguments[12])?;
|
|
32
|
+
let n_threads = Option::<usize>::try_convert(arguments[13])?;
|
|
33
|
+
let path = Option::<String>::try_convert(arguments[14])?;
|
|
34
|
+
let overwrite_dtype = Option::<Vec<(String, Wrap<DataType>)>>::try_convert(arguments[15])?;
|
|
35
|
+
let overwrite_dtype_slice = Option::<Vec<Wrap<DataType>>>::try_convert(arguments[16])?;
|
|
36
|
+
let low_memory = bool::try_convert(arguments[17])?;
|
|
37
|
+
let comment_prefix = Option::<String>::try_convert(arguments[18])?;
|
|
38
|
+
let quote_char = Option::<String>::try_convert(arguments[19])?;
|
|
39
|
+
let null_values = Option::<Wrap<NullValues>>::try_convert(arguments[20])?;
|
|
40
|
+
let missing_utf8_is_empty_string = bool::try_convert(arguments[21])?;
|
|
41
|
+
let try_parse_dates = bool::try_convert(arguments[22])?;
|
|
42
|
+
let skip_rows_after_header = usize::try_convert(arguments[23])?;
|
|
43
|
+
let row_index = Option::<(String, IdxSize)>::try_convert(arguments[24])?;
|
|
44
|
+
let eol_char = String::try_convert(arguments[25])?;
|
|
45
|
+
let raise_if_empty = bool::try_convert(arguments[26])?;
|
|
46
|
+
let truncate_ragged_lines = bool::try_convert(arguments[27])?;
|
|
47
|
+
let decimal_comma = bool::try_convert(arguments[28])?;
|
|
48
|
+
let schema = Option::<Wrap<Schema>>::try_convert(arguments[29])?;
|
|
50
49
|
// end arguments
|
|
51
50
|
|
|
52
51
|
let null_values = null_values.map(|w| w.0);
|
|
@@ -90,6 +89,7 @@ impl RbDataFrame {
|
|
|
90
89
|
.with_has_header(has_header)
|
|
91
90
|
.with_n_rows(n_rows)
|
|
92
91
|
.with_skip_rows(skip_rows)
|
|
92
|
+
.with_skip_lines(skip_lines)
|
|
93
93
|
.with_ignore_errors(ignore_errors)
|
|
94
94
|
.with_projection(projection.map(Arc::new))
|
|
95
95
|
.with_rechunk(rechunk)
|
|
@@ -147,33 +147,6 @@ impl RbDataFrame {
|
|
|
147
147
|
Ok(out.into())
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
pub fn read_ndjson(
|
|
151
|
-
rb_f: Value,
|
|
152
|
-
ignore_errors: bool,
|
|
153
|
-
schema: Option<Wrap<Schema>>,
|
|
154
|
-
schema_overrides: Option<Wrap<Schema>>,
|
|
155
|
-
) -> RbResult<Self> {
|
|
156
|
-
let rb_f = read_if_bytesio(rb_f);
|
|
157
|
-
let mmap_bytes_r = get_mmap_bytes_reader(&rb_f)?;
|
|
158
|
-
|
|
159
|
-
let mut builder = JsonReader::new(mmap_bytes_r)
|
|
160
|
-
.with_json_format(JsonFormat::JsonLines)
|
|
161
|
-
.with_ignore_errors(ignore_errors);
|
|
162
|
-
|
|
163
|
-
if let Some(schema) = schema {
|
|
164
|
-
builder = builder.with_schema(Arc::new(schema.0));
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if let Some(schema) = schema_overrides.as_ref() {
|
|
168
|
-
builder = builder.with_schema_overwrite(&schema.0);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
let out = builder
|
|
172
|
-
.finish()
|
|
173
|
-
.map_err(|e| RbPolarsErr::Other(format!("{e}")))?;
|
|
174
|
-
Ok(out.into())
|
|
175
|
-
}
|
|
176
|
-
|
|
177
150
|
pub fn read_ipc(
|
|
178
151
|
rb_f: Value,
|
|
179
152
|
columns: Option<Vec<String>>,
|
|
@@ -244,102 +217,12 @@ impl RbDataFrame {
|
|
|
244
217
|
Ok(RbDataFrame::new(df))
|
|
245
218
|
}
|
|
246
219
|
|
|
247
|
-
|
|
248
|
-
pub fn write_csv(
|
|
249
|
-
&self,
|
|
250
|
-
rb_f: Value,
|
|
251
|
-
include_header: bool,
|
|
252
|
-
separator: u8,
|
|
253
|
-
quote_char: u8,
|
|
254
|
-
batch_size: Wrap<NonZeroUsize>,
|
|
255
|
-
datetime_format: Option<String>,
|
|
256
|
-
date_format: Option<String>,
|
|
257
|
-
time_format: Option<String>,
|
|
258
|
-
float_precision: Option<usize>,
|
|
259
|
-
null_value: Option<String>,
|
|
260
|
-
) -> RbResult<()> {
|
|
261
|
-
let batch_size = batch_size.0;
|
|
262
|
-
let null = null_value.unwrap_or_default();
|
|
263
|
-
let mut buf = get_file_like(rb_f, true)?;
|
|
264
|
-
CsvWriter::new(&mut buf)
|
|
265
|
-
.include_header(include_header)
|
|
266
|
-
.with_separator(separator)
|
|
267
|
-
.with_quote_char(quote_char)
|
|
268
|
-
.with_batch_size(batch_size)
|
|
269
|
-
.with_datetime_format(datetime_format)
|
|
270
|
-
.with_date_format(date_format)
|
|
271
|
-
.with_time_format(time_format)
|
|
272
|
-
.with_float_precision(float_precision)
|
|
273
|
-
.with_null_value(null)
|
|
274
|
-
.finish(&mut self.df.borrow_mut())
|
|
275
|
-
.map_err(RbPolarsErr::from)?;
|
|
276
|
-
Ok(())
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
pub fn write_parquet(
|
|
280
|
-
&self,
|
|
281
|
-
rb_f: Value,
|
|
282
|
-
compression: String,
|
|
283
|
-
compression_level: Option<i32>,
|
|
284
|
-
statistics: Wrap<StatisticsOptions>,
|
|
285
|
-
row_group_size: Option<usize>,
|
|
286
|
-
data_page_size: Option<usize>,
|
|
287
|
-
) -> RbResult<()> {
|
|
288
|
-
let compression = parse_parquet_compression(&compression, compression_level)?;
|
|
289
|
-
|
|
290
|
-
let buf = get_file_like(rb_f, true)?;
|
|
291
|
-
ParquetWriter::new(buf)
|
|
292
|
-
.with_compression(compression)
|
|
293
|
-
.with_statistics(statistics.0)
|
|
294
|
-
.with_row_group_size(row_group_size)
|
|
295
|
-
.with_data_page_size(data_page_size)
|
|
296
|
-
.finish(&mut self.df.borrow_mut())
|
|
297
|
-
.map_err(RbPolarsErr::from)?;
|
|
298
|
-
Ok(())
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
pub fn write_json(&self, rb_f: Value, pretty: bool, row_oriented: bool) -> RbResult<()> {
|
|
220
|
+
pub fn write_json(&self, rb_f: Value) -> RbResult<()> {
|
|
302
221
|
let file = BufWriter::new(get_file_like(rb_f, true)?);
|
|
303
222
|
|
|
304
|
-
|
|
305
|
-
(
|
|
306
|
-
|
|
307
|
-
.finish(&mut self.df.borrow_mut()),
|
|
308
|
-
(true, _) => serde_json::to_writer_pretty(file, &*self.df.borrow())
|
|
309
|
-
.map_err(|e| PolarsError::ComputeError(format!("{:?}", e).into())),
|
|
310
|
-
(false, _) => serde_json::to_writer(file, &*self.df.borrow())
|
|
311
|
-
.map_err(|e| PolarsError::ComputeError(format!("{:?}", e).into())),
|
|
312
|
-
};
|
|
313
|
-
r.map_err(|e| RbPolarsErr::Other(format!("{:?}", e)))?;
|
|
314
|
-
Ok(())
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
pub fn write_ndjson(&self, rb_f: Value) -> RbResult<()> {
|
|
318
|
-
let file = BufWriter::new(get_file_like(rb_f, true)?);
|
|
319
|
-
|
|
320
|
-
let r = JsonWriter::new(file)
|
|
321
|
-
.with_json_format(JsonFormat::JsonLines)
|
|
322
|
-
.finish(&mut self.df.borrow_mut());
|
|
323
|
-
|
|
324
|
-
r.map_err(|e| RbPolarsErr::Other(format!("{:?}", e)))?;
|
|
325
|
-
Ok(())
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
pub fn write_ipc(
|
|
329
|
-
&self,
|
|
330
|
-
rb_f: Value,
|
|
331
|
-
compression: Wrap<Option<IpcCompression>>,
|
|
332
|
-
compat_level: RbCompatLevel,
|
|
333
|
-
) -> RbResult<()> {
|
|
334
|
-
let either = get_either_file(rb_f, true)?;
|
|
335
|
-
if let EitherRustRubyFile::Rust(ref f) = either {
|
|
336
|
-
ensure_not_mapped(f).map_err(RbPolarsErr::from)?;
|
|
337
|
-
}
|
|
338
|
-
let mut buf = either.into_dyn();
|
|
339
|
-
IpcWriter::new(&mut buf)
|
|
340
|
-
.with_compression(compression.0)
|
|
341
|
-
.with_compat_level(compat_level.0)
|
|
342
|
-
.finish(&mut self.df.borrow_mut())
|
|
223
|
+
JsonWriter::new(file)
|
|
224
|
+
.with_json_format(JsonFormat::Json)
|
|
225
|
+
.finish(&mut self.df.write())
|
|
343
226
|
.map_err(RbPolarsErr::from)?;
|
|
344
227
|
Ok(())
|
|
345
228
|
}
|
|
@@ -354,7 +237,7 @@ impl RbDataFrame {
|
|
|
354
237
|
IpcStreamWriter::new(&mut buf)
|
|
355
238
|
.with_compression(compression.0)
|
|
356
239
|
.with_compat_level(compat_level.0)
|
|
357
|
-
.finish(&mut self.df.
|
|
240
|
+
.finish(&mut self.df.write())
|
|
358
241
|
.map_err(RbPolarsErr::from)?;
|
|
359
242
|
Ok(())
|
|
360
243
|
}
|
|
@@ -370,7 +253,7 @@ impl RbDataFrame {
|
|
|
370
253
|
AvroWriter::new(&mut buf)
|
|
371
254
|
.with_compression(compression.0)
|
|
372
255
|
.with_name(name)
|
|
373
|
-
.finish(&mut self.df.
|
|
256
|
+
.finish(&mut self.df.write())
|
|
374
257
|
.map_err(RbPolarsErr::from)?;
|
|
375
258
|
Ok(())
|
|
376
259
|
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
use magnus::{IntoValue, RArray, Ruby, TryConvert, Value, value::Opaque, value::ReprValue};
|
|
2
|
+
use polars::frame::row::{Row, rows_to_schema_first_non_null};
|
|
3
|
+
use polars_core::utils::CustomIterTools;
|
|
4
|
+
|
|
5
|
+
use super::*;
|
|
6
|
+
use crate::error::RbPolarsErr;
|
|
7
|
+
use crate::prelude::*;
|
|
8
|
+
use crate::ruby::utils::{TryIntoValue, to_pl_err};
|
|
9
|
+
use crate::series::construction::series_from_objects;
|
|
10
|
+
use crate::{RbResult, RbSeries, raise_err};
|
|
11
|
+
|
|
12
|
+
impl RbDataFrame {
|
|
13
|
+
pub fn map_rows(
|
|
14
|
+
rb: &Ruby,
|
|
15
|
+
self_: &Self,
|
|
16
|
+
lambda: Value,
|
|
17
|
+
output_type: Option<Wrap<DataType>>,
|
|
18
|
+
inference_size: usize,
|
|
19
|
+
) -> RbResult<(Value, bool)> {
|
|
20
|
+
let df = self_.df.read();
|
|
21
|
+
let height = df.height();
|
|
22
|
+
let col_series: Vec<_> = df
|
|
23
|
+
.columns()
|
|
24
|
+
.iter()
|
|
25
|
+
.map(|s| s.as_materialized_series().clone())
|
|
26
|
+
.collect();
|
|
27
|
+
let mut iters: Vec<_> = col_series.iter().map(|c| c.iter()).collect();
|
|
28
|
+
drop(df); // Release lock before calling lambda.
|
|
29
|
+
|
|
30
|
+
let lambda_result_iter = (0..height).map(move |_| {
|
|
31
|
+
let iter = iters
|
|
32
|
+
.iter_mut()
|
|
33
|
+
.map(|it| Wrap(it.next().unwrap()).try_into_value_with(rb));
|
|
34
|
+
rb.ary_try_from_iter(iter)
|
|
35
|
+
.and_then(|tpl| lambda.funcall::<_, _, Value>("call", (tpl,)))
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Simple case: return type set.
|
|
39
|
+
if let Some(output_type) = &output_type {
|
|
40
|
+
// If the output type is Object we should not go through AnyValue.
|
|
41
|
+
if let DataType::Object(_) = output_type.0 {
|
|
42
|
+
let objects = lambda_result_iter
|
|
43
|
+
.map(|res| {
|
|
44
|
+
Ok(ObjectValue {
|
|
45
|
+
inner: Opaque::from(res?),
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
.collect::<RbResult<Vec<_>>>()?;
|
|
49
|
+
let s = series_from_objects(rb, PlSmallStr::from_static("map"), objects);
|
|
50
|
+
return Ok((RbSeries::from(s).into_value_with(rb), false));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let avs = lambda_result_iter
|
|
54
|
+
.map(|res| Wrap::<AnyValue>::try_convert(res?).map(|w| w.0))
|
|
55
|
+
.collect::<RbResult<Vec<AnyValue>>>()?;
|
|
56
|
+
let s = Series::from_any_values_and_dtype(
|
|
57
|
+
PlSmallStr::from_static("map"),
|
|
58
|
+
&avs,
|
|
59
|
+
&output_type.0,
|
|
60
|
+
true,
|
|
61
|
+
)
|
|
62
|
+
.map_err(RbPolarsErr::from)?;
|
|
63
|
+
return Ok((RbSeries::from(s).into_value_with(rb), false));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Disambiguate returning a DataFrame vs Series by checking the
|
|
67
|
+
// first non-null output value.
|
|
68
|
+
let mut peek_iter = lambda_result_iter.peekable();
|
|
69
|
+
let mut null_count = 0;
|
|
70
|
+
while let Some(ret) = peek_iter.peek() {
|
|
71
|
+
if let Ok(v) = ret
|
|
72
|
+
&& v.is_nil()
|
|
73
|
+
{
|
|
74
|
+
null_count += 1;
|
|
75
|
+
peek_iter.next();
|
|
76
|
+
} else {
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let first_val = match peek_iter.peek() {
|
|
82
|
+
Some(Ok(v)) => v,
|
|
83
|
+
Some(Err(e)) => return Err(e.clone()),
|
|
84
|
+
None => {
|
|
85
|
+
let msg = "The output type of the 'map_rows' function cannot be determined.\n\
|
|
86
|
+
All returned values are None, consider setting the 'return_dtype'.";
|
|
87
|
+
raise_err!(msg, ComputeError)
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if let Ok(first_row) = RArray::try_convert(*first_val) {
|
|
92
|
+
let width = first_row.len();
|
|
93
|
+
let out_df = collect_lambda_ret_with_rows_output(
|
|
94
|
+
height,
|
|
95
|
+
width,
|
|
96
|
+
null_count,
|
|
97
|
+
inference_size,
|
|
98
|
+
peek_iter,
|
|
99
|
+
)
|
|
100
|
+
.map_err(RbPolarsErr::from)?;
|
|
101
|
+
Ok((RbDataFrame::from(out_df).into_value_with(rb), true))
|
|
102
|
+
} else {
|
|
103
|
+
let avs = peek_iter
|
|
104
|
+
.map(|res| Wrap::<AnyValue>::try_convert(res?).map(|w| w.0))
|
|
105
|
+
.collect::<RbResult<Vec<AnyValue>>>()?;
|
|
106
|
+
let s = Series::from_any_values(PlSmallStr::from_static("map"), &avs, true)
|
|
107
|
+
.map_err(RbPolarsErr::from)?;
|
|
108
|
+
|
|
109
|
+
let out = if null_count > 0 {
|
|
110
|
+
let mut tmp = Series::full_null(s.name().clone(), null_count, s.dtype());
|
|
111
|
+
tmp.append_owned(s).map_err(RbPolarsErr::from)?;
|
|
112
|
+
tmp
|
|
113
|
+
} else {
|
|
114
|
+
s
|
|
115
|
+
};
|
|
116
|
+
Ok((RbSeries::from(out).into_value_with(rb), false))
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fn collect_lambda_ret_with_rows_output(
|
|
122
|
+
height: usize,
|
|
123
|
+
width: usize,
|
|
124
|
+
init_null_count: usize,
|
|
125
|
+
inference_size: usize,
|
|
126
|
+
ret_iter: impl Iterator<Item = RbResult<Value>>,
|
|
127
|
+
) -> PolarsResult<DataFrame> {
|
|
128
|
+
let null_row = Row::new(vec![AnyValue::Null; width]);
|
|
129
|
+
|
|
130
|
+
let mut row_buf = Row::default();
|
|
131
|
+
let mut row_iter = ret_iter.map(|retval| {
|
|
132
|
+
let retval = retval.map_err(to_pl_err)?;
|
|
133
|
+
if retval.is_nil() {
|
|
134
|
+
Ok(&null_row)
|
|
135
|
+
} else {
|
|
136
|
+
let tuple = RArray::try_convert(retval).map_err(|_| polars_err!(ComputeError: format!("expected tuple, got {}", unsafe { retval.classname() })))?;
|
|
137
|
+
row_buf.0.clear();
|
|
138
|
+
for v in tuple {
|
|
139
|
+
let v = Wrap::<AnyValue>::try_convert(v).unwrap().0;
|
|
140
|
+
row_buf.0.push(v);
|
|
141
|
+
}
|
|
142
|
+
let ptr = &row_buf as *const Row;
|
|
143
|
+
// SAFETY:
|
|
144
|
+
// we know that row constructor of polars dataframe does not keep a reference
|
|
145
|
+
// to the row. Before we mutate the row buf again, the reference is dropped.
|
|
146
|
+
// we only cannot prove it to the compiler.
|
|
147
|
+
// we still to this because it save a Vec allocation in a hot loop.
|
|
148
|
+
Ok(unsafe { &*ptr })
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// First rows for schema inference.
|
|
153
|
+
let mut buf = Vec::with_capacity(inference_size);
|
|
154
|
+
for v in (&mut row_iter).take(inference_size) {
|
|
155
|
+
buf.push(v?.clone());
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let schema = rows_to_schema_first_non_null(&buf, Some(50))?;
|
|
159
|
+
|
|
160
|
+
if init_null_count > 0 {
|
|
161
|
+
// SAFETY: we know the iterators size.
|
|
162
|
+
let iter = unsafe {
|
|
163
|
+
(0..init_null_count)
|
|
164
|
+
.map(|_| Ok(&null_row))
|
|
165
|
+
.chain(buf.iter().map(Ok))
|
|
166
|
+
.chain(row_iter)
|
|
167
|
+
.trust_my_length(height)
|
|
168
|
+
};
|
|
169
|
+
DataFrame::try_from_rows_iter_and_schema(iter, &schema)
|
|
170
|
+
} else {
|
|
171
|
+
// SAFETY: we know the iterators size.
|
|
172
|
+
let iter = unsafe { buf.iter().map(Ok).chain(row_iter).trust_my_length(height) };
|
|
173
|
+
DataFrame::try_from_rows_iter_and_schema(iter, &schema)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -2,26 +2,58 @@ mod construction;
|
|
|
2
2
|
mod export;
|
|
3
3
|
mod general;
|
|
4
4
|
mod io;
|
|
5
|
+
mod map;
|
|
5
6
|
mod serde;
|
|
6
7
|
|
|
8
|
+
use magnus::{DataTypeFunctions, TypedData, gc};
|
|
9
|
+
use parking_lot::RwLock;
|
|
7
10
|
use polars::prelude::*;
|
|
8
|
-
use std::cell::RefCell;
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
use crate::series::mark_series;
|
|
13
|
+
|
|
14
|
+
#[derive(TypedData)]
|
|
15
|
+
#[magnus(class = "Polars::RbDataFrame", mark)]
|
|
11
16
|
pub struct RbDataFrame {
|
|
12
|
-
pub df:
|
|
17
|
+
pub df: RwLock<DataFrame>,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
impl Clone for RbDataFrame {
|
|
21
|
+
fn clone(&self) -> Self {
|
|
22
|
+
RbDataFrame {
|
|
23
|
+
df: RwLock::new(self.df.read().clone()),
|
|
24
|
+
}
|
|
25
|
+
}
|
|
13
26
|
}
|
|
14
27
|
|
|
15
28
|
impl From<DataFrame> for RbDataFrame {
|
|
16
29
|
fn from(df: DataFrame) -> Self {
|
|
17
|
-
|
|
30
|
+
Self::new(df)
|
|
18
31
|
}
|
|
19
32
|
}
|
|
20
33
|
|
|
21
34
|
impl RbDataFrame {
|
|
22
35
|
pub fn new(df: DataFrame) -> Self {
|
|
23
36
|
RbDataFrame {
|
|
24
|
-
df:
|
|
37
|
+
df: RwLock::new(df),
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
impl DataTypeFunctions for RbDataFrame {
|
|
43
|
+
fn mark(&self, marker: &gc::Marker) {
|
|
44
|
+
// this is really, really not ideal, as objects will not be marked if unable to borrow
|
|
45
|
+
// currently, this should only happen for write_* methods,
|
|
46
|
+
// which should refuse to write Object datatype, and therefore be safe,
|
|
47
|
+
// since GC will not have a chance to run
|
|
48
|
+
if let Some(df) = self.df.try_read() {
|
|
49
|
+
for column in df.columns() {
|
|
50
|
+
if let DataType::Object(_) = column.dtype() {
|
|
51
|
+
match column {
|
|
52
|
+
Column::Series(s) => mark_series(marker, s),
|
|
53
|
+
Column::Scalar(s) => mark_series(marker, &s.as_single_value_series()),
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
25
57
|
}
|
|
26
58
|
}
|
|
27
59
|
}
|
|
@@ -1,15 +1,30 @@
|
|
|
1
|
-
use
|
|
1
|
+
use std::io::{BufReader, BufWriter};
|
|
2
|
+
|
|
3
|
+
use polars::prelude::*;
|
|
4
|
+
|
|
2
5
|
use crate::file::get_file_like;
|
|
3
|
-
use crate::
|
|
6
|
+
use crate::utils::to_rb_err;
|
|
7
|
+
use crate::{RbDataFrame, RbPolarsErr, RbResult};
|
|
4
8
|
use magnus::Value;
|
|
5
|
-
use std::io::BufWriter;
|
|
6
9
|
|
|
7
10
|
impl RbDataFrame {
|
|
8
|
-
|
|
9
|
-
pub fn serialize_json(&self, rb_f: Value) -> RbResult<()> {
|
|
11
|
+
pub fn serialize_binary(&self, rb_f: Value) -> RbResult<()> {
|
|
10
12
|
let file = get_file_like(rb_f, true)?;
|
|
11
|
-
let writer = BufWriter::new(file);
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
let mut writer = BufWriter::new(file);
|
|
14
|
+
|
|
15
|
+
Ok(self
|
|
16
|
+
.df
|
|
17
|
+
.write()
|
|
18
|
+
.serialize_into_writer(&mut writer)
|
|
19
|
+
.map_err(RbPolarsErr::from)?)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pub fn deserialize_binary(rb_f: Value) -> RbResult<Self> {
|
|
23
|
+
let file = get_file_like(rb_f, false)?;
|
|
24
|
+
let mut file = BufReader::new(file);
|
|
25
|
+
|
|
26
|
+
DataFrame::deserialize_from_reader(&mut file)
|
|
27
|
+
.map(|v| v.into())
|
|
28
|
+
.map_err(to_rb_err)
|
|
14
29
|
}
|
|
15
30
|
}
|
data/ext/polars/src/error.rs
CHANGED
|
@@ -3,11 +3,17 @@ use std::fmt::{Debug, Formatter};
|
|
|
3
3
|
use magnus::Error;
|
|
4
4
|
use polars::prelude::PolarsError;
|
|
5
5
|
|
|
6
|
-
use crate::
|
|
7
|
-
use crate::
|
|
6
|
+
use crate::RbErr;
|
|
7
|
+
use crate::exceptions::{
|
|
8
|
+
AssertionError, ColumnNotFoundError, ComputeError, DuplicateError, InvalidOperationError,
|
|
9
|
+
NoDataError, OutOfBoundsError, SQLInterfaceError, SQLSyntaxError, SchemaError,
|
|
10
|
+
SchemaFieldNotFoundError, ShapeError, StringCacheMismatchError, StructFieldNotFoundError,
|
|
11
|
+
};
|
|
12
|
+
use crate::ruby::exceptions::{RbIOError, RbRuntimeError};
|
|
8
13
|
|
|
9
14
|
pub enum RbPolarsErr {
|
|
10
15
|
Polars(PolarsError),
|
|
16
|
+
Ruby(RbErr),
|
|
11
17
|
Other(String),
|
|
12
18
|
}
|
|
13
19
|
|
|
@@ -17,9 +23,9 @@ impl From<PolarsError> for RbPolarsErr {
|
|
|
17
23
|
}
|
|
18
24
|
}
|
|
19
25
|
|
|
20
|
-
impl From<
|
|
21
|
-
fn from(
|
|
22
|
-
RbPolarsErr::
|
|
26
|
+
impl From<RbErr> for RbPolarsErr {
|
|
27
|
+
fn from(err: RbErr) -> Self {
|
|
28
|
+
RbPolarsErr::Ruby(err)
|
|
23
29
|
}
|
|
24
30
|
}
|
|
25
31
|
|
|
@@ -27,13 +33,43 @@ impl From<RbPolarsErr> for Error {
|
|
|
27
33
|
fn from(err: RbPolarsErr) -> Self {
|
|
28
34
|
match err {
|
|
29
35
|
RbPolarsErr::Polars(err) => match err {
|
|
36
|
+
PolarsError::AssertionError(err) => AssertionError::new_err(err.to_string()),
|
|
37
|
+
PolarsError::ColumnNotFound(name) => ColumnNotFoundError::new_err(name.to_string()),
|
|
30
38
|
PolarsError::ComputeError(err) => ComputeError::new_err(err.to_string()),
|
|
39
|
+
PolarsError::Duplicate(err) => DuplicateError::new_err(err.to_string()),
|
|
31
40
|
PolarsError::InvalidOperation(err) => {
|
|
32
41
|
InvalidOperationError::new_err(err.to_string())
|
|
33
42
|
}
|
|
34
|
-
|
|
43
|
+
PolarsError::IO { error, msg } => {
|
|
44
|
+
let msg = if let Some(msg) = msg {
|
|
45
|
+
msg.to_string()
|
|
46
|
+
} else {
|
|
47
|
+
error.to_string()
|
|
48
|
+
};
|
|
49
|
+
RbIOError::new_err(msg)
|
|
50
|
+
}
|
|
51
|
+
PolarsError::NoData(err) => NoDataError::new_err(err.to_string()),
|
|
52
|
+
PolarsError::OutOfBounds(err) => OutOfBoundsError::new_err(err.to_string()),
|
|
53
|
+
PolarsError::SQLInterface(name) => SQLInterfaceError::new_err(name.to_string()),
|
|
54
|
+
PolarsError::SQLSyntax(name) => SQLSyntaxError::new_err(name.to_string()),
|
|
55
|
+
PolarsError::SchemaFieldNotFound(name) => {
|
|
56
|
+
SchemaFieldNotFoundError::new_err(name.to_string())
|
|
57
|
+
}
|
|
58
|
+
PolarsError::SchemaMismatch(err) => SchemaError::new_err(err.to_string()),
|
|
59
|
+
PolarsError::ShapeMismatch(err) => ShapeError::new_err(err.to_string()),
|
|
60
|
+
PolarsError::StringCacheMismatch(err) => {
|
|
61
|
+
StringCacheMismatchError::new_err(err.to_string())
|
|
62
|
+
}
|
|
63
|
+
PolarsError::StructFieldNotFound(name) => {
|
|
64
|
+
StructFieldNotFoundError::new_err(name.to_string())
|
|
65
|
+
}
|
|
66
|
+
PolarsError::Context { .. } | PolarsError::ExprContext { .. } => {
|
|
67
|
+
let tmp = RbPolarsErr::Polars(err.context_trace());
|
|
68
|
+
RbErr::from(tmp)
|
|
69
|
+
}
|
|
35
70
|
},
|
|
36
|
-
RbPolarsErr::
|
|
71
|
+
RbPolarsErr::Ruby(err) => err,
|
|
72
|
+
err => RbRuntimeError::new_err(format!("{:?}", &err)),
|
|
37
73
|
}
|
|
38
74
|
}
|
|
39
75
|
}
|
|
@@ -43,6 +79,7 @@ impl Debug for RbPolarsErr {
|
|
|
43
79
|
use RbPolarsErr::*;
|
|
44
80
|
match self {
|
|
45
81
|
Polars(err) => write!(f, "{err:?}"),
|
|
82
|
+
Ruby(err) => write!(f, "{err:?}"),
|
|
46
83
|
Other(err) => write!(f, "BindingsError: {err:?}"),
|
|
47
84
|
}
|
|
48
85
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
use crate::rb_modules;
|
|
2
|
-
use magnus::{
|
|
2
|
+
use magnus::{Error, Module, Ruby};
|
|
3
3
|
use std::borrow::Cow;
|
|
4
4
|
|
|
5
5
|
macro_rules! create_exception {
|
|
6
|
-
($type:ident
|
|
6
|
+
($type:ident) => {
|
|
7
7
|
pub struct $type {}
|
|
8
8
|
|
|
9
9
|
impl $type {
|
|
@@ -11,14 +11,27 @@ macro_rules! create_exception {
|
|
|
11
11
|
where
|
|
12
12
|
T: Into<Cow<'static, str>>,
|
|
13
13
|
{
|
|
14
|
-
|
|
14
|
+
let ruby = Ruby::get().unwrap();
|
|
15
|
+
let cls = rb_modules::polars(&ruby)
|
|
16
|
+
.const_get(stringify!($type))
|
|
17
|
+
.unwrap();
|
|
18
|
+
Error::new(cls, message)
|
|
15
19
|
}
|
|
16
20
|
}
|
|
17
21
|
};
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
create_exception!(
|
|
21
|
-
create_exception!(
|
|
22
|
-
create_exception!(
|
|
23
|
-
create_exception!(
|
|
24
|
-
create_exception!(InvalidOperationError
|
|
24
|
+
create_exception!(AssertionError);
|
|
25
|
+
create_exception!(ColumnNotFoundError);
|
|
26
|
+
create_exception!(ComputeError);
|
|
27
|
+
create_exception!(DuplicateError);
|
|
28
|
+
create_exception!(InvalidOperationError);
|
|
29
|
+
create_exception!(NoDataError);
|
|
30
|
+
create_exception!(OutOfBoundsError);
|
|
31
|
+
create_exception!(SQLInterfaceError);
|
|
32
|
+
create_exception!(SQLSyntaxError);
|
|
33
|
+
create_exception!(SchemaError);
|
|
34
|
+
create_exception!(SchemaFieldNotFoundError);
|
|
35
|
+
create_exception!(ShapeError);
|
|
36
|
+
create_exception!(StringCacheMismatchError);
|
|
37
|
+
create_exception!(StructFieldNotFoundError);
|