polars-df 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Cargo.toml CHANGED
@@ -3,8 +3,8 @@ members = ["ext/polars"]
3
3
 
4
4
  [patch.crates-io]
5
5
  jsonpath_lib = { git = "https://github.com/ritchie46/jsonpath", rev = "24eaf0b4416edff38a4d1b6b17bc4b9f3f047b4b" }
6
- magnus-macros = { git = "https://github.com/matsadler/magnus" }
7
- rb-sys-env = { git = "https://github.com/oxidize-rb/rb-sys" }
6
+ halfbrown = { git = "https://github.com/Licenser/halfbrown", rev = "952023c5dd6461b009bb5ba66b9aa979bd75949f" }
7
+ arrow2 = { git = "https://github.com/ankane/arrow2", rev = "9f36b2b97446e6dd495473e4361a70d863ac8027" }
8
8
 
9
9
  [profile.release]
10
10
  strip = true
data/LICENSE.txt CHANGED
@@ -1,5 +1,5 @@
1
1
  Copyright (c) 2020 Ritchie Vink
2
- Copyright (c) 2022 Andrew Kane
2
+ Copyright (c) 2022-2023 Andrew Kane
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -12,8 +12,6 @@ Add this line to your application’s Gemfile:
12
12
  gem "polars-df"
