Five years later, where did the Renewal Schools land?
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
Joined the canonical 94-school 2014 Renewal Schools roster (from Chalkbeat's launch-day list) to our schools table with JS-side fuzzy matching (token Jaccard ≥ 0.5 on normalized names), then pulled per-school outcome trajectories for the 85/94 matches and compared with citywide and same-decile non-Renewal peers.
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/*.
- Database tableproclivity_score / proclivity_decile
Composite student-body-challenge score (lower = lower-need, higher = higher-need) built from % economically disadvantaged, % English Language Learners, % students with disabilities. Decile 1 = lowest-need; decile 10 = highest-need.
- Loader script
scripts/loaders/_renewal_query.tsHolds the verbatim 94-school roster, the JS-side fuzzy-matcher, and all comparison queries. Run with `npx tsx scripts/loaders/_renewal_query.ts > data/stories/renewal-data.json`.
- Cached output
data/stories/renewal-data.jsonCached output: 85 matches, per-school trajectories, peer comparison, summary stats.
- External datasetChalkbeat: "These 94 schools are set to receive extra funding and learning time" (2014-11-03)
Authoritative launch-day roster of all 94 Renewal Schools.
Steps
Pull all schools, then for each Renewal name compute a token Jaccard similarity (after lowercasing, expanding P.S./I.S./M.S./J.H.S., stripping leading zeros, dropping stopwords like 'school'/'academy'/'the'). Accept the highest score above 0.5.
function tokens(s) { /* normalize then split */ } function jaccard(a, b) { /* |A∩B| / |A∪B| */ } // Apply to every Renewal name × every row in the schools table; take argmax with score ≥ 0.5.Per-school chronic-absenteeism and ELA/math proficiency trajectory, comparing 2018-19 and 2024-25 for the 61 still-open matched Renewal schools.
SELECT s.dbn, s.name, MAX(CASE WHEN m.year='2018-19' THEN m.value END) AS y2018, MAX(CASE WHEN m.year='2024-25' THEN m.value END) AS y2024 FROM schools s JOIN school_year_metrics m ON m.school_dbn = s.dbn WHERE s.dbn = ANY($1::text[]) AND m.metric_key IN ('chronic_absenteeism_rate','ela_all_proficiency','math_all_proficiency') AND m.subgroup='ALL' AND m.suppressed=false GROUP BY s.dbn, s.name;Peer comparison: for each proclivity decile, average 2024-25 chronic absenteeism for non-Renewal schools, then compare with the matched-Renewal medians for that decile.
SELECT s.proclivity_decile, AVG(m.value), COUNT(*) FROM schools s JOIN school_year_metrics m ON m.school_dbn = s.dbn WHERE s.dbn NOT IN (<renewal DBNs>) AND m.metric_key='chronic_absenteeism_rate' AND m.subgroup='ALL' AND m.year='2024-25' AND m.suppressed=false GROUP BY s.proclivity_decile;Citywide ELA/math proficiency trend for the same years, so reported Renewal Δs can be put in citywide context.
Caveats
85/94 schools matched in our data; 9 likely closed before our metric coverage began. The 1900-01-01 sentinel date marks 'closed but year-of-closure unknown.' Test-proficiency comparisons span the 2022-23 state-test recalibration, which raised proficiency rates citywide — we report absolute changes against the citywide median so both lines absorb the recalibration. Our graduation-rate field doesn't cover 2014-15, so we can't directly test whether RAND's mid-program graduation-rate gains persisted.
References
Reproduce
Run `npx tsx scripts/loaders/_renewal_query.ts > data/stories/renewal-data.json` against a database with the project schema loaded. The script bundles the 94-school roster (copy-pasted from Chalkbeat 2014-11-03) and re-derives every figure in the story. To audit the fuzzy match, dump the `matches` array — each entry shows the Jaccard score so false positives are visible.
The recipe lives at data/stories/recipes.ts in the repo. Corrections welcome.