Skip to main content

polars_core/frame/
builder.rs

1use std::sync::Arc;
2
3use 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_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_repeated(&scalar_as_series, 0, 1, len * repeats, share);
134                },
135            }
136        }
137
138        self.height += length.min(other.height().saturating_sub(start)) * repeats;
139    }
140
141    /// Extends this builder with the contents of the given dataframe subslice.
142    /// Each element is repeated repeats times. May panic if other does not
143    /// match the schema of this builder.
144    pub fn subslice_extend_each_repeated(
145        &mut self,
146        other: &DataFrame,
147        start: usize,
148        length: usize,
149        repeats: usize,
150        share: ShareStrategy,
151    ) {
152        let columns = other.columns();
153        assert!(self.builders.len() == columns.len());
154        for (builder, column) in self.builders.iter_mut().zip(columns) {
155            match column {
156                Column::Series(s) => {
157                    builder.subslice_extend_each_repeated(s, start, length, repeats, share);
158                },
159                Column::Scalar(sc) => {
160                    let len = sc.len().saturating_sub(start).min(length);
161                    let scalar_as_series = sc.scalar().clone().into_series(PlSmallStr::default());
162                    builder.subslice_extend_repeated(&scalar_as_series, 0, 1, len * repeats, share);
163                },
164            }
165        }
166
167        self.height += length.min(other.height().saturating_sub(start)) * repeats;
168    }
169
170    /// Extends this builder with the contents of the given dataframe at the given
171    /// indices. That is, `other[idxs[i]]` is appended to this builder in order,
172    /// for each i=0..idxs.len(). May panic if other does not match the schema
173    /// of this builder, or if the other dataframe is not rechunked.
174    ///
175    /// # Safety
176    /// The indices must be in-bounds.
177    pub unsafe fn gather_extend(
178        &mut self,
179        other: &DataFrame,
180        idxs: &[IdxSize],
181        share: ShareStrategy,
182    ) {
183        let columns = other.columns();
184        assert!(self.builders.len() == columns.len());
185        for (builder, column) in self.builders.iter_mut().zip(columns) {
186            match column {
187                Column::Series(s) => {
188                    builder.gather_extend(s, idxs, share);
189                },
190                Column::Scalar(sc) => {
191                    let scalar_as_series = sc.scalar().clone().into_series(PlSmallStr::default());
192                    builder.subslice_extend_repeated(&scalar_as_series, 0, 1, idxs.len(), share);
193                },
194            }
195        }
196
197        self.height += idxs.len();
198    }
199
200    /// Extends this builder with the contents of the given dataframe at the given
201    /// indices. That is, `other[idxs[i]]` is appended to this builder in order,
202    /// for each i=0..idxs.len(). Out-of-bounds indices extend with nulls.
203    /// May panic if other does not match the schema of this builder, or if the
204    /// other dataframe is not rechunked.
205    pub fn opt_gather_extend(&mut self, other: &DataFrame, idxs: &[IdxSize], share: ShareStrategy) {
206        let mut trans_idxs = Vec::new();
207        let columns = other.columns();
208        assert!(self.builders.len() == columns.len());
209        for (builder, column) in self.builders.iter_mut().zip(columns) {
210            match column {
211                Column::Series(s) => {
212                    builder.opt_gather_extend(s, idxs, share);
213                },
214                Column::Scalar(sc) => {
215                    let scalar_as_series = sc.scalar().clone().into_series(PlSmallStr::default());
216                    // Reduce call overhead by transforming indices to 0/1 and dispatching to
217                    // opt_gather_extend on the scalar as series.
218                    for idx_chunk in idxs.chunks(4096) {
219                        trans_idxs.clear();
220                        trans_idxs.extend(
221                            idx_chunk
222                                .iter()
223                                .map(|idx| ((*idx as usize) >= sc.len()) as IdxSize),
224                        );
225                        builder.opt_gather_extend(&scalar_as_series, &trans_idxs, share);
226                    }
227                },
228            }
229        }
230
231        self.height += idxs.len();
232    }
233}