From: Ben Pfaff Date: Mon, 18 May 2026 19:10:47 +0000 (-0700) Subject: collect facetlevels X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8ed4c9483236b7e8be352da1ec7e30901aba9314;p=pspp collect facetlevels --- diff --git a/rust/pspp/src/spv/read/graph.rs b/rust/pspp/src/spv/read/graph.rs index 20b6bbd3f9..62b0f4e829 100644 --- a/rust/pspp/src/spv/read/graph.rs +++ b/rust/pspp/src/spv/read/graph.rs @@ -711,9 +711,10 @@ impl Visualization { rect.0[Axis2::Y].1 -= font_height; } + let facet_levels = graph.facet_levels(); let faceting = graph .faceting() - .map(|f| f.decode(&series)) + .map(|f| f.decode(&series, &facet_levels)) .unwrap_or_default(); let x_facets = FacetIterator::new(&faceting[Axis2::X]); @@ -805,11 +806,11 @@ impl Visualization { } let label = zip(&y_facet, &faceting[Axis2::Y]) - .map(|(value, series)| { + .map(|(value, facet)| { if let Some(label) = &value.label { label.clone() } else { - series.formatter.display(&value.value).to_string() + facet.series.formatter.display(&value.value).to_string() } }) .join(", "); @@ -922,8 +923,8 @@ impl Visualization { let mut grouped_data = BTreeMap::<_, Vec<_>>::new(); 'values: for i in 0.. { - for (index, variable) in faceting[Axis2::X].iter().enumerate() { - let Some(value) = variable.values.get(i) else { + for (index, facet) in faceting[Axis2::X].iter().enumerate() { + let Some(value) = facet.series.values.get(i) else { break 'values; }; let expected = x_facet[index]; @@ -931,8 +932,8 @@ impl Visualization { continue 'values; } } - for (index, variable) in faceting[Axis2::Y].iter().enumerate() { - let Some(value) = variable.values.get(i) else { + for (index, facet) in faceting[Axis2::Y].iter().enumerate() { + let Some(value) = facet.series.values.get(i) else { break 'values; }; let expected = y_facet[index]; @@ -2094,13 +2095,23 @@ impl VariableReference { fn decode_faceting<'a>( &self, series: &'a BTreeMap<&str, Series>, - faceting: &mut EnumMap>, - index: usize, + facet_levels: &HashMap, + faceting: &mut EnumMap>>, + axis_index: usize, + variable_index: &mut usize, ) { - let axis = if index % 2 == 0 { Axis2::X } else { Axis2::Y }; + let axis = if axis_index % 2 == 0 { + Axis2::X + } else { + Axis2::Y + }; + *variable_index += 1; let series = series.get(self.reference.as_str()).unwrap(); // XXX assert!(series.categorical); // XXX - faceting[axis].push(series); + faceting[axis].push(Facet { + series, + facet_level: facet_levels.get(&*variable_index).map(|v| &**v), + }); } } @@ -2943,6 +2954,15 @@ impl Graph { fn faceting(&self) -> Option<&Faceting> { self.children.iter().find_map(|child| child.faceting()) } + + fn facet_levels(&self) -> HashMap { + self.children + .iter() + .flat_map(|child| child.facet_layout()) + .flat_map(|facet_layout| facet_layout.facet_levels()) + .map(|facet_level| (facet_level.level, facet_level)) + .collect() + } } impl Locations for Graph { @@ -2987,6 +3007,13 @@ impl GraphChild { } } + fn facet_layout(&self) -> Option<&FacetLayout> { + match self { + Self::FacetLayout(facet_layout) => Some(facet_layout), + _ => None, + } + } + fn location(&self) -> Option<&Location> { match self { Self::Location(location) => Some(location), @@ -3382,6 +3409,12 @@ struct Faceting { children: Vec, } +#[derive(Clone, Debug)] +struct Facet<'a> { + series: &'a Series, + facet_level: Option<&'a FacetLevel>, +} + impl Faceting { /// Returns the faceting for each axis, as variable names from innermost to /// outermost. @@ -3391,10 +3424,15 @@ impl Faceting { /// /// This could decode layering into Z-axis faceting but the corpus doesn't /// contain any examples of layering. - fn decode<'a>(&self, series: &'a BTreeMap<&str, Series>) -> EnumMap> { + fn decode<'a>( + &self, + series: &'a BTreeMap<&str, Series>, + facet_levels: &HashMap, + ) -> EnumMap>> { let mut faceting = EnumMap::default(); + let mut variable_index = 0; for child in &self.children { - child.decode(series, &mut faceting); + child.decode(series, facet_levels, &mut faceting, &mut variable_index); } faceting } @@ -3412,11 +3450,13 @@ impl FacetingChild { fn decode<'a>( &self, series: &'a BTreeMap<&str, Series>, - faceting: &mut EnumMap>, + facet_levels: &HashMap, + faceting: &mut EnumMap>>, + variable_index: &mut usize, ) { match self { FacetingChild::Cross(cross) | FacetingChild::Dot(cross) => { - cross.decode(series, faceting, 0) + cross.decode(series, facet_levels, faceting, 0, variable_index) } FacetingChild::Layer(_layer) => { // XXX warn } @@ -3435,11 +3475,13 @@ impl Cross { fn decode<'a>( &self, series: &'a BTreeMap<&str, Series>, - faceting: &mut EnumMap>, + facet_levels: &HashMap, + faceting: &mut EnumMap>>, start_index: usize, + variable_index: &mut usize, ) { for (child, index) in self.children.iter().zip(start_index..) { - child.decode(series, faceting, index); + child.decode(series, facet_levels, faceting, index, variable_index); } } } @@ -3461,16 +3503,28 @@ impl CrossChild { fn decode<'a>( &self, series: &'a BTreeMap<&str, Series>, - faceting: &mut EnumMap>, + facet_levels: &HashMap, + faceting: &mut EnumMap>>, index: usize, + variable_index: &mut usize, ) { match self { CrossChild::Unity => (), - CrossChild::Nest(nest) => nest.decode(series, faceting, index), + CrossChild::Nest(nest) => { + nest.decode(series, facet_levels, faceting, index, variable_index) + } CrossChild::VariableReference(variable_reference) => { - variable_reference.decode_faceting(series, faceting, index); + variable_reference.decode_faceting( + series, + facet_levels, + faceting, + index, + variable_index, + ); + } + CrossChild::Cross(cross) => { + cross.decode(series, facet_levels, faceting, index, variable_index) } - CrossChild::Cross(cross) => cross.decode(series, faceting, index), } } } @@ -3486,11 +3540,19 @@ impl Nest { fn decode<'a>( &self, series: &'a BTreeMap<&'a str, Series>, - faceting: &mut EnumMap>, + facet_levels: &HashMap, + faceting: &mut EnumMap>>, index: usize, + variable_index: &mut usize, ) { for variable_reference in &self.variable_references { - variable_reference.decode_faceting(series, faceting, index); + variable_reference.decode_faceting( + series, + facet_levels, + faceting, + index, + variable_index, + ); } } } @@ -3515,6 +3577,12 @@ struct FacetLayout { children: Vec, } +impl FacetLayout { + fn facet_levels(&self) -> impl Iterator { + self.children.iter().flat_map(|child| child.facet_level()) + } +} + #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] enum FacetLayoutChild { @@ -3524,6 +3592,15 @@ enum FacetLayoutChild { Other, } +impl FacetLayoutChild { + fn facet_level(&self) -> Option<&FacetLevel> { + match self { + FacetLayoutChild::FacetLevel(facet_level) => Some(facet_level), + _ => None, + } + } +} + #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct SetCellProperties { @@ -4685,22 +4762,22 @@ fn diaconis_rule(iqr: f64, n: f64) -> Option { #[derive(Clone, Debug)] struct FacetIterator<'a> { - series: &'a [&'a Series], + facets: &'a [Facet<'a>], next: SmallVec<[usize; 2]>, remaining: usize, } impl<'a> FacetIterator<'a> { - fn new(series: &'a [&'a Series]) -> Self { + fn new(facets: &'a [Facet<'a>]) -> Self { Self { - series, - next: smallvec![0; series.len()], - remaining: if series.is_empty() { + facets, + next: smallvec![0; facets.len()], + remaining: if facets.is_empty() { 1 } else { - series + facets .into_iter() - .map(|series| series.unique_values.len()) + .map(|facet| facet.series.unique_values.len()) .product() }, } @@ -4718,12 +4795,12 @@ impl<'a> Iterator for FacetIterator<'a> { .next .iter() .copied() - .zip(self.series) - .map(|(index, series)| &series.unique_values[index]) + .zip(self.facets) + .map(|(index, facet)| &facet.series.unique_values[index]) .collect(); - for (index, series) in self.next.iter_mut().zip(self.series) { + for (index, facet) in self.next.iter_mut().zip(self.facets) { *index += 1; - if *index < series.unique_values.len() { + if *index < facet.series.unique_values.len() { break; } *index = 0;