use crate::model::{WacSegment, WacValue};
use bamcensus_core::{
model::identifier::{Geoid, GeoidType},
ops::agg::NumericAggregation,
};
use itertools::Itertools;
use kdam::BarExt;
use std::collections::HashMap;
pub fn aggregate_lodes_wac(
rows: &[(Geoid, Vec<WacValue>)],
target: GeoidType,
agg: NumericAggregation,
) -> Result<Vec<(Geoid, Vec<WacValue>)>, String> {
if target == GeoidType::Block {
return Ok(rows.to_vec());
}
let (geoid_oks, geoid_errs): (Vec<(Geoid, &Vec<WacValue>)>, Vec<String>) = rows
.iter()
.map(|(geoid, values)| {
let trunc_geoid = geoid.truncate_geoid_to_type(&target)?;
Ok((trunc_geoid, values))
})
.partition_result();
if !geoid_errs.is_empty() {
let msg = geoid_errs.into_iter().unique().take(5).join("\n");
return Err(format!(
"errors during aggregation. first 5 unique errors: \n{msg}"
));
}
let mut grouped: HashMap<Geoid, HashMap<WacSegment, Vec<f64>>> = HashMap::new();
let n_geoid_oks = geoid_oks.len();
let group_iter_desc = format!("LODES - geoids to {target}");
let pb1_builder = kdam::BarBuilder::default()
.total(n_geoid_oks)
.desc(group_iter_desc);
let mut pb1 = pb1_builder
.build()
.map_err(|e| format!("error building progress bar: {e}"))?;
for (geoid, values) in geoid_oks.into_iter() {
for wac in values.iter() {
match grouped.get_mut(&geoid) {
Some(inner) => match inner.get_mut(&wac.segment) {
Some(inner_vec) => {
inner_vec.push(wac.value);
}
None => {
let _ = inner.insert(wac.segment, vec![wac.value]);
}
},
None => {
let mut map = HashMap::new();
map.insert(wac.segment, vec![wac.value]);
grouped.insert(geoid.clone(), map);
}
}
}
pb1.update(1)
.map_err(|e| format!("error updating progress bar: {e}"))?;
}
eprintln!();
let n_grouped = grouped.len();
let reduce_desc = format!("LODES - aggregate by {agg}");
let pb2_builder = kdam::BarBuilder::default()
.total(n_grouped)
.desc(reduce_desc);
let mut pb2 = pb2_builder
.build()
.map_err(|e| format!("error building progress bar: {e}"))?;
let output: Result<Vec<(Geoid, Vec<WacValue>)>, String> = grouped
.into_iter()
.map(|(geoid, map)| {
let values = map
.into_iter()
.map(|(seg, values)| {
let value = agg.aggregate(&mut values.into_iter());
WacValue::new(seg, value)
})
.collect_vec();
pb2.update(1)
.map_err(|e| format!("error updating progress bar: {e}"))?;
Ok((geoid, values))
})
.collect::<Result<Vec<_>, _>>();
eprintln!();
output
}