---
title: "Education Enrollment, 2024"
number-sections: true
---
::: {.callout-note icon=false}
## This Note
**Release:** US Census Bureau, 2024 School Enrollment data (CPS October supplement)
**Survey:** Current Population Survey — October School Enrollment Supplement, 2024
**Records pulled:** 99,045 (weighted to national population)
**Verdict:** Headline figures reproduced. Notable findings on gender gap, Black enrollment surge, and continued below-peak college recovery merit closer attention than a typical press release affords.
:::
## The Survey
The Census Bureau's school enrollment figures come from the **Current Population Survey (CPS) October Supplement** — a monthly household survey that, every October, adds a battery of questions about school attendance. The reference period is the survey week, typically the week containing October 15. Respondents report whether household members are currently enrolled, at what level, and whether full- or part-time.
This is not the same as counting students. It is counting *households reporting students*, weighted to the national population. The distinction matters and is addressed in the review section below.
## The Data Pull
All analysis below uses the Census Bureau's public API at `api.census.gov/data/2024/cps/school/oct`. Key variables:
| Variable | Description |
|---|---|
| `PESCHLVL` | School level: 1 = High school, 2 = College/university |
| `PESCH35` | Enrolled, age 3–5 (nursery / preK / kindergarten) |
| `PESCH614` | Enrolled, age 6–14 |
| `PESCHFT` | College enrollment type: 1 = Full-time, 2 = Part-time |
| `PESEX` | Sex: 1 = Male, 2 = Female |
| `PTDTRACE` | Race (detailed) |
| `PEHSPNON` | Hispanic origin: 1 = Hispanic, 2 = Non-Hispanic |
| `PWSSWGT` | Final person weight — use this for all population estimates |
```{python}
#| label: setup
#| code-summary: "Load data and compute weighted totals"
import os, json
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
# Load pre-pulled data (cached from API pull)
df24 = pd.read_pickle("data/processed/cps_school_2024.pkl")
df23 = pd.read_pickle("data/processed/cps_school_2023.pkl")
with open("data/processed/trend_2019_2024.json") as f:
trend = json.load(f)
W = "PWSSWGT"
def wtot(df, mask):
"""Weighted total for a boolean mask, in millions."""
return df[mask][W].sum() / 1e6
# 2024 core estimates
e35_24 = wtot(df24, df24["PESCH35"]==1)
e614_24 = wtot(df24, df24["PESCH614"]==1)
ehs_24 = wtot(df24, df24["PESCHLVL"]==1)
ecol_24 = wtot(df24, df24["PESCHLVL"]==2)
eft_24 = wtot(df24, (df24["PESCHLVL"]==2) & (df24["PESCHFT"]==1))
ept_24 = wtot(df24, (df24["PESCHLVL"]==2) & (df24["PESCHFT"]==2))
em_24 = wtot(df24, (df24["PESCHLVL"]==2) & (df24["PESEX"]==1))
ef_24 = wtot(df24, (df24["PESCHLVL"]==2) & (df24["PESEX"]==2))
ehisp_24 = wtot(df24, (df24["PESCHLVL"]==2) & (df24["PEHSPNON"]==1))
ewh_24 = wtot(df24, (df24["PESCHLVL"]==2) & (df24["PTDTRACE"]==1) & (df24["PEHSPNON"]==2))
ebl_24 = wtot(df24, (df24["PESCHLVL"]==2) & (df24["PTDTRACE"]==2))
eas_24 = wtot(df24, (df24["PESCHLVL"]==2) & (df24["PTDTRACE"]==4))
# 2023 comparison
ecol_23 = wtot(df23, df23["PESCHLVL"]==2)
ehs_23 = wtot(df23, df23["PESCHLVL"]==1)
e35_23 = wtot(df23, df23["PESCH35"]==1)
ebl_23 = wtot(df23, (df23["PESCHLVL"]==2) & (df23["PTDTRACE"]==2))
em_23 = wtot(df23, (df23["PESCHLVL"]==2) & (df23["PESEX"]==1))
ef_23 = wtot(df23, (df23["PESCHLVL"]==2) & (df23["PESEX"]==2))
```
## Headline Figures
The table below reproduces the top-line estimates from 2024 and compares them to 2023.
```{python}
#| label: tbl-headline
#| tbl-cap: "2024 CPS October School Enrollment — API-derived estimates"
#| code-summary: "Summary table"
rows = [
("Nursery / preschool / kindergarten (age 3–5)", e35_24, wtot(df23, df23["PESCH35"]==1)),
("Age 6–14", e614_24, wtot(df23, df23["PESCH614"]==1)),
("High school", ehs_24, ehs_23),
("College — total", ecol_24, ecol_23),
(" Full-time", eft_24, wtot(df23, (df23["PESCHLVL"]==2)&(df23["PESCHFT"]==1))),
(" Part-time", ept_24, wtot(df23, (df23["PESCHLVL"]==2)&(df23["PESCHFT"]==2))),
]
tbl = pd.DataFrame(rows, columns=["Category", "2024 (M)", "2023 (M)"])
tbl["Change (M)"] = tbl["2024 (M)"] - tbl["2023 (M)"]
tbl["Change (%)"] = (tbl["Change (M)"] / tbl["2023 (M)"]) * 100
for c in ["2024 (M)","2023 (M)","Change (M)"]:
tbl[c] = tbl[c].map("{:.3f}".format)
tbl["Change (%)"] = tbl["Change (%)"].map("{:+.1f}%".format)
tbl.style.set_properties(**{"text-align":"left"})
```
**Key takeaways from the table:**
- College enrollment ticked up **+0.7%** (+124,000) year-over-year, continuing a slow recovery from the 2022 trough.
- Full-time college enrollment grew +1.6%; part-time fell −2.4%, suggesting a shift toward full-time status rather than net new students.
- Nursery/preschool enrollment jumped **+3.9%** — the largest single-year increase in recent years.
- High school enrollment rose modestly (+1.4%).
## The Recovery Context
The press release headline of "college enrollment up" is accurate but incomplete without the five-year view. Enrollment peaked before COVID, dropped through 2022, and has only partially recovered.
```{python}
#| label: fig-trend
#| fig-cap: "College enrollment trend 2019–2024, by sex (CPS October, API-derived)"
#| code-summary: "College enrollment trend"
years = [int(y) for y in sorted(trend.keys())]
col_total = [trend[str(y)]["college"] for y in years]
female = [trend[str(y)]["female"] for y in years]
male = [trend[str(y)]["male"] for y in years]
fig = go.Figure()
fig.add_trace(go.Scatter(x=years, y=col_total, name="Total",
line=dict(color="#4f8ef7", width=2.5),
mode="lines+markers", marker=dict(size=7)))
fig.add_trace(go.Scatter(x=years, y=female, name="Female",
line=dict(color="#a47ee8", width=2, dash="solid"),
mode="lines+markers", marker=dict(size=6)))
fig.add_trace(go.Scatter(x=years, y=male, name="Male",
line=dict(color="#e06c4a", width=2, dash="solid"),
mode="lines+markers", marker=dict(size=6)))
fig.add_vline(x=2020, line_dash="dot", line_color="gray",
annotation_text="COVID-19", annotation_position="top right")
fig.update_layout(
xaxis_title="Year", yaxis_title="Enrolled (millions)",
legend=dict(orientation="h", y=1.1),
yaxis=dict(range=[6, 20]),
template="plotly_white", height=400,
margin=dict(t=40, b=40)
)
fig.show()
```
Total college enrollment in 2024 (**17.36M**) remains **4.0% below the 2019 level** (18.09M). The press release's positive YoY framing is accurate; the incomplete recovery is the more important story.
## The Gender Gap
The most striking structural feature of these data is not the headline number — it is the male-female enrollment split.
```{python}
#| label: fig-gender
#| fig-cap: "College enrollment by sex, 2019–2024 (CPS October)"
#| code-summary: "Gender gap visualization"
gender_ratio = [trend[str(y)]["female"] / (trend[str(y)]["female"] + trend[str(y)]["male"]) * 100
for y in years]
fig = make_subplots(rows=1, cols=2,
subplot_titles=("Enrollment by sex (M)", "Female share (%)"))
fig.add_trace(go.Bar(x=years, y=female, name="Female",
marker_color="#a47ee8"), row=1, col=1)
fig.add_trace(go.Bar(x=years, y=male, name="Male",
marker_color="#e06c4a"), row=1, col=1)
fig.add_trace(go.Scatter(x=years, y=gender_ratio, name="Female %",
line=dict(color="#a47ee8", width=2.5), mode="lines+markers",
marker=dict(size=7), showlegend=False), row=1, col=2)
fig.add_hline(y=50, line_dash="dot", line_color="gray", row=1, col=2)
fig.update_layout(barmode="group", template="plotly_white",
height=380, legend=dict(orientation="h", y=1.12),
margin=dict(t=50, b=40))
fig.show()
```
In 2024, **57.7% of college students are female** — 10.02M women vs. 7.34M men. Male enrollment has fallen every year since 2019, declining **9.9% over five years** (8.14M → 7.34M). Female enrollment has held roughly flat before ticking up in 2024.
The gender gap is mentioned in most enrollment press releases but rarely foregrounded. At 57.7/42.3, it is larger than most education-policy discussions acknowledge.
## The Black Enrollment Surge
```{python}
#| label: fig-black-enrollment
#| fig-cap: "Black and Hispanic college enrollment trend, 2019–2024"
#| code-summary: "Demographic enrollment trends"
black = [trend[str(y)]["black"] for y in years]
hisp = [trend[str(y)]["hisp"] for y in years]
fig = go.Figure()
fig.add_trace(go.Scatter(x=years, y=black, name="Black",
line=dict(color="#e06c4a", width=2.5), mode="lines+markers", marker=dict(size=7)))
fig.add_trace(go.Scatter(x=years, y=hisp, name="Hispanic",
line=dict(color="#4ec9a0", width=2.5), mode="lines+markers", marker=dict(size=7)))
fig.add_vline(x=2020, line_dash="dot", line_color="gray")
fig.update_layout(
xaxis_title="Year", yaxis_title="Enrolled (millions)",
template="plotly_white", height=380,
legend=dict(orientation="h", y=1.1),
margin=dict(t=40, b=40)
)
fig.show()
```
Black college enrollment rose **+10.3%** year-over-year (2.56M → 2.82M) — the largest single-year increase of any demographic group in the 2024 data. This is worth flagging as both encouraging and analytically suspect.
Encouraging: the 2022 figure (2.37M) represented a multi-year low; 2024's 2.82M is now above the pre-COVID 2019 level (2.77M).
Analytically suspect: a 10.3% single-year shift from a household survey with ~99,000 records is large. The CPS does not publish standard errors through the API. Without replicate-weight-based variance estimates, it is not possible to determine whether this jump is statistically significant or an artifact of sampling variability in a smaller demographic cell.
## Peer Review: What the Release Gets Right and Wrong
### Strengths of the methodology
**Continuity.** The CPS October supplement has asked comparable questions since 1947. The trend data exists and is publicly accessible. That is unusual and valuable.
**Timeliness.** Results are available within roughly six months of the reference week — faster than the ACS.
**Disaggregation.** The microdata support breakdowns by age, sex, race, Hispanic origin, family income, and school level simultaneously. The API exposes most of this without needing to download the full public-use file.
### Concerns
**1. October reference week is not enrollment.** The survey asks whether a person is currently enrolled in the week of the survey. Students who enrolled in September and dropped by October are missed. Students who enrolled late are missed. Community college students on non-standard calendars are systematically undercounted. A press release that says "X million are enrolled in college" should say "X million reported being enrolled during the week of October 15–21."
**2. Self-report with no institutional validation.** CPS enrollment figures are never cross-checked against IPEDS (the Integrated Postsecondary Education Data System), which is an institutional headcount census. IPEDS typically shows 1–2M more college students than CPS, partly due to the reference-period issue and partly due to definitional differences (IPEDS counts international students on visas; CPS sampling may undercount them). The press release never mentions this discrepancy.
**3. No standard errors reported.** The headline figures in a Census enrollment release are point estimates. The CPS uses a complex sample design with stratification and clustering; naively treating the data as simple random sample understates variance by 30–50% for subgroup estimates. The Bureau publishes replicate weights for proper variance estimation, but they are not accessible through the API. A release that says Black college enrollment rose 10.3% without a confidence interval is incomplete.
**4. Headcounts, not rates.** Reporting enrollment in millions obscures demographic change because the relevant population denominators are shifting. A press release noting that Black college enrollment hit 2.82M should also note the Black 18–24 population size in 2024 vs. 2023. The enrollment *rate* is the more informative statistic.
**5. Part-time decline understated.** Part-time enrollment fell 2.4%. This likely reflects either (a) financial pressure pushing students to full-time to finish faster or (b) part-time students dropping out entirely. These are opposite interpretations. The press release does not investigate.
### Suggested improvements
| Issue | Recommended fix |
|---|---|
| No uncertainty quantification | Publish 90% confidence intervals alongside point estimates; use BRR replicate weights |
| No IPEDS cross-check | Include a one-paragraph reconciliation with IPEDS Fall enrollment each year |
| Headcounts only | Add enrollment *rates* (enrolled / civilian noninstitutional population by age group) |
| October snapshot framing | State explicitly that figures reflect enrollment status in one reference week, not full-year enrollment |
| Gender gap underemphasized | The 57.7/42.3 female/male split warrants a dedicated section, not a footnote |
### Potential follow-on projects
- **Enrollment rates by age group**: pull population denominators from ACS 1-year and compute enrollment rates; cleaner than headcounts for trend analysis
- **CPS vs. IPEDS reconciliation**: compare CPS college headcounts to IPEDS Fall enrollment by year; document and explain the gap
- **Part-time to full-time shift**: disaggregate the full-time/part-time trend by demographic; test whether the shift is concentrated in specific groups
- **Gender gap over time**: the 57.7% female share is the highest on record; build a longer time series and test for structural break around 2010
## Summary
The 2024 CPS October School Enrollment data show college enrollment at 17.36M — up 0.7% from 2023 but 4.0% below the 2019 peak. Full-time enrollment grew while part-time declined. Female students now account for 57.7% of college enrollment, with male enrollment down nearly 10% since 2019. Black college enrollment surged 10.3% YoY to 2.82M, recovering above pre-COVID levels, though the absence of standard errors in the API release makes statistical significance uncertain.
The Bureau's methodology is sound for what it measures, but press release framing omits uncertainty quantification, institutional cross-validation, and enrollment rates. The API is well-structured and reproduces the headline figures; the replicate weights needed for proper variance estimation require the full microdata files.