Why is elementary chronic absenteeism stuck above pre-COVID?
A reproducibility log: the data this analysis touched, the queries it ran, the outside sources it leaned on, and the steps a third party would follow to re-run it.
Summary
Filtered chronic-absenteeism trajectories to elementary schools (grade_band ∈ ES, K8) and compared the recovery pattern with middle and high schools, showing elementary recovery is slower.
Data sources
- Database tableschools
One row per New York City public school (DBN, name, district, borough, school_type, grade_band, latest_enrollment, proclivity_decile, proclivity_score, closed_at, admission_category).
- Database tableschool_year_metrics
Long-format per-school per-year metric facts (school_dbn, year, metric_key, subgroup, value, suppressed). Loaded from DOE/NYSED public files via scripts/loaders/*.
Steps
Averages of chronic_absenteeism_rate by grade_band and year.
SELECT s.grade_band::text, m.year, AVG(m.value), COUNT(*) FROM schools s JOIN school_year_metrics m ON m.school_dbn=s.dbn WHERE m.metric_key='chronic_absenteeism_rate' AND m.subgroup='ALL' AND m.suppressed=false AND m.year IN ('2018-19','2021-22','2024-25') GROUP BY s.grade_band, m.year ORDER BY s.grade_band, m.year;
Caveats
Elementary attendance ≠ pre-K attendance; comparisons across grade bands assume similar measurement conventions, which is true within NYC.
Reproduce
Clone the repo, set DATABASE_URL to a Postgres with the project schema loaded, run `npx tsx scripts/loaders/<source>.ts` for any not-yet-loaded data, then issue the queries in the Steps section. The story page also lists the exact `metric_key`/`subgroup`/`year` filters used. Searchable by the answer's headline number — every figure is recomputable from the queries shown.
The recipe lives at data/stories/recipes.ts in the repo. Corrections welcome.