Projecting enrollment with enrollcast

knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
library(enrollcast)

The grade progression ratio method

The cohort survival / grade progression ratio method projects enrollment by asking: of the students in grade g this year, how many appear in grade g+1 next year? That ratio captures net retention, migration, and repetition. enrollcast estimates these ratios from history and applies them forward with a matrix projection.

A small district

history <- data.frame(
  year = rep(2021:2023, each = 3),
  grade = factor(rep(c("K", "1", "2"), 3), levels = c("K", "1", "2")),
  enrollment = c(100, 90, 80, 110, 95, 88, 120, 99, 91)
)
history

Step 1: progression ratios

ratios <- progression_ratios(history, method = "mean")
ratios

Ratios are calculated only from observed adjacent calendar-year pairs. If the history contains a gap, progression_ratios() warns and uses the adjacent pairs on either side without constructing a transition across the missing period. A history with no adjacent year pair cannot produce progression ratios and is rejected. Gap detection examines the complete supplied history before n_years selects the most recent adjacent transitions, so an older gap still warns even when it lies outside the selected transitions.

The ratios sit on the sub-diagonal of the projection matrix; the entry-grade row is zero because entry is supplied exogenously.

progression_matrix(ratios)

Step 2: project forward

The entry grade (kindergarten here) has no feeder grade, so you supply its future values — for example from a birth-cohort or housing model.

base <- history[history$year == 2023, c("grade", "enrollment")]
projection <- project_enrollment(
  base = base,
  ratios = ratios,
  horizon = 3,
  entry = c(125, 130, 128),
  start_year = 2023
)
projection

Stitching history and projection

project_enrollment() returns projected years only. Combine with history for plotting:

observed <- data.frame(
  year = history$year,
  grade = as.character(history$grade),
  enrollment = history$enrollment
)
combined <- rbind(observed, projection)
head(combined)

Modeling a school modernization swing

A school temporarily relocated during modernization typically sees depressed enrollment that recovers after it returns. swing_schedule() builds a per-year projection schedule: enrollment is held flat during the swing, scaled by recovery multipliers for a few years, then projected normally.

Schedules may contain NA or NaN matrix coefficients. These values are preserved and warned about rather than imputed. A missing coefficient can make its output grade missing. If that missing enrollment remains after the entry grade is replaced, the next matrix multiplication spreads missingness to all grade results because zero times a missing value is still missing. A non-NULL entry value then restores only the entry grade.

depressed <- c(K = 80, `1` = 66, `2` = 60)
schedule <- swing_schedule(ratios,
  horizon = 6, swing_years = 2,
  recovery = c(1.10, 1.10, 1.05), entry = 130
)
project_enrollment(depressed, schedule = schedule, start_year = 2023)


Try the enrollcast package in your browser

Any scripts or data that you put into this service are public.

enrollcast documentation built on Aug. 27, 2026, 1:08 a.m.