polars_core/frame/
horizontal.rs1use polars_error::{PolarsResult, polars_err};
2
3use super::Column;
4use crate::datatypes::AnyValue;
5use crate::frame::DataFrame;
6use crate::frame::validation::validate_columns_slice;
7
8impl DataFrame {
9 pub unsafe fn hstack_mut_unchecked(&mut self, columns: &[Column]) -> &mut Self {
21 if self.shape() == (0, 0)
22 && let Some(c) = columns.first()
23 {
24 unsafe { self.set_height(c.len()) };
25 }
26
27 unsafe { self.columns_mut() }.extend_from_slice(columns);
28
29 if cfg!(debug_assertions) {
30 if let err @ Err(_) = validate_columns_slice(self.height(), self.columns()) {
31 let initial_width = self.width() - columns.len();
32 unsafe { self.columns_mut() }.truncate(initial_width);
33 err.unwrap();
34 }
35 }
36
37 self
38 }
39
40 pub fn hstack_mut(&mut self, columns: &[Column]) -> PolarsResult<&mut Self> {
55 if self.shape() == (0, 0)
56 && let Some(c) = columns.first()
57 {
58 unsafe { self.set_height(c.len()) };
59 }
60
61 unsafe { self.columns_mut() }.extend_from_slice(columns);
62
63 if let err @ Err(_) = validate_columns_slice(self.height(), self.columns()) {
64 let initial_width = self.width() - columns.len();
65 unsafe { self.columns_mut() }.truncate(initial_width);
66 err?;
67 }
68
69 Ok(self)
70 }
71}
72
73pub fn concat_df_horizontal(
77 dfs: &[DataFrame],
78 check_duplicates: bool,
79 strict: bool,
80 unit_length_as_scalar: bool,
81) -> PolarsResult<DataFrame> {
82 let output_height = dfs
83 .iter()
84 .map(|df| df.height())
85 .max()
86 .ok_or_else(|| polars_err!(ComputeError: "cannot concat empty dataframes"))?;
87
88 let owned_df;
89
90 let mut out_width = 0;
91
92 let all_equal_height = dfs.iter().all(|df| {
93 out_width += df.width();
94 df.height() == output_height
95 });
96
97 let dfs = if !all_equal_height {
99 if strict {
100 return Err(
101 polars_err!(ShapeMismatch: "cannot concat dataframes with different heights in 'strict' mode"),
102 );
103 }
104 out_width = 0;
105
106 owned_df = dfs
107 .iter()
108 .cloned()
109 .map(|mut df| {
110 out_width += df.width();
111 let h = df.height();
112
113 if h != output_height {
114 if unit_length_as_scalar && h == 1 {
115 unsafe { df.columns_mut() }.iter_mut().for_each(|c| {
118 let Column::Scalar(s) = c else {
119 panic!("only supported for scalars");
120 };
121
122 *c = Column::Scalar(s.resize(output_height));
123 });
124 } else {
125 let diff = output_height - h;
126
127 unsafe { df.columns_mut() }.iter_mut().for_each(|c| {
130 *c = c.extend_constant(AnyValue::Null, diff).unwrap();
131 });
132 }
133 unsafe {
134 df.set_height(output_height);
135 }
136 }
137
138 df
139 })
140 .collect::<Vec<_>>();
141 owned_df.as_slice()
142 } else {
143 dfs
144 };
145
146 let mut acc_cols = Vec::with_capacity(out_width);
147
148 for df in dfs {
149 acc_cols.extend(df.columns().iter().cloned());
150 }
151
152 let df = if check_duplicates {
153 DataFrame::new(output_height, acc_cols)?
154 } else {
155 unsafe { DataFrame::new_unchecked(output_height, acc_cols) }
156 };
157
158 Ok(df)
159}
160
161#[cfg(test)]
162mod tests {
163 use polars_error::PolarsError;
164
165 #[test]
166 fn test_hstack_mut_empty_frame_height_validation() {
167 use crate::frame::DataFrame;
168 use crate::prelude::{Column, DataType};
169 let mut df = DataFrame::empty();
170 let result = df.hstack_mut(&[
171 Column::full_null("a".into(), 1, &DataType::Null),
172 Column::full_null("b".into(), 3, &DataType::Null),
173 ]);
174
175 assert!(
176 matches!(result, Err(PolarsError::ShapeMismatch(_))),
177 "expected shape mismatch error"
178 );
179
180 assert_eq!(df.width(), 0);
182 }
183}