This commit is contained in:
Andras Schmelczer 2026-05-31 20:20:41 +01:00
parent 8688b7475e
commit e8345cbdc1
40 changed files with 1980 additions and 904 deletions

View file

@ -282,17 +282,23 @@ pub fn compute_crime_by_year(
for &row in matching_rows {
let postcode = data.postcode(row);
let Some(series_list) = crime_by_year.series_by_postcode.get(postcode) else {
continue;
};
// For every type the postcode reports, add its per-year counts.
// For types it doesn't report, treat the row as contributing 0 — so we
// bump the row count for *every* known type below.
for series in series_list {
let acc = &mut per_type_year_sums[series.type_idx as usize];
for point in &series.points {
*acc.entry(point.year).or_insert(0.0) += point.count as f64;
// A postcode absent from the by-year table has no recorded crime within
// 50m, so it contributes 0 to every type's per-year sum. It must still be
// counted in the denominator: the matching `(avg/yr)` stat counts those
// same zero-crime postcodes as 0.0 (crime_by_postcode.parquet has a dense
// row for every boundary postcode), so excluding them here would compute
// the chart over a smaller population and report a higher magnitude than
// the headline. Property postcodes are guaranteed to be boundary
// postcodes by the postcode-boundary-match validation, so "absent" means
// genuinely zero-crime, not missing data.
if let Some(series_list) = crime_by_year.series_by_postcode.get(postcode) {
// For every type the postcode reports, add its per-year counts.
for series in series_list {
let acc = &mut per_type_year_sums[series.type_idx as usize];
for point in &series.points {
*acc.entry(point.year).or_insert(0.0) += point.count as f64;
}
}
}
for c in per_type_row_counts.iter_mut() {