13
13
  ```
14
14
 
15
- Note: Rust is currently required for installation, and it can take 15-20 minutes to compile the extension.
16
-
17
15
  ## Getting Started
18
16
 
19
17
  This library follows the [Polars Python API](https://pola-rs.github.io/polars/py-polars/html/reference/index.html).
@@ -1,6 +1,7 @@
1
1
  [package]
2
2
  name = "polars"
3
- version = "0.1.5"
3
+ version = "0.2.0"
4
+ license = "MIT"
4
5
  authors = ["Andrew Kane <andrew@ankane.org>"]
5
6
  edition = "2021"
6
7
  publish = false
@@ -11,11 +12,11 @@ crate-type = ["cdylib"]
11
12
  [dependencies]
12
13
  ahash = "0.8"
13
14
  magnus = "0.4"
14
- polars-core = "0.25.1"
15
+ polars-core = "0.26.1"
15
16
  serde_json = "1"
16
17
 
17
18
  [dependencies.polars]
18
- version = "0.25.1"
19
+ version = "0.26.1"
19
20
  features = [
20
21
  "abs",
21
22
  "arange",
@@ -1,20 +1,32 @@
1
1
  use magnus::{class, RArray, TryConvert, Value};
2
2
  use polars::prelude::*;
3
3
  use polars_core::frame::row::{rows_to_schema_first_non_null, Row};
4
+ use polars_core::series::SeriesIter;
4
5
 
5
6
  use super::*;
6
7
  use crate::{RbDataFrame, RbPolarsErr, RbSeries, Wrap};
7
8
 
9
+ fn get_iters(df: &DataFrame) -> Vec<SeriesIter> {
10
+ df.get_columns().iter().map(|s| s.iter()).collect()
11
+ }
12
+
13
+ fn get_iters_skip(df: &DataFrame, skip: usize) -> Vec<std::iter::Skip<SeriesIter>> {
14
+ df.get_columns()
15
+ .iter()
16
+ .map(|s| s.iter().skip(skip))
17
+ .collect()
18
+ }
19
+
8
20
  pub fn apply_lambda_unknown<'a>(
9
21
  df: &'a DataFrame,
10
22
  lambda: Value,
11
23
  inference_size: usize,
12
24
  ) -> RbResult<(Value, bool)> {
13
- let columns = df.get_columns();
14
25
  let mut null_count = 0;
26
+ let mut iters = get_iters(df);
15
27
 
16
- for idx in 0..df.height() {
17
- let iter = columns.iter().map(|s: &Series| Wrap(s.get(idx)));
28
+ for _ in 0..df.height() {
29
+ let iter = iters.iter_mut().map(|it| Wrap(it.next().unwrap()));
18
30
  let arg = (iter.collect::<Vec<Wrap<AnyValue>>>(),);
19
31
  let out: Value = lambda.funcall("call", arg)?;
20
32
 
@@ -126,9 +138,9 @@ fn apply_iter<T>(
126
138
  where
127
139
  T: TryConvert,
128
140
  {
129
- let columns = df.get_columns();
130
- ((init_null_count + skip)..df.height()).map(move |idx| {
131
- let iter = columns.iter().map(|s: &Series| Wrap(s.get(idx)));
141
+ let mut iters = get_iters_skip(df, init_null_count + skip);
142
+ ((init_null_count + skip)..df.height()).map(move |_| {
143
+ let iter = iters.iter_mut().map(|it| Wrap(it.next().unwrap()));
132
144
  let tpl = (iter.collect::<Vec<Wrap<AnyValue>>>(),);
133
145
  match lambda.funcall::<_, _, Value>("call", tpl) {
134
146
  Ok(val) => val.try_convert::<T>().ok(),
@@ -197,14 +209,13 @@ pub fn apply_lambda_with_list_out_type<'a>(
197
209
  first_value: Option<&Series>,
198
210
  dt: &DataType,
199
211
  ) -> RbResult<ListChunked> {
200
- let columns = df.get_columns();
201
-
202
212
  let skip = usize::from(first_value.is_some());
203
213
  if init_null_count == df.height() {
204
214
  Ok(ChunkedArray::full_null("apply", df.height()))
205
215
  } else {
206
- let iter = ((init_null_count + skip)..df.height()).map(|idx| {
207
- let iter = columns.iter().map(|s: &Series| Wrap(s.get(idx)));
216
+ let mut iters = get_iters_skip(df, init_null_count + skip);
217
+ let iter = ((init_null_count + skip)..df.height()).map(|_| {
218
+ let iter = iters.iter_mut().map(|it| Wrap(it.next().unwrap()));
208
219
  let tpl = (iter.collect::<Vec<Wrap<AnyValue>>>(),);
209
220
  match lambda.funcall::<_, _, Value>("call", tpl) {
210
221
  Ok(val) => match val.funcall::<_, _, Value>("_s", ()) {
@@ -234,15 +245,15 @@ pub fn apply_lambda_with_rows_output<'a>(
234
245
  first_value: Row<'a>,
235
246
  inference_size: usize,
236
247
  ) -> PolarsResult<DataFrame> {
237
- let columns = df.get_columns();
238
248
  let width = first_value.0.len();
239
249
  let null_row = Row::new(vec![AnyValue::Null; width]);
240
250
 
241
251
  let mut row_buf = Row::default();
242
252
 
243
253
  let skip = 1;
244
- let mut row_iter = ((init_null_count + skip)..df.height()).map(|idx| {
245
- let iter = columns.iter().map(|s: &Series| Wrap(s.get(idx)));
254
+ let mut iters = get_iters_skip(df, init_null_count + skip);
255
+ let mut row_iter = ((init_null_count + skip)..df.height()).map(|_| {
256
+ let iter = iters.iter_mut().map(|it| Wrap(it.next().unwrap()));
246
257
  let tpl = (iter.collect::<Vec<Wrap<AnyValue>>>(),);
247
258
  match lambda.funcall::<_, _, Value>("call", tpl) {
248
259
  Ok(val) => {
@@ -31,12 +31,11 @@ fn iterator_to_struct(
31
31
  capacity: usize,
32
32
  ) -> RbResult<RbSeries> {
33
33
  let (vals, flds) = match &first_value {
34
- AnyValue::Struct(vals, flds) => (&**vals, *flds),
35
- AnyValue::StructOwned(payload) => (&*payload.0, &*payload.1),
34
+ av @ AnyValue::Struct(_, _, flds) => (av._iter_struct_av().collect::<Vec<_>>(), &**flds),
35
+ AnyValue::StructOwned(payload) => (payload.0.clone(), &*payload.1),
36
36
  _ => {
37
37
  return Err(crate::error::ComputeError::new_err(format!(
38
- "expected struct got {:?}",
39
- first_value
38
+ "expected struct got {first_value:?}",
40
39
  )))
41
40
  }
42
41
  };
@@ -1,9 +1,9 @@
1
- use magnus::{class, r_hash::ForEach, RArray, RHash, Symbol, TryConvert, Value, QNIL};
1
+ use magnus::{class, r_hash::ForEach, Module, RArray, RHash, Symbol, TryConvert, Value, QNIL};
2
2
  use polars::chunked_array::object::PolarsObjectSafe;
3
3
  use polars::chunked_array::ops::{FillNullLimit, FillNullStrategy};
4
4
  use polars::datatypes::AnyValue;
5
5
  use polars::frame::row::Row;
6
- use polars::frame::DataFrame;
6
+ use polars::frame::NullStrategy;
7
7
  use polars::io::avro::AvroCompression;
8
8
  use polars::prelude::*;
9
9
  use polars::series::ops::NullBehavior;
@@ -18,6 +18,12 @@ pub(crate) fn slice_to_wrapped<T>(slice: &[T]) -> &[Wrap<T>] {
18
18
  unsafe { std::mem::transmute(slice) }
19
19
  }
20
20
 
21
+ pub(crate) fn vec_extract_wrapped<T>(buf: Vec<Wrap<T>>) -> Vec<T> {
22
+ // Safety:
23
+ // Wrap is transparent.
24
+ unsafe { std::mem::transmute(buf) }
25
+ }
26
+
21
27
  #[repr(transparent)]
22
28
  pub struct Wrap<T>(pub T);
23
29
 
@@ -138,40 +144,127 @@ impl From<Wrap<AnyValue<'_>>> for Value {
138
144
 
139
145
  impl From<Wrap<DataType>> for Value {
140
146
  fn from(w: Wrap<DataType>) -> Self {
141
- Symbol::from(w.0.to_string()).into()
147
+ let pl = crate::module();
148
+
149
+ match &w.0 {
150
+ DataType::Int8 => pl.const_get::<_, Value>("Int8").unwrap(),
151
+ DataType::Int16 => pl.const_get::<_, Value>("Int16").unwrap(),
152
+ DataType::Int32 => pl.const_get::<_, Value>("Int32").unwrap(),
153
+ DataType::Int64 => pl.const_get::<_, Value>("Int64").unwrap(),
154
+ DataType::UInt8 => pl.const_get::<_, Value>("UInt8").unwrap(),
155
+ DataType::UInt16 => pl.const_get::<_, Value>("UInt16").unwrap(),
156
+ DataType::UInt32 => pl.const_get::<_, Value>("UInt32").unwrap(),
157
+ DataType::UInt64 => pl.const_get::<_, Value>("UInt64").unwrap(),
158
+ DataType::Float32 => pl.const_get::<_, Value>("Float32").unwrap(),
159
+ DataType::Float64 => pl.const_get::<_, Value>("Float64").unwrap(),
160
+ DataType::Boolean => pl.const_get::<_, Value>("Boolean").unwrap(),
161
+ DataType::Utf8 => pl.const_get::<_, Value>("Utf8").unwrap(),
162
+ DataType::Binary => pl.const_get::<_, Value>("Binary").unwrap(),
163
+ DataType::List(inner) => {
164
+ let inner = Wrap(*inner.clone());
165
+ let list_class = pl.const_get::<_, Value>("List").unwrap();
166
+ list_class.funcall::<_, _, Value>("new", (inner,)).unwrap()
167
+ }
168
+ DataType::Date => pl.const_get::<_, Value>("Date").unwrap(),
169
+ DataType::Datetime(tu, tz) => {
170
+ let datetime_class = pl.const_get::<_, Value>("Datetime").unwrap();
171
+ datetime_class
172
+ .funcall::<_, _, Value>("new", (tu.to_ascii(), tz.clone()))
173
+ .unwrap()
174
+ }
175
+ DataType::Duration(tu) => {
176
+ let duration_class = pl.const_get::<_, Value>("Duration").unwrap();
177
+ duration_class
178
+ .funcall::<_, _, Value>("new", (tu.to_ascii(),))
179
+ .unwrap()
180
+ }
181
+ DataType::Object(_) => pl.const_get::<_, Value>("Object").unwrap(),
182
+ DataType::Categorical(_) => pl.const_get::<_, Value>("Categorical").unwrap(),
183
+ DataType::Time => pl.const_get::<_, Value>("Time").unwrap(),
184
+ DataType::Struct(fields) => {
185
+ let field_class = pl.const_get::<_, Value>("Field").unwrap();
186
+ let iter = fields.iter().map(|fld| {
187
+ let name = fld.name().clone();
188
+ let dtype = Wrap(fld.data_type().clone());
189
+ field_class
190
+ .funcall::<_, _, Value>("new", (name, dtype))
191
+ .unwrap()
192
+ });
193
+ let fields = RArray::from_iter(iter);
194
+ let struct_class = pl.const_get::<_, Value>("Struct").unwrap();
195
+ struct_class
196
+ .funcall::<_, _, Value>("new", (fields,))
197
+ .unwrap()
198
+ }
199
+ DataType::Null => pl.const_get::<_, Value>("Null").unwrap(),
200
+ DataType::Unknown => pl.const_get::<_, Value>("Unknown").unwrap(),
201
+ }
142
202
  }
143
203
  }
144
204
 
145
205
  impl TryConvert for Wrap<DataType> {
146
206
  fn try_convert(ob: Value) -> RbResult<Self> {
147
- let dtype = match ob.try_convert::<String>()?.as_str() {
148
- "u8" => DataType::UInt8,
149
- "u16" => DataType::UInt16,
150
- "u32" => DataType::UInt32,
151
- "u64" => DataType::UInt64,
152
- "i8" => DataType::Int8,
153
- "i16" => DataType::Int16,
154
- "i32" => DataType::Int32,
155
- "i64" => DataType::Int64,
156
- "str" => DataType::Utf8,
157
- "bin" => DataType::Binary,
158
- "bool" => DataType::Boolean,
159
- "cat" => DataType::Categorical(None),
160
- "date" => DataType::Date,
161
- "datetime" => DataType::Datetime(TimeUnit::Microseconds, None),
162
- "f32" => DataType::Float32,
163
- "time" => DataType::Time,
164
- "dur" => DataType::Duration(TimeUnit::Microseconds),
165
- "f64" => DataType::Float64,
166
- // "obj" => DataType::Object(OBJECT_NAME),
167
- "list" => DataType::List(Box::new(DataType::Boolean)),
168
- "null" => DataType::Null,
169
- "unk" => DataType::Unknown,
170
- _ => {
171
- return Err(RbValueError::new_err(format!(
172
- "{} is not a supported DataType.",
173
- ob
174
- )))
207
+ let dtype = if ob.is_kind_of(class::class()) {
208
+ let name = ob.funcall::<_, _, String>("name", ())?;
209
+ match name.as_str() {
210
+ "Polars::UInt8" => DataType::UInt8,
211
+ "Polars::UInt16" => DataType::UInt16,
212
+ "Polars::UInt32" => DataType::UInt32,
213
+ "Polars::UInt64" => DataType::UInt64,
214
+ "Polars::Int8" => DataType::Int8,
215
+ "Polars::Int16" => DataType::Int16,
216
+ "Polars::Int32" => DataType::Int32,
217
+ "Polars::Int64" => DataType::Int64,
218
+ "Polars::Utf8" => DataType::Utf8,
219
+ "Polars::Binary" => DataType::Binary,
220
+ "Polars::Boolean" => DataType::Boolean,
221
+ "Polars::Categorical" => DataType::Categorical(None),
222
+ "Polars::Date" => DataType::Date,
223
+ "Polars::Datetime" => DataType::Datetime(TimeUnit::Microseconds, None),
224
+ "Polars::Time" => DataType::Time,
225
+ "Polars::Duration" => DataType::Duration(TimeUnit::Microseconds),
226
+ "Polars::Float32" => DataType::Float32,
227
+ "Polars::Float64" => DataType::Float64,
228
+ // "Polars::Object" => DataType::Object(OBJECT_NAME),
229
+ "Polars::List" => DataType::List(Box::new(DataType::Boolean)),
230
+ "Polars::Null" => DataType::Null,
231
+ "Polars::Unknown" => DataType::Unknown,
232
+ dt => {
233
+ return Err(RbValueError::new_err(format!(
234
+ "{dt} is not a correct polars DataType.",
235
+ )))
236
+ }
237
+ }
238
+ } else {
239
+ match ob.try_convert::<String>()?.as_str() {
240
+ "u8" => DataType::UInt8,
241
+ "u16" => DataType::UInt16,
242
+ "u32" => DataType::UInt32,
243
+ "u64" => DataType::UInt64,
244
+ "i8" => DataType::Int8,
245
+ "i16" => DataType::Int16,
246
+ "i32" => DataType::Int32,
247
+ "i64" => DataType::Int64,
248
+ "str" => DataType::Utf8,
249
+ "bin" => DataType::Binary,
250
+ "bool" => DataType::Boolean,
251
+ "cat" => DataType::Categorical(None),
252
+ "date" => DataType::Date,
253
+ "datetime" => DataType::Datetime(TimeUnit::Microseconds, None),
254
+ "f32" => DataType::Float32,
255
+ "time" => DataType::Time,
256
+ "dur" => DataType::Duration(TimeUnit::Microseconds),
257
+ "f64" => DataType::Float64,
258
+ // "obj" => DataType::Object(OBJECT_NAME),
259
+ "list" => DataType::List(Box::new(DataType::Boolean)),
260
+ "null" => DataType::Null,
261
+ "unk" => DataType::Unknown,
262
+ _ => {
263
+ return Err(RbValueError::new_err(format!(
264
+ "{} is not a supported DataType.",
265
+ ob
266
+ )))
267
+ }
175
268
  }
176
269
  };
177
270
  Ok(Wrap(dtype))
@@ -210,6 +303,21 @@ impl TryConvert for Wrap<AsofStrategy> {
210
303
  }
211
304
  }
212
305
 
306
+ impl TryConvert for Wrap<InterpolationMethod> {
307
+ fn try_convert(ob: Value) -> RbResult<Self> {
308
+ let parsed = match ob.try_convert::<String>()?.as_str() {
309
+ "linear" => InterpolationMethod::Linear,
310
+ "nearest" => InterpolationMethod::Nearest,
311
+ v => {
312
+ return Err(RbValueError::new_err(format!(
313
+ "method must be one of {{'linear', 'nearest'}}, got {v}",
314
+ )))
315
+ }
316
+ };
317
+ Ok(Wrap(parsed))
318
+ }
319
+ }
320
+
213
321
  impl TryConvert for Wrap<Option<AvroCompression>> {
214
322
  fn try_convert(ob: Value) -> RbResult<Self> {
215
323
  let parsed = match ob.try_convert::<String>()?.as_str() {
@@ -243,6 +351,22 @@ impl TryConvert for Wrap<CategoricalOrdering> {
243
351
  }
244
352
  }
245
353
 
354
+ impl TryConvert for Wrap<StartBy> {
355
+ fn try_convert(ob: Value) -> RbResult<Self> {
356
+ let parsed = match ob.try_convert::<String>()?.as_str() {
357
+ "window" => StartBy::WindowBound,
358
+ "datapoint" => StartBy::DataPoint,
359
+ "monday" => StartBy::Monday,
360
+ v => {
361
+ return Err(RbValueError::new_err(format!(
362
+ "closed must be one of {{'window', 'datapoint', 'monday'}}, got {v}",
363
+ )))
364
+ }
365
+ };
366
+ Ok(Wrap(parsed))
367
+ }
368
+ }
369
+
246
370
  impl TryConvert for Wrap<ClosedWindow> {
247
371
  fn try_convert(ob: Value) -> RbResult<Self> {
248
372
  let parsed = match ob.try_convert::<String>()?.as_str() {
@@ -1,4 +1,5 @@
1
1
  use magnus::{r_hash::ForEach, RArray, RHash, RString, Value};
2
+ use polars::frame::NullStrategy;
2
3
  use polars::io::avro::AvroCompression;
3
4
  use polars::io::mmap::ReaderBytes;
4
5
  use polars::io::RowCount;
@@ -397,7 +398,7 @@ impl RbDataFrame {
397
398
  let obj: Option<&ObjectValue> = s.get_object(idx).map(|any| any.into());
398
399
  obj.unwrap().to_object()
399
400
  }
400
- _ => Wrap(s.get(idx)).into(),
401
+ _ => Wrap(s.get(idx).unwrap()).into(),
401
402
  })
402
403
  .collect(),
403
404
  )
@@ -420,7 +421,7 @@ impl RbDataFrame {
420
421
  s.get_object(idx).map(|any| any.into());
421
422
  obj.unwrap().to_object()
422
423
  }
423
- _ => Wrap(s.get(idx)).into(),
424
+ _ => Wrap(s.get(idx).unwrap()).into(),
424
425
  })
425
426
  .collect(),
426
427
  )
@@ -573,9 +574,8 @@ impl RbDataFrame {
573
574
  .collect()
574
575
  }
575
576
 
576
- pub fn n_chunks(&self) -> RbResult<usize> {
577
- let n = self.df.borrow().n_chunks().map_err(RbPolarsErr::from)?;
578
- Ok(n)
577
+ pub fn n_chunks(&self) -> usize {
578
+ self.df.borrow().n_chunks()
579
579
  }
580
580
 
581
581
  pub fn shape(&self) -> (usize, usize) {
@@ -348,6 +348,7 @@ impl RbLazyFrame {
348
348
  include_boundaries: bool,
349
349
  closed: Wrap<ClosedWindow>,
350
350
  by: RArray,
351
+ start_by: Wrap<StartBy>,
351
352
  ) -> RbResult<RbLazyGroupBy> {
352
353
  let closed_window = closed.0;
353
354
  let by = rb_exprs_to_exprs(by)?;
@@ -362,6 +363,7 @@ impl RbLazyFrame {
362
363
  truncate,
363
364
  include_boundaries,
364
365
  closed_window,
366
+ start_by: start_by.0,
365
367
  },
366
368
  );
367
369
 
@@ -514,9 +516,13 @@ impl RbLazyFrame {
514
516
  ldf.median().into()
515
517
  }
516
518
 
517
- pub fn quantile(&self, quantile: f64, interpolation: Wrap<QuantileInterpolOptions>) -> Self {
519
+ pub fn quantile(
520
+ &self,
521
+ quantile: &RbExpr,
522
+ interpolation: Wrap<QuantileInterpolOptions>,
523
+ ) -> Self {
518
524
  let ldf = self.ldf.clone();
519
- ldf.quantile(quantile, interpolation.0).into()
525
+ ldf.quantile(quantile.inner.clone(), interpolation.0).into()
520
526
  }
521
527
 
522
528
  pub fn explode(&self, column: RArray) -> RbResult<Self> {
@@ -164,10 +164,14 @@ impl RbExpr {
164
164
  self.clone().inner.list().into()
165
165
  }
166
166
 
167
- pub fn quantile(&self, quantile: f64, interpolation: Wrap<QuantileInterpolOptions>) -> Self {
167
+ pub fn quantile(
168
+ &self,
169
+ quantile: &RbExpr,
170
+ interpolation: Wrap<QuantileInterpolOptions>,
171
+ ) -> Self {
168
172
  self.clone()
169
173
  .inner
170
- .quantile(quantile, interpolation.0)
174
+ .quantile(quantile.inner.clone(), interpolation.0)
171
175
  .into()
172
176
  }
173
177
 
@@ -506,7 +510,13 @@ impl RbExpr {
506
510
  self.inner.clone().shrink_dtype().into()
507
511
  }
508
512
 
509
- pub fn str_parse_date(&self, fmt: Option<String>, strict: bool, exact: bool) -> Self {
513
+ pub fn str_parse_date(
514
+ &self,
515
+ fmt: Option<String>,
516
+ strict: bool,
517
+ exact: bool,
518
+ cache: bool,
519
+ ) -> Self {
510
520
  self.inner
511
521
  .clone()
512
522
  .str()
@@ -515,11 +525,20 @@ impl RbExpr {
515
525
  fmt,
516
526
  strict,
517
527
  exact,
528
+ cache,
529
+ tz_aware: false,
518
530
  })
519
531
  .into()
520
532
  }
521
533
 
522
- pub fn str_parse_datetime(&self, fmt: Option<String>, strict: bool, exact: bool) -> Self {
534
+ pub fn str_parse_datetime(
535
+ &self,
536
+ fmt: Option<String>,
537
+ strict: bool,
538
+ exact: bool,
539
+ cache: bool,
540
+ tz_aware: bool,
541
+ ) -> Self {
523
542
  let tu = match fmt {
524
543
  Some(ref fmt) => {
525
544
  if fmt.contains("%.9f")
@@ -544,11 +563,19 @@ impl RbExpr {
544
563
  fmt,
545
564
  strict,
546
565
  exact,
566
+ cache,
567
+ tz_aware,
547
568
  })
548
569
  .into()
549
570
  }
550
571
 
551
- pub fn str_parse_time(&self, fmt: Option<String>, strict: bool, exact: bool) -> Self {
572
+ pub fn str_parse_time(
573
+ &self,
574
+ fmt: Option<String>,
575
+ strict: bool,
576
+ exact: bool,
577
+ cache: bool,
578
+ ) -> Self {
552
579
  self.inner
553
580
  .clone()
554
581
  .str()
@@ -557,6 +584,8 @@ impl RbExpr {
557
584
  fmt,
558
585
  strict,
559
586
  exact,
587
+ cache,
588
+ tz_aware: false,
560
589
  })
561
590
  .into()
562
591
  }
@@ -723,8 +752,12 @@ impl RbExpr {
723
752
  self.inner.clone().str().extract(&pat, group_index).into()
724
753
  }
725
754
 
726
- pub fn str_extract_all(&self, pat: String) -> Self {
727
- self.inner.clone().str().extract_all(&pat).into()
755
+ pub fn str_extract_all(&self, pat: &RbExpr) -> Self {
756
+ self.inner
757
+ .clone()
758
+ .str()
759
+ .extract_all(pat.inner.clone())
760
+ .into()
728
761
  }
729
762
 
730
763
  pub fn count_match(&self, pat: String) -> Self {
@@ -989,14 +1022,12 @@ impl RbExpr {
989
1022
  .clone()
990
1023
  .map_alias(move |name| {
991
1024
  let out = lambda.call::<_, String>((name,));
992
- // TODO switch to match
993
- out.unwrap()
994
- // match out {
995
- // Ok(out) => Ok(out.to_string()),
996
- // Err(e) => Err(PolarsError::ComputeError(
997
- // format!("Ruby function in 'map_alias' produced an error: {}.", e).into(),
998
- // )),
999
- // }
1025
+ match out {
1026
+ Ok(out) => Ok(out),
1027
+ Err(e) => Err(PolarsError::ComputeError(
1028
+ format!("Ruby function in 'map_alias' produced an error: {}.", e).into(),
1029
+ )),
1030
+ }
1000
1031
  })
1001
1032
  .into()
1002
1033
  }
@@ -1005,8 +1036,8 @@ impl RbExpr {
1005
1036
  self.inner.clone().exclude(columns).into()
1006
1037
  }
1007
1038
 
1008
- pub fn interpolate(&self) -> Self {
1009
- self.inner.clone().interpolate().into()
1039
+ pub fn interpolate(&self, method: Wrap<InterpolationMethod>) -> Self {
1040
+ self.inner.clone().interpolate(method.0).into()
1010
1041
  }
1011
1042
 
1012
1043
  pub fn rolling_sum(
@@ -1297,6 +1328,7 @@ impl RbExpr {
1297
1328
  &self,
1298
1329
  width_strat: Wrap<ListToStructWidthStrategy>,
1299
1330
  _name_gen: Option<Value>,
1331
+ upper_bound: usize,
1300
1332
  ) -> RbResult<Self> {
1301
1333
  // TODO fix
1302
1334
  let name_gen = None;
@@ -1311,7 +1343,7 @@ impl RbExpr {
1311
1343
  .inner
1312
1344
  .clone()
1313
1345
  .arr()
1314
- .to_struct(width_strat.0, name_gen)
1346
+ .to_struct(width_strat.0, name_gen, upper_bound)
1315
1347
  .into())
1316
1348
  }
1317
1349
 
@@ -1498,6 +1530,10 @@ pub fn cols(names: Vec<String>) -> RbExpr {
1498
1530
  dsl::cols(names).into()
1499
1531
  }
1500
1532
 
1533
+ pub fn dtype_cols(dtypes: Vec<DataType>) -> RbExpr {
1534
+ dsl::dtype_cols(dtypes).into()
1535
+ }
1536
+
1501
1537
  pub fn fold(acc: &RbExpr, lambda: Value, exprs: RArray) -> RbResult<RbExpr> {
1502
1538
  let exprs = rb_exprs_to_exprs(exprs)?;
1503
1539