polars-df 0.10.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/Cargo.lock +392 -351
  4. data/README.md +6 -6
  5. data/ext/polars/Cargo.toml +12 -7
  6. data/ext/polars/src/batched_csv.rs +53 -52
  7. data/ext/polars/src/conversion/any_value.rs +261 -0
  8. data/ext/polars/src/conversion/chunked_array.rs +4 -4
  9. data/ext/polars/src/conversion/mod.rs +60 -66
  10. data/ext/polars/src/dataframe/construction.rs +184 -0
  11. data/ext/polars/src/dataframe/export.rs +48 -0
  12. data/ext/polars/src/dataframe/general.rs +597 -0
  13. data/ext/polars/src/dataframe/io.rs +473 -0
  14. data/ext/polars/src/dataframe/mod.rs +26 -0
  15. data/ext/polars/src/error.rs +26 -4
  16. data/ext/polars/src/expr/categorical.rs +0 -10
  17. data/ext/polars/src/expr/datetime.rs +4 -8
  18. data/ext/polars/src/expr/general.rs +129 -94
  19. data/ext/polars/src/expr/mod.rs +2 -2
  20. data/ext/polars/src/expr/rolling.rs +201 -77
  21. data/ext/polars/src/expr/string.rs +11 -36
  22. data/ext/polars/src/functions/eager.rs +10 -10
  23. data/ext/polars/src/functions/lazy.rs +23 -21
  24. data/ext/polars/src/functions/range.rs +69 -1
  25. data/ext/polars/src/interop/mod.rs +1 -0
  26. data/ext/polars/src/interop/numo/mod.rs +2 -0
  27. data/ext/polars/src/interop/numo/to_numo_df.rs +23 -0
  28. data/ext/polars/src/interop/numo/to_numo_series.rs +61 -0
  29. data/ext/polars/src/lazyframe/mod.rs +135 -136
  30. data/ext/polars/src/lib.rs +94 -59
  31. data/ext/polars/src/map/dataframe.rs +2 -2
  32. data/ext/polars/src/map/lazy.rs +5 -25
  33. data/ext/polars/src/map/series.rs +7 -1
  34. data/ext/polars/src/rb_modules.rs +25 -1
  35. data/ext/polars/src/series/aggregation.rs +49 -30
  36. data/ext/polars/src/series/arithmetic.rs +21 -11
  37. data/ext/polars/src/series/construction.rs +56 -38
  38. data/ext/polars/src/series/export.rs +131 -49
  39. data/ext/polars/src/series/mod.rs +32 -141
  40. data/ext/polars/src/sql.rs +3 -1
  41. data/lib/polars/array_expr.rb +4 -4
  42. data/lib/polars/batched_csv_reader.rb +11 -5
  43. data/lib/polars/cat_expr.rb +0 -36
  44. data/lib/polars/cat_name_space.rb +0 -37
  45. data/lib/polars/convert.rb +6 -1
  46. data/lib/polars/data_frame.rb +176 -403
  47. data/lib/polars/data_types.rb +1 -1
  48. data/lib/polars/date_time_expr.rb +525 -572
  49. data/lib/polars/date_time_name_space.rb +263 -460
  50. data/lib/polars/dynamic_group_by.rb +5 -5
  51. data/lib/polars/exceptions.rb +7 -0
  52. data/lib/polars/expr.rb +1394 -243
  53. data/lib/polars/expr_dispatch.rb +1 -1
  54. data/lib/polars/functions/aggregation/horizontal.rb +8 -8
  55. data/lib/polars/functions/as_datatype.rb +63 -40
  56. data/lib/polars/functions/lazy.rb +63 -14
  57. data/lib/polars/functions/lit.rb +1 -1
  58. data/lib/polars/functions/range/date_range.rb +90 -57
  59. data/lib/polars/functions/range/datetime_range.rb +149 -0
  60. data/lib/polars/functions/range/int_range.rb +2 -2
  61. data/lib/polars/functions/range/time_range.rb +141 -0
  62. data/lib/polars/functions/repeat.rb +1 -1
  63. data/lib/polars/functions/whenthen.rb +1 -1
  64. data/lib/polars/group_by.rb +88 -23
  65. data/lib/polars/io/avro.rb +24 -0
  66. data/lib/polars/{io.rb → io/csv.rb} +299 -493
  67. data/lib/polars/io/database.rb +73 -0
  68. data/lib/polars/io/ipc.rb +247 -0
  69. data/lib/polars/io/json.rb +29 -0
  70. data/lib/polars/io/ndjson.rb +80 -0
  71. data/lib/polars/io/parquet.rb +227 -0
  72. data/lib/polars/lazy_frame.rb +143 -272
  73. data/lib/polars/lazy_group_by.rb +100 -3
  74. data/lib/polars/list_expr.rb +11 -11
  75. data/lib/polars/list_name_space.rb +5 -1
  76. data/lib/polars/rolling_group_by.rb +7 -9
  77. data/lib/polars/series.rb +103 -187
  78. data/lib/polars/string_expr.rb +78 -102
  79. data/lib/polars/string_name_space.rb +5 -4
  80. data/lib/polars/testing.rb +2 -2
  81. data/lib/polars/utils/constants.rb +9 -0
  82. data/lib/polars/utils/convert.rb +97 -0
  83. data/lib/polars/utils/parse.rb +89 -0
  84. data/lib/polars/utils/various.rb +76 -0
  85. data/lib/polars/utils/wrap.rb +19 -0
  86. data/lib/polars/utils.rb +8 -300
  87. data/lib/polars/version.rb +1 -1
  88. data/lib/polars/whenthen.rb +6 -6
  89. data/lib/polars.rb +20 -1
  90. metadata +28 -7
  91. data/ext/polars/src/conversion/anyvalue.rs +0 -186
  92. data/ext/polars/src/dataframe.rs +0 -1208
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # Polars Ruby
1
+ # Ruby Polars
2
2
 
