Skip to main content

polars_core/frame/
builder.rs

1use std::sync::Arc;
2
3use polars_arrow::array::builder::ShareStrategy;
4use polars_utils::IdxSize;
5
6use crate::frame::DataFrame;
7use crate::prelude::*;
8use crate::schema::Schema;
9use crate::series::builder::SeriesBuilder;
10
11pub struct DataFrameBuilder {
12    schema: Arc<Schema>,
13    builders: Vec<SeriesBuilder>,
14    height: usize,
15}
16
17impl DataFrameBuilder {
18    pub fn new(schema: Arc<Schema>) -> Self {
19        let builders = schema
20            .iter_values()
21            .map(|dt| SeriesBuilder::new(dt.clone()))
22            .collect();
23        Self {
24            schema,
25            builders,
26            height: 0,
27        }
28    }
29
30    pub fn reserve(&mut self, additional: usize) {
31        for builder in &mut self.builders {
32            builder.reserve(additional);
33        }
34    }
35
36    pub fn freeze(self) -> DataFrame {
37        let columns = self
38            .schema
39            .iter_names()
40            .zip(self.builders)
41            .map(|(n, b)| {
42                let s = b.freeze(n.clone());
43                assert_eq!(s.len(), self.height);
44                Column::from(s)
45            })
46            .collect();
47
48        // SAFETY: we checked the lengths and the names are unique because they
49        // come from Schema.
50        unsafe { DataFrame::new_unchecked(self.height, columns) }
51    }
52
53    pub fn freeze_reset(&mut self) -> DataFrame {
54        let columns = self
55            .schema
56            .iter_names()
57            .zip(&mut self.builders)
58            .map(|(n, b)| {
59                let s = b.freeze_reset(n.clone());
60                assert!(s.len() == self.height);
61                Column::from(s)
62            })
63            .collect();
64
65        // SAFETY: we checked the lengths and the names are unique because they
66        // come from Schema.
67        let out = unsafe { DataFrame::new_unchecked(self.height, columns) };
68        self.height = 0;
69        out
70    }
71
72    pub fn len(&self) -> usize {
73        self.height
74    }
75
76    pub fn is_empty(&self) -> bool {
77        self.height == 0
78    }
79
80    /// Extends this builder with the contents of the given dataframe. May panic
81    /// if other does not match the schema of this builder.
82    pub fn extend(&mut self, other: &DataFrame, share: ShareStrategy) {
83        self.subslice_extend(other, 0, other.height(), share);
84    }
85
86    /// Extends this builder with the contents of the given dataframe subslice.
87    /// May panic if other does not match the schema of this builder.
88    pub fn subslice_extend(
89        &mut self,
90        other: &DataFrame,
91        start: usize,
92        length: usize,
93        share: ShareStrategy,
94    ) {
95        let columns = other.columns();
96        assert!(self.builders.len() == columns.len());
97        for (builder, column) in self.builders.iter_mut().zip(columns) {
98            match column {
99                Column::Series(s) => {
100                    builder.subslice_extend(s, start, length, share);
101                },
102                Column::Scalar(sc) => {
103                    let len = sc.len().saturating_sub(start).min(length);
104                    let scalar_as_series = sc.scalar().clone().into_series(PlSmallStr::default());
105                    builder.subslice_extend_each_repeated(&scalar_as_series, 0, 1, len, share);
106                },
107            }
108        }
109
110        self.height += length.min(other.height().saturating_sub(start));
111    }
112
113    /// Extends this builder with the contents of the given dataframe subslice, repeating it `repeats` times.
114    /// May panic if other does not match the schema of this builder.
115    pub fn subslice_extend_repeated(
116        &mut self,
117        other: &DataFrame,
118        start: usize,
119        length: usize,
120        repeats: usize,
121        share: ShareStrategy,
122    ) {
123        let columns = other.columns();
124        assert!(self.builders.len() == columns.len());
125        for (builder, column) in self.builders.iter_mut().zip(columns) {
126            match column {
127                Column::Series(s) => {
128                    builder.subslice_extend_repeated(s, start, length, repeats, share);
129                },
130                Column::Scalar(sc) => {
131                    let len = sc.len().saturating_sub(start).min(length);
132                    let scalar_as_series = sc.scalar().clone().into_series(PlSmallStr::default());
133                    builder.subslice_extend_each_repeated(
134                        &scalar_as_series,
135                        0,
136                        1,
137                        len * repeats,
138                        share,
139                    );
140                },
141            }
142        }
143
144        self.height += length.min(other.height().saturating_sub(start)) * repeats;
145    }
146
147    /// Extends this builder with the contents of the given dataframe subslice.
148    /// Each element is repeated repeats times. May panic if other does not
149    /// match the schema of this builder.
150    pub fn subslice_extend_each_repeated(
151        &mut self,
152        other: &DataFrame,
153        start: usize,
154        length: usize,
155        repeats: usize,
156        share: ShareStrategy,
157    ) {
158        let columns = other.columns();
159        assert!(self.builders.len() == columns.len());
160        for (builder, column) in self.builders.iter_mut().zip(columns) {
161            match column {
162                Column::Series(s) => {
163                    builder.subslice_extend_each_repeated(s, start, length, repeats, share);
164                },
165                Column::Scalar(sc) => {
166                    let len = sc.len().saturating_sub(start).min(length);
167                    let scalar_as_series = sc.scalar().clone().into_series(PlSmallStr::default());
168                    builder.subslice_extend_each_repeated(
169                        &scalar_as_series,
170                        0,
171                        1,
172                        len * repeats,
173                        share,
174                    );
175                },
176            }
177        }
178
179        self.height += length.min(other.height().saturating_sub(start)) * repeats;
180    }
181
182    /// Extends this builder with the contents of the given dataframe at the given
183    /// indices. That is, `other[idxs[i]]` is appended to this builder in order,
184    /// for each i=0..idxs.len(). May panic if other does not match the schema
185    /// of this builder, or if the other dataframe is not rechunked.
186    ///
187    /// # Safety
188    /// The indices must be in-bounds.
189    pub unsafe fn gather_extend(
190        &mut self,
191        other: &DataFrame,
192        idxs: &[IdxSize],
193        share: ShareStrategy,
194    ) {
195        let columns = other.columns();
196        assert!(self.builders.len() == columns.len());
197        for (builder, column) in self.builders.iter_mut().zip(columns) {
198            match column {
199                Column::Series(s) => {
200                    builder.gather_extend(s, idxs, share);
201                },
202                Column::Scalar(sc) => {
203                    let scalar_as_series = sc.scalar().clone().into_series(PlSmallStr::default());
204                    builder.subslice_extend_each_repeated(
205                        &scalar_as_series,
206                        0,
207                        1,
208                        idxs.len(),
209                        share,
210                    );
211                },
212            }
213        }
214
215        self.height += idxs.len();
216    }
217
218    /// Extends this builder with the contents of the given dataframe at the given
219    /// indices. That is, `other[idxs[i]]` is appended to this builder in order,
220    /// for each i=0..idxs.len(). Out-of-bounds indices extend with nulls.
221    /// May panic if other does not match the schema of this builder, or if the
222    /// other dataframe is not rechunked.
223    pub fn opt_gather_extend(&mut self, other: &DataFrame, idxs: &[IdxSize], share: ShareStrategy) {
224        let mut trans_idxs = Vec::new();
225        let columns = other.columns();
226        assert!(self.builders.len() == columns.len());
227        for (builder, column) in self.builders.iter_mut().zip(columns) {
228            match column {
229                Column::Series(s) => {
230                    builder.opt_gather_extend(s, idxs, share);
231                },
232                Column::Scalar(sc) => {
233                    let scalar_as_series = sc.scalar().clone().into_series(PlSmallStr::default());
234                    // Reduce call overhead by transforming indices to 0/1 and dispatching to
235                    // opt_gather_extend on the scalar as series.
236                    for idx_chunk in idxs.chunks(4096) {
237                        trans_idxs.clear();
238                        trans_idxs.extend(
239                            idx_chunk
240                                .iter()
241                                .map(|idx| ((*idx as usize) >= sc.len()) as IdxSize),
242                        );
243                        builder.opt_gather_extend(&scalar_as_series, &trans_idxs, share);
244                    }
245                },
246            }
247        }
248
249        self.height += idxs.len();
250    }
251}