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.
Files changed (206) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +274 -0
  3. data/Cargo.lock +1465 -867
  4. data/Cargo.toml +3 -0
  5. data/LICENSE.txt +2 -2
  6. data/README.md +87 -37
  7. data/ext/polars/Cargo.toml +47 -16
  8. data/ext/polars/src/c_api/allocator.rs +7 -0
  9. data/ext/polars/src/c_api/mod.rs +1 -0
  10. data/ext/polars/src/catalog/mod.rs +1 -0
  11. data/ext/polars/src/catalog/unity.rs +470 -0
  12. data/ext/polars/src/conversion/any_value.rs +99 -84
  13. data/ext/polars/src/conversion/categorical.rs +30 -0
  14. data/ext/polars/src/conversion/chunked_array.rs +71 -62
  15. data/ext/polars/src/conversion/datetime.rs +63 -0
  16. data/ext/polars/src/conversion/mod.rs +796 -312
  17. data/ext/polars/src/dataframe/construction.rs +6 -18
  18. data/ext/polars/src/dataframe/export.rs +30 -39
  19. data/ext/polars/src/dataframe/general.rs +294 -362
  20. data/ext/polars/src/dataframe/io.rs +33 -150
  21. data/ext/polars/src/dataframe/map.rs +175 -0
  22. data/ext/polars/src/dataframe/mod.rs +37 -5
  23. data/ext/polars/src/dataframe/serde.rs +23 -8
  24. data/ext/polars/src/error.rs +44 -7
  25. data/ext/polars/src/exceptions.rs +21 -8
  26. data/ext/polars/src/expr/array.rs +86 -22
  27. data/ext/polars/src/expr/binary.rs +50 -1
  28. data/ext/polars/src/expr/bitwise.rs +39 -0
  29. data/ext/polars/src/expr/categorical.rs +20 -0
  30. data/ext/polars/src/expr/datatype.rs +51 -0
  31. data/ext/polars/src/expr/datetime.rs +99 -41
  32. data/ext/polars/src/expr/extension.rs +12 -0
  33. data/ext/polars/src/expr/general.rs +252 -128
  34. data/ext/polars/src/expr/list.rs +56 -60
  35. data/ext/polars/src/expr/meta.rs +30 -35
  36. data/ext/polars/src/expr/mod.rs +28 -6
  37. data/ext/polars/src/expr/name.rs +29 -14
  38. data/ext/polars/src/expr/rolling.rs +111 -3
  39. data/ext/polars/src/expr/selector.rs +219 -0
  40. data/ext/polars/src/expr/serde.rs +28 -0
  41. data/ext/polars/src/expr/string.rs +118 -20
  42. data/ext/polars/src/expr/struct.rs +14 -1
  43. data/ext/polars/src/file.rs +194 -86
  44. data/ext/polars/src/functions/aggregation.rs +13 -12
  45. data/ext/polars/src/functions/business.rs +2 -3
  46. data/ext/polars/src/functions/eager.rs +3 -2
  47. data/ext/polars/src/functions/io.rs +90 -18
  48. data/ext/polars/src/functions/lazy.rs +267 -118
  49. data/ext/polars/src/functions/meta.rs +8 -7
  50. data/ext/polars/src/functions/misc.rs +1 -1
  51. data/ext/polars/src/functions/mod.rs +2 -1
  52. data/ext/polars/src/functions/range.rs +88 -31
  53. data/ext/polars/src/functions/strings.rs +6 -0
  54. data/ext/polars/src/functions/utils.rs +8 -0
  55. data/ext/polars/src/interop/arrow/mod.rs +52 -1
  56. data/ext/polars/src/interop/arrow/{to_ruby.rs → to_rb.rs} +37 -7
  57. data/ext/polars/src/interop/arrow/to_rust.rs +43 -0
  58. data/ext/polars/src/interop/numo/to_numo_df.rs +1 -1
  59. data/ext/polars/src/interop/numo/to_numo_series.rs +72 -50
  60. data/ext/polars/src/io/cloud_options.rs +107 -0
  61. data/ext/polars/src/io/mod.rs +4 -0
  62. data/ext/polars/src/io/scan_options.rs +113 -0
  63. data/ext/polars/src/io/sink_options.rs +46 -0
  64. data/ext/polars/src/io/sink_output.rs +21 -0
  65. data/ext/polars/src/lazyframe/exitable.rs +39 -0
  66. data/ext/polars/src/lazyframe/general.rs +846 -368
  67. data/ext/polars/src/lazyframe/mod.rs +58 -5
  68. data/ext/polars/src/lazyframe/optflags.rs +59 -0
  69. data/ext/polars/src/lazyframe/serde.rs +36 -4
  70. data/ext/polars/src/lazyframe/sink.rs +46 -0
  71. data/ext/polars/src/lazygroupby.rs +38 -9
  72. data/ext/polars/src/lib.rs +574 -165
  73. data/ext/polars/src/map/lazy.rs +44 -74
  74. data/ext/polars/src/map/mod.rs +18 -254
  75. data/ext/polars/src/map/series.rs +241 -1087
  76. data/ext/polars/src/on_startup.rs +192 -9
  77. data/ext/polars/src/prelude.rs +1 -0
  78. data/ext/polars/src/rb_modules.rs +10 -57
  79. data/ext/polars/src/ruby/exceptions.rs +26 -0
  80. data/ext/polars/src/ruby/gvl.rs +104 -0
  81. data/ext/polars/src/ruby/lazy.rs +46 -0
  82. data/ext/polars/src/ruby/mod.rs +11 -0
  83. data/ext/polars/src/ruby/numo.rs +52 -0
  84. data/ext/polars/src/ruby/plan_callback.rs +198 -0
  85. data/ext/polars/src/ruby/rb_modules.rs +16 -0
  86. data/ext/polars/src/ruby/ruby_convert_registry.rs +51 -0
  87. data/ext/polars/src/ruby/ruby_function.rs +11 -0
  88. data/ext/polars/src/ruby/ruby_udf.rs +164 -0
  89. data/ext/polars/src/ruby/thread.rs +65 -0
  90. data/ext/polars/src/ruby/utils.rs +39 -0
  91. data/ext/polars/src/series/aggregation.rs +116 -91
  92. data/ext/polars/src/series/arithmetic.rs +16 -22
  93. data/ext/polars/src/series/comparison.rs +101 -222
  94. data/ext/polars/src/series/construction.rs +80 -70
  95. data/ext/polars/src/series/export.rs +98 -56
  96. data/ext/polars/src/series/general.rs +323 -440
  97. data/ext/polars/src/series/import.rs +22 -5
  98. data/ext/polars/src/series/map.rs +103 -0
  99. data/ext/polars/src/series/mod.rs +57 -15
  100. data/ext/polars/src/series/scatter.rs +139 -82
  101. data/ext/polars/src/sql.rs +16 -9
  102. data/ext/polars/src/testing/frame.rs +31 -0
  103. data/ext/polars/src/testing/mod.rs +5 -0
  104. data/ext/polars/src/testing/series.rs +31 -0
  105. data/ext/polars/src/timeout.rs +105 -0
  106. data/ext/polars/src/utils.rs +105 -4
  107. data/lib/polars/array_expr.rb +500 -22
  108. data/lib/polars/array_name_space.rb +384 -10
  109. data/lib/polars/batched_csv_reader.rb +48 -66
  110. data/lib/polars/binary_expr.rb +217 -0
  111. data/lib/polars/binary_name_space.rb +155 -1
  112. data/lib/polars/cat_expr.rb +224 -0
  113. data/lib/polars/cat_name_space.rb +132 -32
  114. data/lib/polars/catalog/unity/catalog_info.rb +20 -0
  115. data/lib/polars/catalog/unity/column_info.rb +31 -0
  116. data/lib/polars/catalog/unity/namespace_info.rb +21 -0
  117. data/lib/polars/catalog/unity/table_info.rb +50 -0
  118. data/lib/polars/catalog.rb +448 -0
  119. data/lib/polars/collect_batches.rb +22 -0
  120. data/lib/polars/config.rb +3 -3
  121. data/lib/polars/convert.rb +201 -36
  122. data/lib/polars/data_frame.rb +2851 -1017
  123. data/lib/polars/data_frame_plot.rb +173 -0
  124. data/lib/polars/data_type_expr.rb +52 -0
  125. data/lib/polars/data_type_group.rb +6 -0
  126. data/lib/polars/data_types.rb +118 -18
  127. data/lib/polars/date_time_expr.rb +426 -84
  128. data/lib/polars/date_time_name_space.rb +384 -111
  129. data/lib/polars/dynamic_group_by.rb +102 -10
  130. data/lib/polars/exceptions.rb +50 -5
  131. data/lib/polars/expr.rb +2159 -915
  132. data/lib/polars/extension_expr.rb +39 -0
  133. data/lib/polars/extension_name_space.rb +39 -0
  134. data/lib/polars/functions/aggregation/horizontal.rb +11 -6
  135. data/lib/polars/functions/aggregation/vertical.rb +2 -3
  136. data/lib/polars/functions/as_datatype.rb +290 -8
  137. data/lib/polars/functions/business.rb +95 -0
  138. data/lib/polars/functions/col.rb +6 -5
  139. data/lib/polars/functions/datatype.rb +62 -0
  140. data/lib/polars/functions/eager.rb +426 -24
  141. data/lib/polars/functions/escape_regex.rb +21 -0
  142. data/lib/polars/functions/lazy.rb +813 -195
  143. data/lib/polars/functions/lit.rb +21 -10
  144. data/lib/polars/functions/range/int_range.rb +74 -2
  145. data/lib/polars/functions/range/linear_space.rb +195 -0
  146. data/lib/polars/functions/range/time_range.rb +1 -1
  147. data/lib/polars/functions/repeat.rb +7 -12
  148. data/lib/polars/functions/whenthen.rb +2 -2
  149. data/lib/polars/group_by.rb +188 -58
  150. data/lib/polars/iceberg_dataset.rb +108 -0
  151. data/lib/polars/in_process_query.rb +37 -0
  152. data/lib/polars/io/cloud.rb +18 -0
  153. data/lib/polars/io/csv.rb +336 -128
  154. data/lib/polars/io/database.rb +19 -4
  155. data/lib/polars/io/delta.rb +134 -0
  156. data/lib/polars/io/iceberg.rb +34 -0
  157. data/lib/polars/io/ipc.rb +63 -63
  158. data/lib/polars/io/json.rb +16 -0
  159. data/lib/polars/io/lines.rb +172 -0
  160. data/lib/polars/io/ndjson.rb +176 -20
  161. data/lib/polars/io/parquet.rb +173 -95
  162. data/lib/polars/io/scan_options.rb +55 -0
  163. data/lib/polars/io/sink_options.rb +27 -0
  164. data/lib/polars/io/utils.rb +17 -0
  165. data/lib/polars/lazy_frame.rb +3017 -622
  166. data/lib/polars/lazy_group_by.rb +436 -2
  167. data/lib/polars/list_expr.rb +551 -59
  168. data/lib/polars/list_name_space.rb +465 -51
  169. data/lib/polars/meta_expr.rb +146 -24
  170. data/lib/polars/name_expr.rb +87 -2
  171. data/lib/polars/query_opt_flags.rb +264 -0
  172. data/lib/polars/rolling_group_by.rb +90 -5
  173. data/lib/polars/scan_cast_options.rb +86 -0
  174. data/lib/polars/schema.rb +128 -0
  175. data/lib/polars/selector.rb +245 -0
  176. data/lib/polars/selectors.rb +1048 -201
  177. data/lib/polars/series.rb +2522 -774
  178. data/lib/polars/series_plot.rb +72 -0
  179. data/lib/polars/slice.rb +1 -1
  180. data/lib/polars/sql_context.rb +13 -6
  181. data/lib/polars/string_cache.rb +19 -72
  182. data/lib/polars/string_expr.rb +561 -107
  183. data/lib/polars/string_name_space.rb +781 -109
  184. data/lib/polars/struct_expr.rb +139 -18
  185. data/lib/polars/struct_name_space.rb +19 -1
  186. data/lib/polars/testing.rb +24 -273
  187. data/lib/polars/utils/constants.rb +2 -0
  188. data/lib/polars/utils/construction/data_frame.rb +410 -0
  189. data/lib/polars/utils/construction/series.rb +350 -0
  190. data/lib/polars/utils/construction/utils.rb +9 -0
  191. data/lib/polars/utils/convert.rb +18 -8
  192. data/lib/polars/utils/deprecation.rb +11 -0
  193. data/lib/polars/utils/parse.rb +62 -9
  194. data/lib/polars/utils/reduce_balanced.rb +43 -0
  195. data/lib/polars/utils/serde.rb +22 -0
  196. data/lib/polars/utils/unstable.rb +19 -0
  197. data/lib/polars/utils/various.rb +86 -1
  198. data/lib/polars/utils.rb +63 -48
  199. data/lib/polars/version.rb +1 -1
  200. data/lib/polars.rb +85 -2
  201. metadata +80 -28
  202. data/ext/polars/src/allocator.rs +0 -13
  203. data/ext/polars/src/batched_csv.rs +0 -138
  204. data/ext/polars/src/functions/string_cache.rs +0 -25
  205. data/ext/polars/src/map/dataframe.rs +0 -338
  206. data/lib/polars/plot.rb +0 -109