3
3
  :fire: Blazingly fast DataFrames for Ruby, powered by [Polars](https://github.com/pola-rs/polars)
4
4
 
5
- [![Build Status](https://github.com/ankane/polars-ruby/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/polars-ruby/actions)
5
+ [![Build Status](https://github.com/ankane/ruby-polars/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/ruby-polars/actions)
6
6
 
7
7
  ## Installation
8
8
 
@@ -420,16 +420,16 @@ View the [changelog](CHANGELOG.md)
420
420
 
421
421
  Everyone is encouraged to help improve this project. Here are a few ways you can help:
422
422
 
423
- - [Report bugs](https://github.com/ankane/polars-ruby/issues)
424
- - Fix bugs and [submit pull requests](https://github.com/ankane/polars-ruby/pulls)
423
+ - [Report bugs](https://github.com/ankane/ruby-polars/issues)
424
+ - Fix bugs and [submit pull requests](https://github.com/ankane/ruby-polars/pulls)
425
425
  - Write, clarify, or fix documentation
426
426
  - Suggest or add new features
427
427
 
428
428
  To get started with development:
429
429
 
430
430
  ```sh
431
- git clone https://github.com/ankane/polars-ruby.git
432
- cd polars-ruby
431
+ git clone https://github.com/ankane/ruby-polars.git
432
+ cd ruby-polars
433
433
  bundle install
434
434
  bundle exec rake compile
435
435
  bundle exec rake test
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "polars"
3
- version = "0.10.0"
3
+ version = "0.12.0"
4
4
  license = "MIT"
5
5
  authors = ["Andrew Kane <andrew@ankane.org>"]
6
6
  edition = "2021"
@@ -14,15 +14,15 @@ crate-type = ["cdylib"]
14
14
  ahash = "0.8"
15
15
  chrono = "0.4"
16
16
  either = "1.8"
17
- magnus = "0.6"
18
- polars-core = "=0.39.2"
19
- polars-parquet = "=0.39.2"
20
- polars-utils = "=0.39.2"
17
+ magnus = "0.7"
18
+ polars-core = "=0.41.3"
19
+ polars-parquet = "=0.41.3"
20
+ polars-utils = "=0.41.3"
21
21
  serde_json = "1"
22
22
  smartstring = "1"
23
23
 
24
24
  [dependencies.polars]
25
- version = "=0.39.2"
25
+ version = "=0.41.3"
26
26
  features = [
27
27
  "abs",
28
28
  "approx_unique",
@@ -41,7 +41,6 @@ features = [
41
41
  "cumulative_eval",
42
42
  "cutqcut",
43
43
  "dataframe_arithmetic",
44
- "date_offset",
45
44
  "diagonal_concat",
46
45
  "diff",
47
46
  "dot_product",
@@ -55,6 +54,8 @@ features = [
55
54
  "fmt",
56
55
  "interpolate",
57
56
  "ipc",
57
+ "ipc_streaming",
58
+ "is_between",
58
59
  "is_first_distinct",
59
60
  "is_in",
60
61
  "is_last_distinct",
@@ -73,6 +74,9 @@ features = [
73
74
  "meta",
74
75
  "mode",
75
76
  "moment",
77
+ "month_start",
78
+ "month_end",
79
+ "offset_by",
76
80
  "object",
77
81
  "parquet",
78
82
  "partition_by",
@@ -91,6 +95,7 @@ features = [
91
95
  "replace",
92
96
  "rle",
93
97
  "rolling_window",
98
+ "rolling_window_by",
94
99
  "round_series",
95
100
  "row_hash",
96
101
  "search_sorted",
@@ -1,23 +1,19 @@
1
+ use std::cell::RefCell;
2
+ use std::path::PathBuf;
3
+ use std::sync::Mutex;
4
+
1
5
  use magnus::{prelude::*, RArray, Value};
6
+ use polars::io::csv::read::OwnedBatchedCsvReader;
2
7
  use polars::io::mmap::MmapBytesReader;
3
8
  use polars::io::RowIndex;
4
- use polars::prelude::read_impl::OwnedBatchedCsvReader;
5
9
  use polars::prelude::*;
6
- use std::cell::RefCell;
7
- use std::path::PathBuf;
8
10
 
9
11
  use crate::conversion::*;
10
- use crate::prelude::read_impl::OwnedBatchedCsvReaderMmap;
11
12
  use crate::{RbDataFrame, RbPolarsErr, RbResult};
12
13
 
13
- pub enum BatchedReader {
14
- MMap(OwnedBatchedCsvReaderMmap),
15
- Read(OwnedBatchedCsvReader),
16
- }
17
-
18
14
  #[magnus::wrap(class = "Polars::RbBatchedCsv")]
19
15
  pub struct RbBatchedCsv {
20
- pub reader: RefCell<BatchedReader>,
16
+ pub reader: RefCell<Mutex<OwnedBatchedCsvReader>>,
21
17
  }
22
18
 
23
19
  impl RbBatchedCsv {
@@ -44,19 +40,23 @@ impl RbBatchedCsv {
44
40
  let comment_prefix = Option::<String>::try_convert(arguments[16])?;
45
41
  let quote_char = Option::<String>::try_convert(arguments[17])?;
46
42
  let null_values = Option::<Wrap<NullValues>>::try_convert(arguments[18])?;
47
- let try_parse_dates = bool::try_convert(arguments[19])?;
48
- let skip_rows_after_header = usize::try_convert(arguments[20])?;
49
- let row_index = Option::<(String, IdxSize)>::try_convert(arguments[21])?;
50
- let sample_size = usize::try_convert(arguments[22])?;
51
- let eol_char = String::try_convert(arguments[23])?;
52
- let truncate_ragged_lines = bool::try_convert(arguments[24])?;
43
+ let missing_utf8_is_empty_string = bool::try_convert(arguments[19])?;
44
+ let try_parse_dates = bool::try_convert(arguments[20])?;
45
+ let skip_rows_after_header = usize::try_convert(arguments[21])?;
46
+ let row_index = Option::<(String, IdxSize)>::try_convert(arguments[22])?;
47
+ let sample_size = usize::try_convert(arguments[23])?;
48
+ let eol_char = String::try_convert(arguments[24])?;
49
+ let raise_if_empty = bool::try_convert(arguments[25])?;
50
+ let truncate_ragged_lines = bool::try_convert(arguments[26])?;
51
+ let decimal_comma = bool::try_convert(arguments[27])?;
53
52
  // end arguments
54
53
 
55
54
  let null_values = null_values.map(|w| w.0);
56
55
  let eol_char = eol_char.as_bytes()[0];
57
-
58
- let row_index = row_index.map(|(name, offset)| RowIndex { name, offset });
59
-
56
+ let row_index = row_index.map(|(name, offset)| RowIndex {
57
+ name: Arc::from(name.as_str()),
58
+ offset,
59
+ });
60
60
  let quote_char = if let Some(s) = quote_char {
61
61
  if s.is_empty() {
62
62
  None
@@ -86,54 +86,55 @@ impl RbBatchedCsv {
86
86
 
87
87
  let file = std::fs::File::open(path).map_err(RbPolarsErr::io)?;
88
88
  let reader = Box::new(file) as Box<dyn MmapBytesReader>;
89
- let reader = CsvReader::new(reader)
90
- .infer_schema(infer_schema_length)
91
- .has_header(has_header)
89
+ let reader = CsvReadOptions::default()
90
+ .with_infer_schema_length(infer_schema_length)
91
+ .with_has_header(has_header)
92
92
  .with_n_rows(n_rows)
93
- .with_separator(separator.as_bytes()[0])
94
93
  .with_skip_rows(skip_rows)
95
94
  .with_ignore_errors(ignore_errors)
96
- .with_projection(projection)
95
+ .with_projection(projection.map(Arc::new))
97
96
  .with_rechunk(rechunk)
98
97
  .with_chunk_size(chunk_size)
99
- .with_encoding(encoding.0)
100
- .with_columns(columns)
98
+ .with_columns(columns.map(Arc::from))
101
99
  .with_n_threads(n_threads)
102
- .with_dtypes_slice(overwrite_dtype_slice.as_deref())
103
- .low_memory(low_memory)
104
- .with_comment_prefix(comment_prefix.as_deref())
105
- .with_null_values(null_values)
106
- .with_try_parse_dates(try_parse_dates)
107
- .with_quote_char(quote_char)
108
- .with_end_of_line_char(eol_char)
100
+ .with_dtype_overwrite(overwrite_dtype_slice.map(Arc::new))
101
+ .with_low_memory(low_memory)
109
102
  .with_skip_rows_after_header(skip_rows_after_header)
110
103
  .with_row_index(row_index)
111
- .sample_size(sample_size)
112
- .truncate_ragged_lines(truncate_ragged_lines);
104
+ .with_sample_size(sample_size)
105
+ .with_raise_if_empty(raise_if_empty)
106
+ .with_parse_options(
107
+ CsvParseOptions::default()
108
+ .with_separator(separator.as_bytes()[0])
109
+ .with_encoding(encoding.0)
110
+ .with_missing_is_null(!missing_utf8_is_empty_string)
111
+ .with_comment_prefix(comment_prefix.as_deref())
112
+ .with_null_values(null_values)
113
+ .with_try_parse_dates(try_parse_dates)
114
+ .with_quote_char(quote_char)
115
+ .with_eol_char(eol_char)
116
+ .with_truncate_ragged_lines(truncate_ragged_lines)
117
+ .with_decimal_comma(decimal_comma),
118
+ )
119
+ .into_reader_with_file_handle(reader);
113
120
 
114
- let reader = if low_memory {
115
- let reader = reader
116
- .batched_read(overwrite_dtype.map(Arc::new))
117
- .map_err(RbPolarsErr::from)?;
118
- BatchedReader::Read(reader)
119
- } else {
120
- let reader = reader
121
- .batched_mmap(overwrite_dtype.map(Arc::new))
122
- .map_err(RbPolarsErr::from)?;
123
- BatchedReader::MMap(reader)
124
- };
121
+ let reader = reader
122
+ .batched(overwrite_dtype.map(Arc::new))
123
+ .map_err(RbPolarsErr::from)?;
125
124
 
126
125
  Ok(RbBatchedCsv {
127
- reader: RefCell::new(reader),
126
+ reader: RefCell::new(Mutex::new(reader)),
128
127
  })
129
128
  }
130
129
 
131
130
  pub fn next_batches(&self, n: usize) -> RbResult<Option<RArray>> {
132
- let batches = match &mut *self.reader.borrow_mut() {
133
- BatchedReader::MMap(reader) => reader.next_batches(n),
134
- BatchedReader::Read(reader) => reader.next_batches(n),
135
- }
136
- .map_err(RbPolarsErr::from)?;
131
+ let reader = &self.reader;
132
+ let batches = reader
133
+ .borrow()
134
+ .lock()
135
+ .map_err(|e| RbPolarsErr::other(e.to_string()))?
136
+ .next_batches(n)
137
+ .map_err(RbPolarsErr::from)?;
137
138
 
138
139
  Ok(batches.map(|batches| RArray::from_iter(batches.into_iter().map(RbDataFrame::from))))
139
140
  }
@@ -0,0 +1,261 @@
1
+ use magnus::encoding::{EncodingCapable, Index};
2
+ use magnus::{
3
+ class, prelude::*, r_hash::ForEach, IntoValue, RArray, RHash, RString, Ruby, TryConvert, Value,
4
+ };
5
+ use polars::prelude::*;
6
+ use polars_core::utils::any_values_to_supertype_and_n_dtypes;
7
+
8
+ use super::{struct_dict, ObjectValue, Wrap};
9
+
10
+ use crate::error::RbOverflowError;
11
+ use crate::rb_modules::utils;
12
+ use crate::{RbPolarsErr, RbResult, RbSeries};
13
+
14
+ impl IntoValue for Wrap<AnyValue<'_>> {
15
+ fn into_value_with(self, ruby: &Ruby) -> Value {
16
+ any_value_into_rb_object(self.0, ruby)
17
+ }
18
+ }
19
+
20
+ impl<'s> TryConvert for Wrap<AnyValue<'s>> {
21
+ fn try_convert(ob: Value) -> RbResult<Self> {
22
+ rb_object_to_any_value(ob, true).map(Wrap)
23
+ }
24
+ }
25
+
26
+ pub(crate) fn any_value_into_rb_object(av: AnyValue, ruby: &Ruby) -> Value {
27
+ match av {
28
+ AnyValue::UInt8(v) => ruby.into_value(v),
29
+ AnyValue::UInt16(v) => ruby.into_value(v),
30
+ AnyValue::UInt32(v) => ruby.into_value(v),
31
+ AnyValue::UInt64(v) => ruby.into_value(v),
32
+ AnyValue::Int8(v) => ruby.into_value(v),
33
+ AnyValue::Int16(v) => ruby.into_value(v),
34
+ AnyValue::Int32(v) => ruby.into_value(v),
35
+ AnyValue::Int64(v) => ruby.into_value(v),
36
+ AnyValue::Float32(v) => ruby.into_value(v),
37
+ AnyValue::Float64(v) => ruby.into_value(v),
38
+ AnyValue::Null => ruby.qnil().as_value(),
39
+ AnyValue::Boolean(v) => ruby.into_value(v),
40
+ AnyValue::String(v) => ruby.into_value(v),
41
+ AnyValue::StringOwned(v) => ruby.into_value(v.as_str()),
42
+ AnyValue::Categorical(idx, rev, arr) | AnyValue::Enum(idx, rev, arr) => {
43
+ let s = if arr.is_null() {
44
+ rev.get(idx)
45
+ } else {
46
+ unsafe { arr.deref_unchecked().value(idx as usize) }
47
+ };
48
+ s.into_value()
49
+ }
50
+ AnyValue::Date(v) => utils().funcall("_to_ruby_date", (v,)).unwrap(),
51
+ AnyValue::Datetime(v, time_unit, time_zone) => {
52
+ let time_unit = time_unit.to_ascii();
53
+ utils()
54
+ .funcall("_to_ruby_datetime", (v, time_unit, time_zone.clone()))
55
+ .unwrap()
56
+ }
57
+ AnyValue::Duration(v, time_unit) => {
58
+ let time_unit = time_unit.to_ascii();
59
+ utils()
60
+ .funcall("_to_ruby_duration", (v, time_unit))
61
+ .unwrap()
62
+ }
63
+ AnyValue::Time(v) => utils().funcall("_to_ruby_time", (v,)).unwrap(),
64
+ AnyValue::Array(v, _) | AnyValue::List(v) => RbSeries::new(v).to_a().into_value(),
65
+ ref av @ AnyValue::Struct(_, _, flds) => struct_dict(av._iter_struct_av(), flds),
66
+ AnyValue::StructOwned(payload) => struct_dict(payload.0.into_iter(), &payload.1),
67
+ AnyValue::Object(v) => {
68
+ let object = v.as_any().downcast_ref::<ObjectValue>().unwrap();
69
+ object.to_object()
70
+ }
71
+ AnyValue::ObjectOwned(v) => {
72
+ let object = v.0.as_any().downcast_ref::<ObjectValue>().unwrap();
73
+ object.to_object()
74
+ }
75
+ AnyValue::Binary(v) => RString::from_slice(v).into_value(),
76
+ AnyValue::BinaryOwned(v) => RString::from_slice(&v).into_value(),
77
+ AnyValue::Decimal(v, scale) => utils()
78
+ .funcall("_to_ruby_decimal", (v.to_string(), -(scale as i32)))
79
+ .unwrap(),
80
+ }
81
+ }
82
+
83
+ pub(crate) fn rb_object_to_any_value<'s>(ob: Value, strict: bool) -> RbResult<AnyValue<'s>> {
84
+ // Conversion functions.
85
+ fn get_null(_ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
86
+ Ok(AnyValue::Null)
87
+ }
88
+
89
+ fn get_bool(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
90
+ let b = bool::try_convert(ob)?;
91
+ Ok(AnyValue::Boolean(b))
92
+ }
93
+
94
+ fn get_int(ob: Value, strict: bool) -> RbResult<AnyValue<'static>> {
95
+ if let Ok(v) = i64::try_convert(ob) {
96
+ Ok(AnyValue::Int64(v))
97
+ } else if let Ok(v) = u64::try_convert(ob) {
98
+ Ok(AnyValue::UInt64(v))
99
+ } else if !strict {
100
+ let f = f64::try_convert(ob)?;
101
+ Ok(AnyValue::Float64(f))
102
+ } else {
103
+ Err(RbOverflowError::new_err(format!(
104
+ "int value too large for Polars integer types: {ob}"
105
+ )))
106
+ }
107
+ }
108
+
109
+ fn get_float(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
110
+ Ok(AnyValue::Float64(f64::try_convert(ob)?))
111
+ }
112
+
113
+ fn get_str(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
114
+ let v = RString::from_value(ob).unwrap();
115
+ if v.enc_get() == Index::utf8() {
116
+ Ok(AnyValue::StringOwned(v.to_string()?.into()))
117
+ } else {
118
+ Ok(AnyValue::BinaryOwned(unsafe { v.as_slice() }.to_vec()))
119
+ }
120
+ }
121
+
122
+ fn get_list(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
123
+ let v = RArray::from_value(ob).unwrap();
124
+ if v.is_empty() {
125
+ Ok(AnyValue::List(Series::new_empty("", &DataType::Null)))
126
+ } else {
127
+ let list = v;
128
+
129
+ let mut avs = Vec::with_capacity(25);
130
+ let mut iter = list.into_iter();
131
+
132
+ for item in (&mut iter).take(25) {
133
+ avs.push(Wrap::<AnyValue>::try_convert(item)?.0)
134
+ }
135
+
136
+ let (dtype, _n_types) =
137
+ any_values_to_supertype_and_n_dtypes(&avs).map_err(RbPolarsErr::from)?;
138
+
139
+ // push the rest
140
+ avs.reserve(list.len());
141
+ for item in iter {
142
+ avs.push(Wrap::<AnyValue>::try_convert(item)?.0)
143
+ }
144
+
145
+ let s = Series::from_any_values_and_dtype("", &avs, &dtype, true)
146
+ .map_err(RbPolarsErr::from)?;
147
+ Ok(AnyValue::List(s))
148
+ }
149
+ }
150
+
151
+ fn get_list_from_series(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
152
+ let s = super::get_series(ob)?;
153
+ Ok(AnyValue::List(s))
154
+ }
155
+
156
+ fn get_struct(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
157
+ let dict = RHash::from_value(ob).unwrap();
158
+ let len = dict.len();
159
+ let mut keys = Vec::with_capacity(len);
160
+ let mut vals = Vec::with_capacity(len);
161
+ dict.foreach(|k: Value, v: Value| {
162
+ let key = String::try_convert(k)?;
163
+ let val = Wrap::<AnyValue>::try_convert(v)?.0;
164
+ let dtype = DataType::from(&val);
165
+ keys.push(Field::new(&key, dtype));
166
+ vals.push(val);
167
+ Ok(ForEach::Continue)
168
+ })?;
169
+ Ok(AnyValue::StructOwned(Box::new((vals, keys))))
170
+ }
171
+
172
+ fn get_date(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
173
+ // convert to DateTime for UTC
174
+ let v = ob
175
+ .funcall::<_, _, Value>("to_datetime", ())?
176
+ .funcall::<_, _, Value>("to_time", ())?
177
+ .funcall::<_, _, i64>("to_i", ())?;
178
+ Ok(AnyValue::Date((v / 86400) as i32))
179
+ }
180
+
181
+ fn get_time(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
182
+ let sec = ob.funcall::<_, _, i64>("to_i", ())?;
183
+ let nsec = ob.funcall::<_, _, i64>("nsec", ())?;
184
+ let v = sec * 1_000_000_000 + nsec;
185
+ // TODO support time zone when possible
186
+ // https://github.com/pola-rs/polars/issues/9103
187
+ Ok(AnyValue::Datetime(v, TimeUnit::Nanoseconds, &None))
188
+ }
189
+
190
+ fn get_datetime(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
191
+ let sec: i64 = ob.funcall("to_i", ())?;
192
+ let nsec: i64 = ob.funcall("nsec", ())?;
193
+ Ok(AnyValue::Datetime(
194
+ sec * 1_000_000_000 + nsec,
195
+ TimeUnit::Nanoseconds,
196
+ &None,
197
+ ))
198
+ }
199
+
200
+ fn get_decimal(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
201
+ fn abs_decimal_from_digits(digits: String, exp: i32) -> Option<(i128, usize)> {
202
+ let exp = exp - (digits.len() as i32);
203
+ match digits.parse::<i128>() {
204
+ Ok(mut v) => {
205
+ let scale = if exp > 0 {
206
+ v = 10_i128
207
+ .checked_pow(exp as u32)
208
+ .and_then(|factor| v.checked_mul(factor))?;
209
+ 0
210
+ } else {
211
+ (-exp) as usize
212
+ };
213
+ Some((v, scale))
214
+ }
215
+ Err(_) => None,
216
+ }
217
+ }
218
+
219
+ let (sign, digits, _, exp): (i8, String, i32, i32) = ob.funcall("split", ()).unwrap();
220
+ let (mut v, scale) = abs_decimal_from_digits(digits, exp).ok_or_else(|| {
221
+ RbPolarsErr::other("BigDecimal is too large to fit in Decimal128".into())
222
+ })?;
223
+ if sign < 0 {
224
+ // TODO better error
225
+ v = v.checked_neg().unwrap();
226
+ }
227
+ Ok(AnyValue::Decimal(v, scale))
228
+ }
229
+
230
+ if ob.is_nil() {
231
+ get_null(ob, strict)
232
+ } else if ob.is_kind_of(class::true_class()) || ob.is_kind_of(class::false_class()) {
233
+ get_bool(ob, strict)
234
+ } else if ob.is_kind_of(class::integer()) {
235
+ get_int(ob, strict)
236
+ } else if ob.is_kind_of(class::float()) {
237
+ get_float(ob, strict)
238
+ } else if ob.is_kind_of(class::string()) {
239
+ get_str(ob, strict)
240
+ } else if ob.is_kind_of(class::array()) {
241
+ get_list(ob, strict)
242
+ } else if ob.is_kind_of(class::hash()) {
243
+ get_struct(ob, strict)
244
+ } else if ob.respond_to("_s", true)? {
245
+ get_list_from_series(ob, strict)
246
+ // call is_a? for ActiveSupport::TimeWithZone
247
+ } else if ob.funcall::<_, _, bool>("is_a?", (class::time(),))? {
248
+ get_time(ob, strict)
249
+ } else if ob.is_kind_of(crate::rb_modules::datetime()) {
250
+ get_datetime(ob, strict)
251
+ } else if ob.is_kind_of(crate::rb_modules::date()) {
252
+ get_date(ob, strict)
253
+ } else if ob.is_kind_of(crate::rb_modules::bigdecimal()) {
254
+ get_decimal(ob, strict)
255
+ } else {
256
+ Err(RbPolarsErr::other(format!(
257
+ "object type not supported {:?}",
258
+ ob
259
+ )))
260
+ }
261
+ }
@@ -11,8 +11,8 @@ impl TryConvert for Wrap<StringChunked> {
11
11
  let (seq, len) = get_rbseq(obj)?;
12
12
  let mut builder = StringChunkedBuilder::new("", len);
13
13
 
14
- for res in seq.each() {
15
- let item = res?;
14
+ for res in seq.into_iter() {
15
+ let item = res;
16
16
  match String::try_convert(item) {
17
17
  Ok(val) => builder.append_value(&val),
18
18
  Err(_) => builder.append_null(),
@@ -27,8 +27,8 @@ impl TryConvert for Wrap<BinaryChunked> {
27
27
  let (seq, len) = get_rbseq(obj)?;
28
28
  let mut builder = BinaryChunkedBuilder::new("", len);
29
29
 
30
- for res in seq.each() {
31
- let item = res?;
30
+ for res in seq.into_iter() {
31
+ let item = res;
32
32
  match RString::try_convert(item) {
33
33
  Ok(val) => builder.append_value(unsafe { val.as_slice() }),
34
34
  Err(_) => builder.append_null(),