antecedent_validate/
panel_slice.rs1use std::sync::Arc;
11
12use antecedent_data::{
13 Float64Column, OwnedColumn, OwnedColumnarStorage, PanelData, PanelUnit, TableView, TabularData,
14 TimeSeriesData, ValidityBitmap,
15};
16
17use crate::error::ValidationError;
18
19#[derive(Clone, Debug)]
22pub struct PanelSliceTemplate<'a> {
23 original: &'a PanelData,
24 baseline_ptrs: Vec<Option<*const u8>>,
26 original_ncols: usize,
28 offsets: Vec<usize>,
29 lengths: Vec<usize>,
30}
31
32impl<'a> PanelSliceTemplate<'a> {
33 pub fn from_panel(
41 original: &'a PanelData,
42 stacked: &TabularData,
43 ) -> Result<Self, ValidationError> {
44 let expected = original.total_rows();
45 if stacked.row_count() != expected {
46 return Err(ValidationError::data_msg(format!(
47 "stacked panel refute rows {} != panel total_rows {expected}",
48 stacked.row_count()
49 )));
50 }
51 let original_ncols = original.schema().len();
52 let mut offsets = Vec::with_capacity(original.unit_count());
53 let mut lengths = Vec::with_capacity(original.unit_count());
54 let mut offset = 0usize;
55 for u in original.units() {
56 let n = u.series.row_count();
57 if u.series.storage().columns().len() != original_ncols {
58 return Err(ValidationError::data_msg(
59 "panel unit column count disagrees with panel schema",
60 ));
61 }
62 offsets.push(offset);
63 lengths.push(n);
64 offset += n;
65 }
66 let baseline_ptrs = stacked.storage().columns().iter().map(payload_ptr).collect();
67 Ok(Self { original, baseline_ptrs, original_ncols, offsets, lengths })
68 }
69
70 pub fn apply_stacked(&self, stacked: &TabularData) -> Result<PanelData, ValidationError> {
80 if stacked.row_count() != self.original.total_rows() {
81 return Err(ValidationError::data_msg(format!(
82 "stacked panel refute rows {} != panel total_rows {}",
83 stacked.row_count(),
84 self.original.total_rows()
85 )));
86 }
87 let stacked_storage = stacked.storage();
88 let stacked_cols = stacked_storage.columns();
89 let schema = stacked_storage.schema().clone();
90 let mut units = Vec::with_capacity(self.original.unit_count());
91 for (unit_idx, u) in self.original.units().iter().enumerate() {
92 let start = self.offsets[unit_idx];
93 let len = self.lengths[unit_idx];
94 let mut cols = Vec::with_capacity(stacked_cols.len());
95 for (j, col) in stacked_cols.iter().enumerate() {
96 let reuse = j < self.original_ncols
97 && j < self.baseline_ptrs.len()
98 && payload_ptr(col) == self.baseline_ptrs[j];
99 if reuse {
100 cols.push(u.series.storage().columns()[j].clone());
101 } else {
102 cols.push(slice_column(col, start, len)?);
103 }
104 }
105 let mask = stacked_storage
106 .analysis_mask()
107 .map(|m| slice_validity(m, start, len))
108 .transpose()
109 .map_err(ValidationError::from)?;
110 let weights = stacked_storage
111 .weights()
112 .map(|w| Arc::<[f64]>::from(w[start..start + len].to_vec()));
113 let storage = OwnedColumnarStorage::try_new(schema.clone(), cols, mask, weights)
114 .map_err(ValidationError::from)?;
115 let series = TimeSeriesData::try_new(storage, u.series.time_index().clone())
116 .map_err(ValidationError::from)?;
117 units.push(PanelUnit { unit_id: u.unit_id, series });
118 }
119 PanelData::try_new(Arc::from(units)).map_err(ValidationError::from)
120 }
121}
122
123fn payload_ptr(col: &OwnedColumn) -> Option<*const u8> {
124 match col {
125 OwnedColumn::Float64(c) => {
126 let s = c.values.as_slice();
127 (!s.is_empty()).then(|| s.as_ptr().cast())
128 }
129 OwnedColumn::Int64(c) => (!c.values.is_empty()).then(|| c.values.as_ptr().cast()),
130 OwnedColumn::Boolean(c) => (!c.values.is_empty()).then(|| c.values.as_ptr().cast()),
131 OwnedColumn::Categorical(c) => (!c.codes.is_empty()).then(|| c.codes.as_ptr().cast()),
132 OwnedColumn::Timestamp(c) => (!c.values_ns.is_empty()).then(|| c.values_ns.as_ptr().cast()),
133 OwnedColumn::FixedVector(c) => (!c.values.is_empty()).then(|| c.values.as_ptr().cast()),
134 }
135}
136
137fn slice_validity(
138 src: &ValidityBitmap,
139 start: usize,
140 len: usize,
141) -> Result<ValidityBitmap, antecedent_data::DataError> {
142 let mut bytes = vec![0u8; len.div_ceil(8)];
143 for i in 0..len {
144 if src.is_valid(start + i) {
145 bytes[i / 8] |= 1 << (i % 8);
146 }
147 }
148 ValidityBitmap::from_bytes(bytes, len)
149}
150
151fn slice_column(
152 col: &OwnedColumn,
153 start: usize,
154 len: usize,
155) -> Result<OwnedColumn, ValidationError> {
156 let end = start
157 .checked_add(len)
158 .ok_or(ValidationError::NotApplicable { message: "panel slice out of range" })?;
159 match col {
160 OwnedColumn::Float64(c) => {
161 if end > c.values.len() {
162 return Err(ValidationError::NotApplicable { message: "panel slice out of range" });
163 }
164 let values: Arc<[f64]> = Arc::from(c.values.as_slice()[start..end].to_vec());
165 let validity =
166 slice_validity(&c.validity, start, len).map_err(ValidationError::from)?;
167 Ok(OwnedColumn::Float64(
168 Float64Column::new(c.id, values, validity).map_err(ValidationError::from)?,
169 ))
170 }
171 _ => Err(ValidationError::NotApplicable {
172 message: "panel refute slice requires float64 columns",
173 }),
174 }
175}
176
177#[cfg(test)]
179pub(crate) fn copy_all_panel_from_stacked(
180 original: &PanelData,
181 stacked: &TabularData,
182) -> Result<PanelData, ValidationError> {
183 let expected = original.total_rows();
184 if stacked.row_count() != expected {
185 return Err(ValidationError::data_msg(format!(
186 "stacked panel refute rows {} != panel total_rows {expected}",
187 stacked.row_count()
188 )));
189 }
190 let mut offset = 0usize;
191 let mut units = Vec::with_capacity(original.unit_count());
192 for u in original.units() {
193 let n = u.series.row_count();
194 let mut cols = Vec::with_capacity(stacked.storage().columns().len());
195 for col in stacked.storage().columns() {
196 cols.push(slice_column(col, offset, n)?);
197 }
198 let mask = stacked
199 .storage()
200 .analysis_mask()
201 .map(|m| slice_validity(m, offset, n))
202 .transpose()
203 .map_err(ValidationError::from)?;
204 let weights =
205 stacked.storage().weights().map(|w| Arc::<[f64]>::from(w[offset..offset + n].to_vec()));
206 let storage =
207 OwnedColumnarStorage::try_new(stacked.storage().schema().clone(), cols, mask, weights)
208 .map_err(ValidationError::from)?;
209 let series = TimeSeriesData::try_new(storage, u.series.time_index().clone())
210 .map_err(ValidationError::from)?;
211 units.push(PanelUnit { unit_id: u.unit_id, series });
212 offset += n;
213 }
214 PanelData::try_new(Arc::from(units)).map_err(ValidationError::from)
215}