new demo mode & tenure
This commit is contained in:
parent
7656f24544
commit
4a0f00f2a4
64 changed files with 2875 additions and 338 deletions
|
|
@ -255,6 +255,36 @@ pub fn compute_feature_stats(
|
|||
(numeric_features, enum_features_out)
|
||||
}
|
||||
|
||||
/// Count occurrences of each variant of a single enum feature across `rows`.
|
||||
///
|
||||
/// Unlike [`compute_feature_stats`], which is driven by the filter-matching
|
||||
/// subset, this lets a caller compute a count that should reflect a whole area
|
||||
/// regardless of the active filters (e.g. the council-house count, which is an
|
||||
/// attribute of the postcode itself, not of the currently filtered properties).
|
||||
/// Returns `None` if `feature_idx` is not an enum feature.
|
||||
pub fn compute_enum_feature_counts(
|
||||
rows: &[usize],
|
||||
data: &PropertyData,
|
||||
feature_idx: usize,
|
||||
) -> Option<HashMap<String, u64>> {
|
||||
let variants = data.enum_values.get(&feature_idx)?;
|
||||
let mut value_counts = vec![0u64; variants.len()];
|
||||
for &row in rows {
|
||||
let value = data.get_feature(row, feature_idx);
|
||||
if value.is_finite() && value >= 0.0 && (value as usize) < value_counts.len() {
|
||||
value_counts[value as usize] += 1;
|
||||
}
|
||||
}
|
||||
Some(
|
||||
value_counts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, &count)| count > 0)
|
||||
.map(|(idx, &count)| (variants[idx].clone(), count))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute property-weighted per-year crime means across the selection.
|
||||
///
|
||||
/// Each matching property contributes its postcode's per-year counts (incidents
|
||||
|
|
@ -447,3 +477,42 @@ pub fn compute_poi_feature_stats(
|
|||
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::consts::NAN_U16;
|
||||
|
||||
fn enum_data(values: &[u16]) -> PropertyData {
|
||||
let mut data = PropertyData::empty_for_tests();
|
||||
data.num_features = 1;
|
||||
data.num_numeric = 0; // single enum feature at index 0
|
||||
data.feature_data = values.to_vec();
|
||||
data.enum_values
|
||||
.insert(0, vec!["Yes".to_string(), "No".to_string()]);
|
||||
data
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_counts_tally_only_given_rows() {
|
||||
// Rows: Yes, No, Yes, <missing>
|
||||
let data = enum_data(&[0, 1, 0, NAN_U16]);
|
||||
|
||||
let all = compute_enum_feature_counts(&[0, 1, 2, 3], &data, 0).unwrap();
|
||||
assert_eq!(all.get("Yes"), Some(&2));
|
||||
assert_eq!(all.get("No"), Some(&1));
|
||||
|
||||
// A filter-matching subset would yield a different tally — confirming
|
||||
// the count is driven purely by the rows passed in (so callers can pass
|
||||
// the full area to make it filter-independent).
|
||||
let subset = compute_enum_feature_counts(&[0, 3], &data, 0).unwrap();
|
||||
assert_eq!(subset.get("Yes"), Some(&1));
|
||||
assert_eq!(subset.get("No"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_counts_none_for_non_enum_feature() {
|
||||
let data = enum_data(&[0, 1]);
|
||||
assert!(compute_enum_feature_counts(&[0, 1], &data, 1).is_none());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue