.iter()
.zip(presentation_indexes.iter())
{
- data_indexes[dim_index] = self.dimensions[dim_index].presentation_order[pindex];
+ data_indexes[dim_index] = self.dimensions[dim_index].ptod[pindex];
}
}
data_indexes
/// subcategories.
root: Group,
- /// Ordering of leaves for presentation.
+ /// Maps from an index in presentation order to a data index ("p" to "d").
///
- /// This is a permutation of `0..n` where `n` is the number of leaves. It
- /// maps from an index in presentation order to an index in data order.
- pub presentation_order: Vec<usize>,
+ /// This is a permutation of `0..n` where `n` is the number of leaves.
+ /// Given a [Leaf] that can be found as via `dimension.nth_leaf(leaf_idx)`,
+ /// the corresponding data index is `ptod[leaf_idx]`.
+ ptod: Vec<usize>,
/// Display.
pub hide_all_labels: bool,
}
-impl Dimension {
- /// Returns the root [Group] of the dimension.
- pub fn root(&self) -> &Group {
- &self.root
- }
-}
-
/// A vector of references to [Group]s.
///
/// Used to represent a sequence of groups along a [Path]. This is a [SmallVec]
/// Group indexes visited along a [Path].
pub type IndexVec = SmallVec<[usize; 4]>;
+/// Indicates that the argument to [Dimension::set_ptod] was not a valid
+/// permutation.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct InvalidPermutation;
+
impl Dimension {
/// Constructs a new [Dimension] with the given `root`.
pub fn new(root: Group) -> Self {
Dimension {
- presentation_order: (0..root.len()).collect(),
+ ptod: (0..root.len()).collect(),
root,
hide_all_labels: false,
}
}
+ /// Returns the root [Group] of the dimension.
+ pub fn root(&self) -> &Group {
+ &self.root
+ }
+
+ /// Returns the presentation-to-data index mapping.
+ pub fn ptod(&self) -> &[usize] {
+ &self.ptod
+ }
+
+ /// Returns this dimension with its presentation-to-data index mapping
+ /// replaced by `ptod`, which must be a permutation of `0..self.len()`.
+ pub fn set_ptod(&mut self, ptod: Vec<usize>) -> Result<(), InvalidPermutation> {
+ if ptod.len() != self.ptod.len() {
+ return Err(InvalidPermutation);
+ }
+
+ let mut seen = vec![false; ptod.len()];
+ for element in ptod.iter().copied() {
+ if element >= ptod.len() || seen[element] {
+ return Err(InvalidPermutation);
+ }
+ seen[element] = true;
+ }
+
+ self.ptod = ptod;
+ Ok(())
+ }
+
/// Returns this dimension with [Dimension::hide_all_labels] set to true.
pub fn with_all_labels_hidden(self) -> Self {
self.with_hide_all_labels(true)
let mut columns = Vec::new();
let mut height = 0;
for indexes in column_enumeration.iter() {
- let mut path = dimension
- .leaf_path(dimension.presentation_order[indexes[dim_index]])
- .unwrap();
+ let mut path = dimension.leaf_path(indexes[dim_index]).unwrap();
path.groups.retain(|group| group.show_label);
height = height.max(1 + path.groups.len());
columns.push(path);
/// Dimension with index {0} appears twice in table axes.
DuplicateDimensionIndex(usize),
+
+ /// Dimension {0} has invalid leaf index permutation (so categories may appear out of order).
+ InvalidPermutation(usize),
}
struct Context {
}
pub fn decode(&self, mut warn: &mut dyn FnMut(LightWarning)) -> PivotTable {
- dbg!(self);
let encoding = self.formats.encoding(warn);
let n1 = self.formats.n1();
let dimensions = self
.dimensions
.iter()
- .map(|d| {
+ .enumerate()
+ .map(|(dim_index, d)| {
let mut root = Group::new(d.name.decode(encoding, &footnotes, warn))
.with_show_label(!d.hide_dim_label);
+ let mut ptod = Vec::new();
for category in &d.categories {
- category.decode(encoding, &footnotes, &mut root, warn);
+ category.decode(encoding, &footnotes, &mut root, &mut ptod, warn);
}
- pivot::Dimension::new(root).with_hide_all_labels(d.hide_all_labels)
+ let mut dimension =
+ pivot::Dimension::new(root).with_hide_all_labels(d.hide_all_labels);
+ if dimension.set_ptod(ptod).is_err() {
+ warn(LightWarning::InvalidPermutation(dim_index));
+ };
+ dimension
})
.collect::<Vec<_>>();
let dimensions = match self.axes.decode(dimensions) {
encoding: &'static Encoding,
footnotes: &Footnotes,
group: &mut pivot::Group,
+ ptod: &mut Vec<usize>,
warn: &mut dyn FnMut(LightWarning),
) {
let name = self.name.decode(encoding, footnotes, warn);
match &self.child {
- Child::Leaf { leaf_index: _ } => {
+ Child::Leaf { leaf_index } => {
+ ptod.push(*leaf_index as usize);
group.push(pivot::Leaf::new(name));
}
Child::Group {
subcategories,
} => {
for subcategory in subcategories {
- subcategory.decode(encoding, footnotes, group, warn);
+ subcategory.decode(encoding, footnotes, group, ptod, warn);
}
}
Child::Group {
} => {
let mut subgroup = Group::new(name).with_label_shown();
for subcategory in subcategories {
- subcategory.decode(encoding, footnotes, &mut subgroup, warn);
+ subcategory.decode(encoding, footnotes, &mut subgroup, ptod, warn);
}
group.push(subgroup);
}
--- /dev/null
+ Chi-Square Tests
+╭────────────────────────────┬────────┬──┬─────────────────────┬────────────────────┬────────────────────┬─────────────────╮
+│ │ Value │df│Asymp. Sig. (2-sided)│Exact Sig. (2-sided)│Exact Sig. (1-sided)│Point Probability│
+├────────────────────────────┼────────┼──┼─────────────────────┼────────────────────┼────────────────────┼─────────────────┤
+│Pearson Chi-Square │4.496[a]│ 2│ .106│ .115│ │ │
+│Likelihood Ratio │ 4.646│ 2│ .098│ .108│ │ │
+│Fisher's Exact Test │ 4.446│ │ │ .108│ │ │
+│Linear-by-Linear Association│4.403[b]│ 1│ .036│ .047│ .026│ .015│
+│N of Valid Cases │ 60│ │ │ │ │ │
+╰────────────────────────────┴────────┴──┴─────────────────────┴────────────────────┴────────────────────┴─────────────────╯
+a. 0 cells (.0%) have expected count less than 5. The minimum expected count is 6.97.
+b. The standardized statistic is 2.098.
)
.write_options(writer, endian, ())?;
- let mut data_indexes = self.presentation_order.iter().copied();
+ let mut data_indexes = self.ptod().iter().copied();
for child in self.root().children() {
child.write_le(writer, &mut data_indexes)?;
}