@@ -1,139 +1,136 @@
1
+ use std::hash::BuildHasher;
2
+
3
+ use arrow::bitmap::MutableBitmap;
1
4
  use either::Either;
2
- use magnus::{prelude::*, typed_data::Obj, IntoValue, RArray, Value};
3
- use polars::frame::NullStrategy;
4
- use polars::prelude::pivot::{pivot, pivot_stable};
5
+ use magnus::{RArray, Ruby, Value, prelude::*, value::Opaque};
5
6
  use polars::prelude::*;
6
7
 
7
8
  use crate::conversion::*;
8
- use crate::map::dataframe::{
9
- apply_lambda_unknown, apply_lambda_with_bool_out_type, apply_lambda_with_primitive_out_type,
10
- apply_lambda_with_utf8_out_type,
11
- };
12
9
  use crate::prelude::strings_to_pl_smallstr;
13
- use crate::series::{to_rbseries, to_series};
14
- use crate::{RbDataFrame, RbExpr, RbLazyFrame, RbPolarsErr, RbResult, RbSeries};
10
+ use crate::rb_modules::pl_utils;
11
+ use crate::ruby::exceptions::RbIndexError;
12
+ use crate::ruby::gvl::GvlExt;
13
+ use crate::ruby::utils::TryIntoValue;
14
+ use crate::series::ToRbSeries;
15
+ use crate::series::to_series;
16
+ use crate::utils::EnterPolarsExt;
17
+ use crate::{RbDataFrame, RbLazyFrame, RbPolarsErr, RbResult, RbSeries};
15
18
 
