feat: Implement Phase 4 dbt model restructuring
Create neighbourhood-centric dbt transformation layer: Staging (5 models): - stg_toronto__neighbourhoods - Neighbourhood dimension - stg_toronto__census - Census demographics - stg_toronto__crime - Crime statistics - stg_toronto__amenities - Amenity counts - stg_cmhc__zone_crosswalk - Zone-to-neighbourhood weights Intermediate (5 models): - int_neighbourhood__demographics - Combined census with quintiles - int_neighbourhood__housing - Housing + affordability indicators - int_neighbourhood__crime_summary - Aggregated crime with YoY - int_neighbourhood__amenity_scores - Per-capita amenity metrics - int_rentals__neighbourhood_allocated - CMHC via area weights Marts (5 models): - mart_neighbourhood_overview - Composite livability score - mart_neighbourhood_housing - Affordability index - mart_neighbourhood_safety - Crime rates per 100K - mart_neighbourhood_demographics - Income/age indices - mart_neighbourhood_amenities - Amenity index Closes #60, #61, #62, #63 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
81
dbt/models/intermediate/int_neighbourhood__crime_summary.sql
Normal file
81
dbt/models/intermediate/int_neighbourhood__crime_summary.sql
Normal file
@@ -0,0 +1,81 @@
|
||||
-- Intermediate: Aggregated crime by neighbourhood with YoY change
|
||||
-- Pivots crime types and calculates year-over-year trends
|
||||
-- Grain: One row per neighbourhood per year
|
||||
|
||||
with neighbourhoods as (
|
||||
select * from {{ ref('stg_toronto__neighbourhoods') }}
|
||||
),
|
||||
|
||||
crime as (
|
||||
select * from {{ ref('stg_toronto__crime') }}
|
||||
),
|
||||
|
||||
-- Aggregate crime types
|
||||
crime_by_year as (
|
||||
select
|
||||
neighbourhood_id,
|
||||
crime_year as year,
|
||||
sum(incident_count) as total_incidents,
|
||||
sum(case when crime_type = 'Assault' then incident_count else 0 end) as assault_count,
|
||||
sum(case when crime_type = 'Auto Theft' then incident_count else 0 end) as auto_theft_count,
|
||||
sum(case when crime_type = 'Break and Enter' then incident_count else 0 end) as break_enter_count,
|
||||
sum(case when crime_type = 'Robbery' then incident_count else 0 end) as robbery_count,
|
||||
sum(case when crime_type = 'Theft Over' then incident_count else 0 end) as theft_over_count,
|
||||
sum(case when crime_type = 'Homicide' then incident_count else 0 end) as homicide_count,
|
||||
avg(rate_per_100k) as avg_rate_per_100k
|
||||
from crime
|
||||
group by neighbourhood_id, crime_year
|
||||
),
|
||||
|
||||
-- Add year-over-year changes
|
||||
with_yoy as (
|
||||
select
|
||||
c.*,
|
||||
lag(c.total_incidents, 1) over (
|
||||
partition by c.neighbourhood_id
|
||||
order by c.year
|
||||
) as prev_year_incidents,
|
||||
round(
|
||||
(c.total_incidents - lag(c.total_incidents, 1) over (
|
||||
partition by c.neighbourhood_id
|
||||
order by c.year
|
||||
))::numeric /
|
||||
nullif(lag(c.total_incidents, 1) over (
|
||||
partition by c.neighbourhood_id
|
||||
order by c.year
|
||||
), 0) * 100,
|
||||
2
|
||||
) as yoy_change_pct
|
||||
from crime_by_year c
|
||||
),
|
||||
|
||||
crime_summary as (
|
||||
select
|
||||
n.neighbourhood_id,
|
||||
n.neighbourhood_name,
|
||||
n.geometry,
|
||||
n.population,
|
||||
|
||||
w.year,
|
||||
w.total_incidents,
|
||||
w.assault_count,
|
||||
w.auto_theft_count,
|
||||
w.break_enter_count,
|
||||
w.robbery_count,
|
||||
w.theft_over_count,
|
||||
w.homicide_count,
|
||||
w.avg_rate_per_100k,
|
||||
w.yoy_change_pct,
|
||||
|
||||
-- Crime rate per 100K population
|
||||
case
|
||||
when n.population > 0
|
||||
then round(w.total_incidents::numeric / n.population * 100000, 2)
|
||||
else null
|
||||
end as crime_rate_per_100k
|
||||
|
||||
from neighbourhoods n
|
||||
inner join with_yoy w on n.neighbourhood_id = w.neighbourhood_id
|
||||
)
|
||||
|
||||
select * from crime_summary
|
||||
Reference in New Issue
Block a user