16
19
  impl RbDataFrame {
17
20
  pub fn init(columns: RArray) -> RbResult<Self> {
18
21
  let mut cols = Vec::new();
19
22
  for i in columns.into_iter() {
20
- cols.push(<&RbSeries>::try_convert(i)?.series.borrow().clone().into());
23
+ cols.push(<&RbSeries>::try_convert(i)?.series.read().clone().into());
21
24
  }
22
- let df = DataFrame::new(cols).map_err(RbPolarsErr::from)?;
25
+ let df = DataFrame::new_infer_height(cols).map_err(RbPolarsErr::from)?;
23
26
  Ok(RbDataFrame::new(df))
24
27
  }
25
28
 
26
29
  pub fn estimated_size(&self) -> usize {
27
- self.df.borrow().estimated_size()
30
+ self.df.read().estimated_size()
28
31
  }
29
32
 
30
33
  pub fn dtype_strings(&self) -> Vec<String> {
31
34
  self.df
32
- .borrow()
33
- .get_columns()
35
+ .read()
36
+ .columns()
34
37
  .iter()
35
38
  .map(|s| format!("{}", s.dtype()))
36
39
  .collect()
37
40
  }
38
41
 
39
- pub fn add(&self, s: &RbSeries) -> RbResult<Self> {
40
- let df = (&*self.df.borrow() + &*s.series.borrow()).map_err(RbPolarsErr::from)?;
41
- Ok(df.into())
42
+ pub fn add(rb: &Ruby, self_: &Self, s: &RbSeries) -> RbResult<Self> {
43
+ rb.enter_polars_df(|| &*self_.df.read() + &*s.series.read())
42
44
  }
43
45
 
44
- pub fn sub(&self, s: &RbSeries) -> RbResult<Self> {
45
- let df = (&*self.df.borrow() - &*s.series.borrow()).map_err(RbPolarsErr::from)?;
46
- Ok(df.into())
46
+ pub fn sub(rb: &Ruby, self_: &Self, s: &RbSeries) -> RbResult<Self> {
47
+ rb.enter_polars_df(|| &*self_.df.read() - &*s.series.read())
47
48
  }
48
49
 
49
- pub fn div(&self, s: &RbSeries) -> RbResult<Self> {
50
- let df = (&*self.df.borrow() / &*s.series.borrow()).map_err(RbPolarsErr::from)?;
51
- Ok(df.into())
50
+ pub fn div(rb: &Ruby, self_: &Self, s: &RbSeries) -> RbResult<Self> {
51
+ rb.enter_polars_df(|| &*self_.df.read() / &*s.series.read())
52
52
  }
53
53
 
54
- pub fn mul(&self, s: &RbSeries) -> RbResult<Self> {
55
- let df = (&*self.df.borrow() * &*s.series.borrow()).map_err(RbPolarsErr::from)?;
56
- Ok(df.into())
54
+ pub fn mul(rb: &Ruby, self_: &Self, s: &RbSeries) -> RbResult<Self> {
55
+ rb.enter_polars_df(|| &*self_.df.read() * &*s.series.read())
57
56
  }
58
57
 
59
- pub fn rem(&self, s: &RbSeries) -> RbResult<Self> {
60
- let df = (&*self.df.borrow() % &*s.series.borrow()).map_err(RbPolarsErr::from)?;
61
- Ok(df.into())
58
+ pub fn rem(rb: &Ruby, self_: &Self, s: &RbSeries) -> RbResult<Self> {
59
+ rb.enter_polars_df(|| &*self_.df.read() % &*s.series.read())
62
60
  }
63
61
 
64
- pub fn add_df(&self, s: &Self) -> RbResult<Self> {
65
- let df = (&*self.df.borrow() + &*s.df.borrow()).map_err(RbPolarsErr::from)?;
66
- Ok(df.into())
62
+ pub fn add_df(rb: &Ruby, self_: &Self, s: &Self) -> RbResult<Self> {
63
+ rb.enter_polars_df(|| &*self_.df.read() + &*s.df.read())
67
64
  }
68
65
 
69
- pub fn sub_df(&self, s: &Self) -> RbResult<Self> {
70
- let df = (&*self.df.borrow() - &*s.df.borrow()).map_err(RbPolarsErr::from)?;
71
- Ok(df.into())
66
+ pub fn sub_df(rb: &Ruby, self_: &Self, s: &Self) -> RbResult<Self> {
67
+ rb.enter_polars_df(|| &*self_.df.read() - &*s.df.read())
72
68
  }
73
69
 
74
- pub fn div_df(&self, s: &Self) -> RbResult<Self> {
75
- let df = (&*self.df.borrow() / &*s.df.borrow()).map_err(RbPolarsErr::from)?;
76
- Ok(df.into())
70
+ pub fn div_df(rb: &Ruby, self_: &Self, s: &Self) -> RbResult<Self> {
71
+ rb.enter_polars_df(|| &*self_.df.read() / &*s.df.read())
77
72
  }
78
73
 
79
- pub fn mul_df(&self, s: &Self) -> RbResult<Self> {
80
- let df = (&*self.df.borrow() * &*s.df.borrow()).map_err(RbPolarsErr::from)?;
81
- Ok(df.into())
74
+ pub fn mul_df(rb: &Ruby, self_: &Self, s: &Self) -> RbResult<Self> {
75
+ rb.enter_polars_df(|| &*self_.df.read() * &*s.df.read())
82
76
  }
83
77
 
84
- pub fn rem_df(&self, s: &Self) -> RbResult<Self> {
85
- let df = (&*self.df.borrow() % &*s.df.borrow()).map_err(RbPolarsErr::from)?;
86
- Ok(df.into())
78
+ pub fn rem_df(rb: &Ruby, self_: &Self, s: &Self) -> RbResult<Self> {
79
+ rb.enter_polars_df(|| &*self_.df.read() % &*s.df.read())
87
80
  }
88
81
 
89
82
  pub fn sample_n(
90
- &self,
83
+ rb: &Ruby,
84
+ self_: &Self,
91
85
  n: &RbSeries,
92
86
  with_replacement: bool,
93
87
  shuffle: bool,
94
88
  seed: Option<u64>,
95
89
  ) -> RbResult<Self> {
96
- let df = self
97
- .df
98
- .borrow()
99
- .sample_n(&n.series.borrow(), with_replacement, shuffle, seed)
100
- .map_err(RbPolarsErr::from)?;
101
- Ok(df.into())
90
+ rb.enter_polars_df(|| {
91
+ self_
92
+ .df
93
+ .read()
94
+ .sample_n(&n.series.read(), with_replacement, shuffle, seed)
95
+ })
102
96
  }
103
97
 
104
98
  pub fn sample_frac(
105
- &self,
99
+ rb: &Ruby,
100
+ self_: &Self,
106
101
  frac: &RbSeries,
107
102
  with_replacement: bool,
108
103
  shuffle: bool,
109
104
  seed: Option<u64>,
110
105
  ) -> RbResult<Self> {
111
- let df = self
112
- .df
113
- .borrow()
114
- .sample_frac(&frac.series.borrow(), with_replacement, shuffle, seed)
115
- .map_err(RbPolarsErr::from)?;
116
- Ok(df.into())
106
+ rb.enter_polars_df(|| {
107
+ self_
108
+ .df
109
+ .read()
110
+ .sample_frac(&frac.series.read(), with_replacement, shuffle, seed)
111
+ })
117
112
  }
118
113
 
119
- pub fn rechunk(&self) -> Self {
120
- let mut df = self.df.borrow_mut().clone();
121
- df.as_single_chunk_par();
122
- df.into()
114
+ pub fn rechunk(rb: &Ruby, self_: &Self) -> RbResult<Self> {
115
+ rb.enter_polars_df(|| {
116
+ let mut df = self_.df.write().clone();
117
+ df.rechunk_mut_par();
118
+ Ok(df)
119
+ })
123
120
  }
124
121
 
125
122
  pub fn as_str(&self) -> String {
126
- format!("{}", self.df.borrow())
123
+ format!("{}", self.df.read())
127
124
  }
128
125
 
129
- pub fn get_columns(&self) -> RArray {
130
- let cols = self.df.borrow().get_columns().to_vec();
131
- to_rbseries(cols)
126
+ pub fn get_columns(rb: &Ruby, self_: &Self) -> RArray {
127
+ let cols = self_.df.read().columns().to_vec();
128
+ cols.to_rbseries(rb)
132
129
  }
133
130
 
134
131
  pub fn columns(&self) -> Vec<String> {
135
132
  self.df
136
- .borrow()
133
+ .read()
137
134
  .get_column_names()
138
135
  .iter()
139
136
  .map(|v| v.to_string())
@@ -142,415 +139,334 @@ impl RbDataFrame {
142
139
 
143
140
  pub fn set_column_names(&self, names: Vec<String>) -> RbResult<()> {
144
141
  self.df
145
- .borrow_mut()
142
+ .write()
146
143
  .set_column_names(&names)
147
144
  .map_err(RbPolarsErr::from)?;
148
145
  Ok(())
149
146
  }
150
147
 
151
- pub fn dtypes(&self) -> RArray {
152
- RArray::from_iter(
153
- self.df
154
- .borrow()
155
- .iter()
156
- .map(|s| Wrap(s.dtype().clone()).into_value()),
157
- )
148
+ pub fn dtypes(ruby: &Ruby, self_: &Self) -> RbResult<RArray> {
149
+ let df = self_.df.read();
150
+ let iter = df
151
+ .columns()
152
+ .iter()
153
+ .map(|s| Wrap(s.dtype().clone()).try_into_value_with(ruby));
154
+ ruby.ary_try_from_iter(iter)
158
155
  }
159
156
 
160
157
  pub fn n_chunks(&self) -> usize {
161
- self.df.borrow().n_chunks()
158
+ self.df.read().first_col_n_chunks()
162
159
  }
163
160
 
164
161
  pub fn shape(&self) -> (usize, usize) {
165
- self.df.borrow().shape()
162
+ self.df.read().shape()
166
163
  }
167
164
 
168
165
  pub fn height(&self) -> usize {
169
- self.df.borrow().height()
166
+ self.df.read().height()
170
167
  }
171
168
 
172
169
  pub fn width(&self) -> usize {
173
- self.df.borrow().width()
170
+ self.df.read().width()
174
171
  }
175
172
 
176
- pub fn hstack(&self, columns: RArray) -> RbResult<Self> {
173
+ pub fn hstack(rb: &Ruby, self_: &Self, columns: RArray) -> RbResult<Self> {
177
174
  let columns = to_series(columns)?;
178
175
  let columns = columns.into_iter().map(Into::into).collect::<Vec<_>>();
179
- let df = self
180
- .df
181
- .borrow()
182
- .hstack(&columns)
183
- .map_err(RbPolarsErr::from)?;
184
- Ok(df.into())
176
+ rb.enter_polars_df(|| self_.df.read().hstack(&columns))
185
177
  }
186
178
 
187
- pub fn hstack_mut(&self, columns: RArray) -> RbResult<()> {
179
+ pub fn hstack_mut(rb: &Ruby, self_: &Self, columns: RArray) -> RbResult<()> {
188
180
  let columns = to_series(columns)?;
189
181
  let columns = columns.into_iter().map(Into::into).collect::<Vec<_>>();
190
- self.df
191
- .borrow_mut()
192
- .hstack_mut(&columns)
193
- .map_err(RbPolarsErr::from)?;
182
+ rb.enter_polars(|| self_.df.write().hstack_mut(&columns).map(drop))?;
194
183
  Ok(())
195
184
  }
196
185
 
197
- pub fn vstack(&self, df: &RbDataFrame) -> RbResult<Self> {
198
- let df = self
199
- .df
200
- .borrow()
201
- .vstack(&df.df.borrow())
202
- .map_err(RbPolarsErr::from)?;
203
- Ok(df.into())
186
+ pub fn vstack(rb: &Ruby, self_: &Self, other: &RbDataFrame) -> RbResult<Self> {
187
+ rb.enter_polars_df(|| self_.df.read().vstack(&other.df.read()))
204
188
  }
205
189
 
206
- pub fn vstack_mut(&self, df: &RbDataFrame) -> RbResult<()> {
207
- self.df
208
- .borrow_mut()
209
- .vstack_mut(&df.df.borrow())
210
- .map_err(RbPolarsErr::from)?;
190
+ pub fn vstack_mut(rb: &Ruby, self_: &Self, other: &RbDataFrame) -> RbResult<()> {
191
+ rb.enter_polars(|| {
192
+ // Prevent self-vstack deadlocks.
193
+ let other = other.df.read().clone();
194
+ self_.df.write().vstack_mut(&other)?;
195
+ PolarsResult::Ok(())
196
+ })?;
211
197
  Ok(())
212
198
  }
213
199
 
214
- pub fn extend(&self, df: &RbDataFrame) -> RbResult<()> {
215
- self.df
216
- .borrow_mut()
217
- .extend(&df.df.borrow())
218
- .map_err(RbPolarsErr::from)?;
200
+ pub fn extend(rb: &Ruby, self_: &Self, other: &RbDataFrame) -> RbResult<()> {
201
+ rb.enter_polars(|| {
202
+ // Prevent self-extend deadlocks.
203
+ let other = other.df.read().clone();
204
+ self_.df.write().extend(&other)
205
+ })?;
219
206
  Ok(())
220
207
  }
221
208
 
222
209
  pub fn drop_in_place(&self, name: String) -> RbResult<RbSeries> {
223
210
  let s = self
224
211
  .df
225
- .borrow_mut()
212
+ .write()
226
213
  .drop_in_place(&name)
227
214
  .map_err(RbPolarsErr::from)?;
228
215
  let s = s.take_materialized_series();
229
216
  Ok(RbSeries::new(s))
230
217
  }
231
218
 
232
- pub fn select_at_idx(&self, idx: usize) -> Option<RbSeries> {
233
- self.df
234
- .borrow()
235
- .select_at_idx(idx)
236
- .map(|s| RbSeries::new(s.as_materialized_series().clone()))
219
+ pub fn to_series(&self, index: isize) -> RbResult<RbSeries> {
220
+ let df = &self.df.read();
221
+
222
+ let index_adjusted = if index < 0 {
223
+ df.width().checked_sub(index.unsigned_abs())
224
+ } else {
225
+ Some(usize::try_from(index).unwrap())
226
+ };
227
+
228
+ let s = index_adjusted.and_then(|i| df.select_at_idx(i));
229
+ match s {
230
+ Some(s) => Ok(RbSeries::new(s.as_materialized_series().clone())),
231
+ None => Err(RbIndexError::new_err(
232
+ polars_err!(oob = index, df.width()).to_string(),
233
+ )),
234
+ }
237
235
  }
238
236
 
239
237
  pub fn get_column_index(&self, name: String) -> Option<usize> {
240
- self.df.borrow().get_column_index(&name)
238
+ self.df.read().get_column_index(&name)
241
239
  }
242
240
 
243
241
  pub fn get_column(&self, name: String) -> RbResult<RbSeries> {
244
242
  let series = self
245
243
  .df
246
- .borrow()
244
+ .read()
247
245
  .column(&name)
248
246
  .map(|s| RbSeries::new(s.as_materialized_series().clone()))
249
247
  .map_err(RbPolarsErr::from)?;
250
248
  Ok(series)
251
249
  }
252
250
 
253
- pub fn select(&self, selection: Vec<String>) -> RbResult<Self> {
254
- let df = self
255
- .df
256
- .borrow()
257
- .select(selection)
258
- .map_err(RbPolarsErr::from)?;
259
- Ok(RbDataFrame::new(df))
251
+ pub fn select(rb: &Ruby, self_: &Self, columns: Vec<String>) -> RbResult<Self> {
252
+ rb.enter_polars_df(|| self_.df.read().select(columns.iter().map(|x| &**x)))
260
253
  }
261
254
 
262
- pub fn gather(&self, indices: Vec<IdxSize>) -> RbResult<Self> {
255
+ pub fn gather(rb: &Ruby, self_: &Self, indices: Vec<IdxSize>) -> RbResult<Self> {
263
256
  let indices = IdxCa::from_vec("".into(), indices);
264
- let df = self.df.borrow().take(&indices).map_err(RbPolarsErr::from)?;
265
- Ok(RbDataFrame::new(df))
257
+ rb.enter_polars_df(|| self_.df.read().take(&indices))
266
258
  }
267
259
 
268
- pub fn take_with_series(&self, indices: &RbSeries) -> RbResult<Self> {
269
- let binding = indices.series.borrow();
270
- let idx = binding.idx().map_err(RbPolarsErr::from)?;
271
- let df = self.df.borrow().take(idx).map_err(RbPolarsErr::from)?;
272
- Ok(RbDataFrame::new(df))
260
+ pub fn gather_with_series(rb: &Ruby, self_: &Self, indices: &RbSeries) -> RbResult<Self> {
261
+ let idx_s = indices.series.read();
262
+ let indices = idx_s.idx().map_err(RbPolarsErr::from)?;
263
+ rb.enter_polars_df(|| self_.df.read().take(indices))
273
264
  }
274
265
 
275
266
  pub fn replace(&self, column: String, new_col: &RbSeries) -> RbResult<()> {
276
267
  self.df
277
- .borrow_mut()
278
- .replace(&column, new_col.series.borrow().clone())
268
+ .write()
269
+ .replace(&column, new_col.clone().series.into_inner().into_column())
279
270
  .map_err(RbPolarsErr::from)?;
280
271
  Ok(())
281
272
  }
282
273
 
283
274
  pub fn replace_column(&self, index: usize, new_col: &RbSeries) -> RbResult<()> {
284
275
  self.df
285
- .borrow_mut()
286
- .replace_column(index, new_col.series.borrow().clone())
276
+ .write()
277
+ .replace_column(index, new_col.clone().series.into_inner().into_column())
287
278
  .map_err(RbPolarsErr::from)?;
288
279
  Ok(())
289
280
  }
290
281
 
291
282
  pub fn insert_column(&self, index: usize, new_col: &RbSeries) -> RbResult<()> {
292
283
  self.df
293
- .borrow_mut()
294
- .insert_column(index, new_col.series.borrow().clone())
284
+ .write()
285
+ .insert_column(index, new_col.clone().series.into_inner().into_column())
295
286
  .map_err(RbPolarsErr::from)?;
296
287
  Ok(())
297
288
  }
298
289
 
299
- pub fn slice(&self, offset: usize, length: Option<usize>) -> Self {
300
- let df = self.df.borrow().slice(
301
- offset as i64,
302
- length.unwrap_or_else(|| self.df.borrow().height()),
303
- );
304
- df.into()
290
+ pub fn slice(rb: &Ruby, self_: &Self, offset: i64, length: Option<usize>) -> RbResult<Self> {
291
+ rb.enter_polars_df(|| {
292
+ let df = self_.df.read();
293
+ Ok(df.slice(offset, length.unwrap_or_else(|| df.height())))
294
+ })
305
295
  }
306
296
 
307
- pub fn head(&self, length: Option<usize>) -> Self {
308
- self.df.borrow().head(length).into()
297
+ pub fn head(rb: &Ruby, self_: &Self, n: usize) -> RbResult<Self> {
298
+ rb.enter_polars_df(|| Ok(self_.df.read().head(Some(n))))
309
299
  }
310
300
 
311
- pub fn tail(&self, length: Option<usize>) -> Self {
312
- self.df.borrow().tail(length).into()
301
+ pub fn tail(rb: &Ruby, self_: &Self, n: usize) -> RbResult<Self> {
302
+ rb.enter_polars_df(|| Ok(self_.df.read().tail(Some(n))))
313
303
  }
314
304
 
315
- pub fn is_unique(&self) -> RbResult<RbSeries> {
316
- let mask = self.df.borrow().is_unique().map_err(RbPolarsErr::from)?;
317
- Ok(mask.into_series().into())
305
+ pub fn is_unique(rb: &Ruby, self_: &Self) -> RbResult<RbSeries> {
306
+ rb.enter_polars_series(|| self_.df.read().is_unique())
318
307
  }
319
308
 
320
- pub fn is_duplicated(&self) -> RbResult<RbSeries> {
321
- let mask = self
322
- .df
323
- .borrow()
324
- .is_duplicated()
325
- .map_err(RbPolarsErr::from)?;
326
- Ok(mask.into_series().into())
309
+ pub fn is_duplicated(rb: &Ruby, self_: &Self) -> RbResult<RbSeries> {
310
+ rb.enter_polars_series(|| self_.df.read().is_duplicated())
327
311
  }
328
312
 
329
- pub fn equals(&self, other: &RbDataFrame, null_equal: bool) -> bool {
313
+ pub fn equals(
314
+ rb: &Ruby,
315
+ self_: &Self,
316
+ other: &RbDataFrame,
317
+ null_equal: bool,
318
+ ) -> RbResult<bool> {
330
319
  if null_equal {
331
- self.df.borrow().equals_missing(&other.df.borrow())
320
+ rb.enter_polars_ok(|| self_.df.read().equals_missing(&other.df.read()))
332
321
  } else {
333
- self.df.borrow().equals(&other.df.borrow())
322
+ rb.enter_polars_ok(|| self_.df.read().equals(&other.df.read()))
334
323
  }
335
324
  }
336
325
 
337
- pub fn with_row_index(&self, name: String, offset: Option<IdxSize>) -> RbResult<Self> {
338
- let df = self
339
- .df
340
- .borrow()
341
- .with_row_index(name.into(), offset)
342
- .map_err(RbPolarsErr::from)?;
343
- Ok(df.into())
326
+ pub fn with_row_index(
327
+ rb: &Ruby,
328
+ self_: &Self,
329
+ name: String,
330
+ offset: Option<IdxSize>,
331
+ ) -> RbResult<Self> {
332
+ rb.enter_polars_df(|| self_.df.read().with_row_index(name.into(), offset))
333
+ }
334
+
335
+ pub fn group_by_map_groups(
336
+ rb: &Ruby,
337
+ self_: &Self,
338
+ by: Vec<String>,
339
+ lambda: Value,
340
+ maintain_order: bool,
341
+ ) -> RbResult<Self> {
342
+ let lambda = Opaque::from(lambda);
343
+ rb.enter_polars_df(|| {
344
+ let df = self_.df.read().clone(); // Clone so we can't deadlock on re-entrance from lambda.
345
+ let gb = if maintain_order {
346
+ df.group_by_stable(by.iter().map(|x| &**x))
347
+ } else {
348
+ df.group_by(by.iter().map(|x| &**x))
349
+ }?;
350
+
351
+ let function = move |df: DataFrame| {
352
+ Ruby::attach(|rb| {
353
+ let lambda = rb.get_inner(lambda);
354
+
355
+ let rbpolars = pl_utils(rb);
356
+ let rbdf = RbDataFrame::new(df);
357
+ let ruby_df_wrapper: Value =
358
+ rbpolars.funcall("wrap_df", (rbdf,)).unwrap();
359
+
360
+ // Call the lambda and get a Ruby-side DataFrame wrapper.
361
+ let result_df_wrapper: Value = match lambda.funcall("call", (ruby_df_wrapper,)) {
362
+ Ok(rbobj) => rbobj,
363
+ Err(e) => panic!("UDF failed: {}", e),
364
+ };
365
+ let rbdf: &RbDataFrame = result_df_wrapper.funcall("_df", ()).expect(
366
+ "Could not get DataFrame attribute '_df'. Make sure that you return a DataFrame object.",
367
+ );
368
+
369
+ Ok(rbdf.clone().df.into_inner())
370
+ })
371
+ };
372
+
373
+ gb.apply(function)
374
+ })
344
375
  }
345
376
 
346
377
  pub fn clone(&self) -> Self {
347
- RbDataFrame::new(self.df.borrow().clone())
378
+ Clone::clone(self)
348
379
  }
349
380
 
350
381
  pub fn unpivot(
351
- &self,
352
- on: Vec<String>,
382
+ rb: &Ruby,
383
+ self_: &Self,
384
+ on: Option<Vec<String>>,
353
385
  index: Vec<String>,
354
386
  value_name: Option<String>,
355
387
  variable_name: Option<String>,
356
388
  ) -> RbResult<Self> {
357
- let args = UnpivotArgsIR {
358
- on: strings_to_pl_smallstr(on),
359
- index: strings_to_pl_smallstr(index),
360
- value_name: value_name.map(|s| s.into()),
361
- variable_name: variable_name.map(|s| s.into()),
362
- };
389
+ use polars_ops::unpivot::UnpivotDF;
390
+ let args = UnpivotArgsIR::new(
391
+ self_.df.read().get_column_names_owned(),
392
+ on.map(strings_to_pl_smallstr),
393
+ strings_to_pl_smallstr(index),
394
+ value_name.map(|s| s.into()),
395
+ variable_name.map(|s| s.into()),
396
+ );
363
397
 
364
- let df = self.df.borrow().unpivot2(args).map_err(RbPolarsErr::from)?;
365
- Ok(RbDataFrame::new(df))
366
- }
367
-
368
- #[allow(clippy::too_many_arguments)]
369
- pub fn pivot_expr(
370
- &self,
371
- on: Vec<String>,
372
- index: Option<Vec<String>>,
373
- values: Option<Vec<String>>,
374
- maintain_order: bool,
375
- sort_columns: bool,
376
- aggregate_expr: Option<&RbExpr>,
377
- separator: Option<String>,
378
- ) -> RbResult<Self> {
379
- let fun = if maintain_order { pivot_stable } else { pivot };
380
- let agg_expr = aggregate_expr.map(|aggregate_expr| aggregate_expr.inner.clone());
381
- let df = fun(
382
- &self.df.borrow(),
383
- on,
384
- index,
385
- values,
386
- sort_columns,
387
- agg_expr,
388
- separator.as_deref(),
389
- )
390
- .map_err(RbPolarsErr::from)?;
391
- Ok(RbDataFrame::new(df))
398
+ rb.enter_polars_df(|| self_.df.read().unpivot2(args))
392
399
  }
393
400
 
394
401
  pub fn partition_by(
395
- &self,
402
+ rb: &Ruby,
403
+ self_: &Self,
396
404
  by: Vec<String>,
397
405
  maintain_order: bool,
398
406
  include_key: bool,
399
407
  ) -> RbResult<RArray> {
400
408
  let out = if maintain_order {
401
- self.df.borrow().partition_by_stable(by, include_key)
409
+ self_.df.read().partition_by_stable(by, include_key)
402
410
  } else {
403
- self.df.borrow().partition_by(by, include_key)
411
+ self_.df.read().partition_by(by, include_key)
404
412
  }
405
413
  .map_err(RbPolarsErr::from)?;
406
- Ok(RArray::from_iter(out.into_iter().map(RbDataFrame::new)))
414
+ Ok(rb.ary_from_iter(out.into_iter().map(RbDataFrame::new)))
407
415
  }
408
416
 
409
417
  pub fn lazy(&self) -> RbLazyFrame {
410
- self.df.borrow().clone().lazy().into()
411
- }
412
-
413
- pub fn max_horizontal(&self) -> RbResult<Option<RbSeries>> {
414
- let s = self
415
- .df
416
- .borrow()
417
- .max_horizontal()
418
- .map_err(RbPolarsErr::from)?;
419
- Ok(s.map(|s| s.take_materialized_series().into()))
420
- }
421
-
422
- pub fn min_horizontal(&self) -> RbResult<Option<RbSeries>> {
423
- let s = self
424
- .df
425
- .borrow()
426
- .min_horizontal()
427
- .map_err(RbPolarsErr::from)?;
428
- Ok(s.map(|s| s.take_materialized_series().into()))
429
- }
430
-
431
- pub fn sum_horizontal(&self, ignore_nulls: bool) -> RbResult<Option<RbSeries>> {
432
- let null_strategy = if ignore_nulls {
433
- NullStrategy::Ignore
434
- } else {
435
- NullStrategy::Propagate
436
- };
437
- let s = self
438
- .df
439
- .borrow()
440
- .sum_horizontal(null_strategy)
441
- .map_err(RbPolarsErr::from)?;
442
- Ok(s.map(|s| s.into()))
443
- }
444
-
445
- pub fn mean_horizontal(&self, ignore_nulls: bool) -> RbResult<Option<RbSeries>> {
446
- let null_strategy = if ignore_nulls {
447
- NullStrategy::Ignore
448
- } else {
449
- NullStrategy::Propagate
450
- };
451
- let s = self
452
- .df
453
- .borrow()
454
- .mean_horizontal(null_strategy)
455
- .map_err(RbPolarsErr::from)?;
456
- Ok(s.map(|s| s.into()))
418
+ self.df.read().clone().lazy().into()
457
419
  }
458
420
 
459
421
  pub fn to_dummies(
460
- &self,
422
+ rb: &Ruby,
423
+ self_: &Self,
461
424
  columns: Option<Vec<String>>,
462
425
  separator: Option<String>,
463
426
  drop_first: bool,
427
+ drop_nulls: bool,
464
428
  ) -> RbResult<Self> {
465
- let df = match columns {
466
- Some(cols) => self.df.borrow().columns_to_dummies(
429
+ rb.enter_polars_df(|| match columns {
430
+ Some(cols) => self_.df.read().columns_to_dummies(
467
431
  cols.iter().map(|x| x as &str).collect(),
468
432
  separator.as_deref(),
469
433
  drop_first,
434
+ drop_nulls,
470
435
  ),
471
- None => self
436
+ None => self_
472
437
  .df
473
- .borrow()
474
- .to_dummies(separator.as_deref(), drop_first),
475
- }
476
- .map_err(RbPolarsErr::from)?;
477
- Ok(df.into())
438
+ .read()
439
+ .to_dummies(separator.as_deref(), drop_first, drop_nulls),
440
+ })
478
441
  }
479
442
 
480
- pub fn null_count(&self) -> Self {
481
- let df = self.df.borrow().null_count();
482
- df.into()
443
+ pub fn null_count(rb: &Ruby, self_: &Self) -> RbResult<Self> {
444
+ rb.enter_polars_df(|| Ok(self_.df.read().null_count()))
483
445
  }
484
446
 
485
- pub fn map_rows(
486
- &self,
487
- lambda: Value,
488
- output_type: Option<Wrap<DataType>>,
489
- inference_size: usize,
490
- ) -> RbResult<(Value, bool)> {
491
- let df = &self.df.borrow();
492
-
493
- let output_type = output_type.map(|dt| dt.0);
494
- let out = match output_type {
495
- Some(DataType::Int32) => {
496
- apply_lambda_with_primitive_out_type::<Int32Type>(df, lambda, 0, None).into_series()
497
- }
498
- Some(DataType::Int64) => {
499
- apply_lambda_with_primitive_out_type::<Int64Type>(df, lambda, 0, None).into_series()
500
- }
501
- Some(DataType::UInt32) => {
502
- apply_lambda_with_primitive_out_type::<UInt32Type>(df, lambda, 0, None)
503
- .into_series()
504
- }
505
- Some(DataType::UInt64) => {
506
- apply_lambda_with_primitive_out_type::<UInt64Type>(df, lambda, 0, None)
507
- .into_series()
508
- }
509
- Some(DataType::Float32) => {
510
- apply_lambda_with_primitive_out_type::<Float32Type>(df, lambda, 0, None)
511
- .into_series()
512
- }
513
- Some(DataType::Float64) => {
514
- apply_lambda_with_primitive_out_type::<Float64Type>(df, lambda, 0, None)
515
- .into_series()
516
- }
517
- Some(DataType::Boolean) => {
518
- apply_lambda_with_bool_out_type(df, lambda, 0, None).into_series()
519
- }
520
- Some(DataType::Date) => {
521
- apply_lambda_with_primitive_out_type::<Int32Type>(df, lambda, 0, None)
522
- .into_date()
523
- .into_series()
524
- }
525
- Some(DataType::Datetime(tu, tz)) => {
526
- apply_lambda_with_primitive_out_type::<Int64Type>(df, lambda, 0, None)
527
- .into_datetime(tu, tz)
528
- .into_series()
529
- }
530
- Some(DataType::String) => {
531
- apply_lambda_with_utf8_out_type(df, lambda, 0, None).into_series()
532
- }
533
- _ => return apply_lambda_unknown(df, lambda, inference_size),
534
- };
535
-
536
- Ok((Obj::wrap(RbSeries::from(out)).as_value(), false))
537
- }
538
-
539
- pub fn shrink_to_fit(&self) {
540
- self.df.borrow_mut().shrink_to_fit();
447
+ pub fn shrink_to_fit(rb: &Ruby, self_: &Self) -> RbResult<()> {
448
+ rb.enter_polars_ok(|| self_.df.write().shrink_to_fit())
541
449
  }
542
450
 
543
- pub fn hash_rows(&self, k0: u64, k1: u64, k2: u64, k3: u64) -> RbResult<RbSeries> {
544
- let hb = ahash::RandomState::with_seeds(k0, k1, k2, k3);
545
- let hash = self
546
- .df
547
- .borrow_mut()
548
- .hash_rows(Some(hb))
549
- .map_err(RbPolarsErr::from)?;
550
- Ok(hash.into_series().into())
451
+ pub fn hash_rows(
452
+ rb: &Ruby,
453
+ self_: &Self,
454
+ k0: u64,
455
+ k1: u64,
456
+ k2: u64,
457
+ k3: u64,
458
+ ) -> RbResult<RbSeries> {
459
+ let seed = PlFixedStateQuality::default().hash_one((k0, k1, k2, k3));
460
+ let hb = PlSeedableRandomStateQuality::seed_from_u64(seed);
461
+ rb.enter_polars_series(|| self_.df.write().hash_rows(Some(hb)))
551
462
  }
552
463
 
553
- pub fn transpose(&self, keep_names_as: Option<String>, column_names: Value) -> RbResult<Self> {
464
+ pub fn transpose(
465
+ rb: &Ruby,
466
+ self_: &Self,
467
+ keep_names_as: Option<String>,
468
+ column_names: Value,
469
+ ) -> RbResult<Self> {
554
470
  let new_col_names = if let Ok(name) = <Vec<String>>::try_convert(column_names) {
555
471
  Some(Either::Right(name))
556
472
  } else if let Ok(name) = String::try_convert(column_names) {
@@ -558,45 +474,61 @@ impl RbDataFrame {
558
474
  } else {
559
475
  None
560
476
  };
561
- Ok(self
562
- .df
563
- .borrow_mut()
564
- .transpose(keep_names_as.as_deref(), new_col_names)
565
- .map_err(RbPolarsErr::from)?
566
- .into())
477
+ rb.enter_polars_df(|| {
478
+ self_
479
+ .df
480
+ .write()
481
+ .transpose(keep_names_as.as_deref(), new_col_names)
482
+ })
567
483
  }
568
484
 
569
485
  pub fn upsample(
570
- &self,
486
+ rb: &Ruby,
487
+ self_: &Self,
571
488
  by: Vec<String>,
572
489
  index_column: String,
573
490
  every: String,
574
491
  stable: bool,
575
492
  ) -> RbResult<Self> {
576
- let out = if stable {
577
- self.df
578
- .borrow()
579
- .upsample_stable(by, &index_column, Duration::parse(&every))
580
- } else {
581
- self.df
582
- .borrow()
583
- .upsample(by, &index_column, Duration::parse(&every))
584
- };
585
- let out = out.map_err(RbPolarsErr::from)?;
586
- Ok(out.into())
587
- }
588
-
589
- pub fn to_struct(&self, name: String) -> RbSeries {
590
- let s = self.df.borrow().clone().into_struct(name.into());
591
- s.into_series().into()
592
- }
593
-
594
- pub fn unnest(&self, names: Vec<String>) -> RbResult<Self> {
595
- let df = self.df.borrow().unnest(names).map_err(RbPolarsErr::from)?;
596
- Ok(df.into())
493
+ rb.enter_polars_df(|| {
494
+ if stable {
495
+ self_
496
+ .df
497
+ .read()
498
+ .upsample_stable(by, &index_column, Duration::parse(&every))
499
+ } else {
500
+ self_
501
+ .df
502
+ .read()
503
+ .upsample(by, &index_column, Duration::parse(&every))
504
+ }
505
+ })
506
+ }
507
+
508
+ pub fn to_struct(
509
+ rb: &Ruby,
510
+ self_: &Self,
511
+ name: String,
512
+ invalid_indices: Vec<usize>,
513
+ ) -> RbResult<RbSeries> {
514
+ rb.enter_polars_series(|| {
515
+ let mut ca = self_.df.read().clone().into_struct(name.into());
516
+
517
+ if !invalid_indices.is_empty() {
518
+ let mut validity = MutableBitmap::with_capacity(ca.len());
519
+ validity.extend_constant(ca.len(), true);
520
+ for i in invalid_indices {
521
+ validity.set(i, false);
522
+ }
523
+ ca.rechunk_mut();
524
+ Ok(ca.with_outer_validity(Some(validity.freeze())))
525
+ } else {
526
+ Ok(ca)
527
+ }
528
+ })
597
529
  }
598
530
 
599
- pub fn clear(&self) -> Self {
600
- self.df.borrow().clear().into()
531
+ pub fn clear(rb: &Ruby, self_: &Self) -> RbResult<Self> {
532
+ rb.enter_polars_df(|| Ok(self_.df.read().clear()))
601
533
  }
602
534
  }