1 Introduction

Chronic absenteeism — typically defined as missing 10% or more of school days in a year — is one of the strongest early-warning indicators available to educators. Research consistently links chronic absence to weaker academic performance, lower likelihood of being on-track to graduate, and increased risk of course failure and dropout, with effects that compound as students move through grade levels (NC DPI AttendNC Counts, 2026). The relationship is not symmetric across the student population either: national research from Attendance Works notes that chronic absenteeism disproportionately affects economically disadvantaged students, meaning attendance gaps often track existing equity gaps in a school system.

The COVID-19 pandemic sharply worsened the problem. National chronic absenteeism nearly doubled, and researchers at UNC’s Education Policy Initiative at Carolina (EPIC) and Duke’s Center for Child and Family Policy have documented similarly large increases using North Carolina administrative data, with some cohorts of persistently absent students growing several-fold between the pre- and post-pandemic periods. Notably, recent student-level analyses of North Carolina data (Swiderski, Fuller, & Bastian, 2025) find that while each additional absence is still associated with a measurable drop in math achievement, the size of that per-day penalty was somewhat smaller in the immediate post-pandemic years than before COVID — suggesting that the volume of absenteeism, not just its per-day effect, is driving much of the continued academic recovery gap. Taken together, this literature is the motivation for the analysis below: understanding where absenteeism remains highest post-COVID, and whether it has meaningfully changed over time, is a prerequisite for targeting policy responses.

(Sources to cite in text/references: NC DPI, “Chronic Absenteeism: A Review of the Research,” AttendNC Counts, 2026; Swiderski, T., Fuller, S.C., & Bastian, K.C. (2025). The Relationship Between Student Attendance and Achievement, Pre- and Post-COVID. AERA Open; EPIC at Carolina, “Stalled Progress: Post-Pandemic Absenteeism,” 2026.)

2 Data Overview

# Get a first look at the structure of the data before doing any wrangling.
# Run this chunk FIRST and check the console/knitted output for the actual
# column names -- the wrangling chunk below assumes some column names
# (rural/locale, income/economically disadvantaged, and a pre/post-COVID
# year or period variable) that may need to be renamed to match your data.

glimpse(school_sample)
## Rows: 2,116
## Columns: 6
## $ school_nam      <chr> "AydenGrifton High", "Ayden Middle", "A G Cox Middle",…
## $ pre_covid_pct   <dbl> 0.21230, 0.19365, 0.06415, 0.11570, 0.16995, 0.08895, …
## $ after_covid_pct <dbl> 0.2987926, 0.3206309, 0.1843114, 0.1849570, 0.3868627,…
## $ rural           <chr> "non-rural", "non-rural", "non-rural", "non-rural", "n…
## $ income          <chr> "low income", "low income", "high income", "low income…
## $ geometry        <POINT [US_survey_foot]> POINT (2467304 615181.5), POINT (24…
names(school_sample)
## [1] "school_nam"      "pre_covid_pct"   "after_covid_pct" "rural"          
## [5] "income"          "geometry"

Confirmed columns (from glimpse()): school_nam, pre_covid_pct, after_covid_pct, rural (already coded "rural"/"non-rural"), income (already coded "low income"/"high income"), geometry.

3 Data Wrangling

school_df <- school_sample %>%
  st_drop_geometry() %>%   # drop spatial geometry for the statistical tests; keep school_sample (with geometry) for mapping
  as_tibble() %>%
  mutate(
    rural_group = str_to_title(rural),     # "rural" / "non-rural" -> "Rural" / "Non-Rural"
    income_group = str_to_title(income),   # "low income" / "high income" -> "Low Income" / "High Income"
    # pre_covid_pct / after_covid_pct arrive as proportions (e.g., 0.2123) --
    # convert to whole percentages (e.g., 21.23) so plots/tables read naturally.
    pre_covid_pct = pre_covid_pct * 100,
    after_covid_pct = after_covid_pct * 100
  )

# --- Pre/Post-COVID chronic absenteeism, long format for plotting ---
school_long <- school_df %>%
  select(school_nam, rural_group, income_group,
         pre_covid = pre_covid_pct, post_covid = after_covid_pct) %>%
  pivot_longer(cols = c(pre_covid, post_covid),
               names_to = "period", values_to = "chronic_rate") %>%
  mutate(period = recode(period, pre_covid = "Pre-COVID", post_covid = "Post-COVID"))

post_covid_df <- school_df %>%
  filter(!is.na(after_covid_pct))

4 Post-COVID Chronic Absenteeism: Descriptive Statistics

post_covid_df %>%
  summarise(
    n_schools = n(),
    mean_rate = mean(after_covid_pct, na.rm = TRUE),
    median_rate = median(after_covid_pct, na.rm = TRUE),
    sd_rate = sd(after_covid_pct, na.rm = TRUE),
    min_rate = min(after_covid_pct, na.rm = TRUE),
    max_rate = max(after_covid_pct, na.rm = TRUE)
  )
## # A tibble: 1 × 6
##   n_schools mean_rate median_rate sd_rate min_rate max_rate
##       <int>     <dbl>       <dbl>   <dbl>    <dbl>    <dbl>
## 1      2116      27.8        26.8    11.8        5     80.5
post_covid_df %>%
  group_by(rural_group) %>%
  summarise(mean_rate = mean(after_covid_pct, na.rm = TRUE),
            median_rate = median(after_covid_pct, na.rm = TRUE),
            sd_rate = sd(after_covid_pct, na.rm = TRUE),
            n = n())
## # A tibble: 2 × 5
##   rural_group mean_rate median_rate sd_rate     n
##   <chr>           <dbl>       <dbl>   <dbl> <int>
## 1 Non-Rural        26.5        25.5    11.9  1193
## 2 Rural            29.4        28.5    11.4   923
post_covid_df %>%
  group_by(income_group) %>%
  summarise(mean_rate = mean(after_covid_pct, na.rm = TRUE),
            median_rate = median(after_covid_pct, na.rm = TRUE),
            sd_rate = sd(after_covid_pct, na.rm = TRUE),
            n = n())
## # A tibble: 2 × 5
##   income_group mean_rate median_rate sd_rate     n
##   <chr>            <dbl>       <dbl>   <dbl> <int>
## 1 High Income       23.8        23.1    9.97  1058
## 2 Low Income        31.7        30.7   12.2   1058

Across the state’s 2,116 sampled schools, the post-COVID chronic absenteeism rate averages approximately 27.8% (calculated from the balanced income-group means below), meaning that more than one in four students is missing over 10% of the school year in a typical school. The histogram below shows a roughly bell-shaped but right-skewed distribution, with a meaningful cluster of schools above 40%, indicating that the burden is not evenly spread — a small but real subset of schools face especially acute attendance crises. The group breakdowns already show a clear pattern before any formal testing: low-income schools average 31.71% versus 23.80% for high-income schools, and rural schools average 29.39% versus 26.49% for non-rural schools — both gaps are examined formally below.

4.1 Visualizations

ggplot(post_covid_df, aes(x = after_covid_pct)) +
  geom_histogram(binwidth = 2, fill = "#4B9CD3", color = "white") +
  labs(title = "Distribution of Post-COVID Chronic Absenteeism Rates",
       x = "Chronic Absenteeism Rate (%)", y = "Number of Schools") +
  theme_minimal()

ggplot(post_covid_df, aes(x = rural_group, y = after_covid_pct, fill = rural_group)) +
  geom_boxplot() +
  labs(title = "Post-COVID Chronic Absenteeism: Rural vs. Non-Rural Schools",
       x = NULL, y = "Chronic Absenteeism Rate (%)") +
  theme_minimal() + theme(legend.position = "none")

ggplot(post_covid_df, aes(x = income_group, y = after_covid_pct, fill = income_group)) +
  geom_boxplot() +
  labs(title = "Post-COVID Chronic Absenteeism: High-Income vs. Low-Income Schools",
       x = NULL, y = "Chronic Absenteeism Rate (%)") +
  theme_minimal() + theme(legend.position = "none")

tmap_mode("plot")

tm_shape(school_sample) +
  tm_polygons("after_covid_pct",
              title = "Chronic Absenteeism (%)",
              palette = "BuPu", style = "jenks") +
  tm_layout(main.title = "Post-COVID Chronic Absenteeism by School",
            legend.outside = TRUE)

5 Hypothesis Test 1: Rural vs. Non-Rural Schools (Post-COVID)

Hypotheses

  • \(H_0\): There is no difference in mean post-COVID chronic absenteeism rates between rural and non-rural schools (\(\mu_{rural} = \mu_{non-rural}\)).
  • \(H_A\): There is a difference in mean post-COVID chronic absenteeism rates between rural and non-rural schools (\(\mu_{rural} \neq \mu_{non-rural}\)).
# Normality check within each group
post_covid_df %>%
  group_by(rural_group) %>%
  summarise(shapiro_p = shapiro.test(after_covid_pct)$p.value)
## # A tibble: 2 × 2
##   rural_group shapiro_p
##   <chr>           <dbl>
## 1 Non-Rural    6.78e-14
## 2 Rural        2.06e- 9
ggplot(post_covid_df, aes(sample = after_covid_pct)) +
  stat_qq() + stat_qq_line() +
  facet_wrap(~rural_group) +
  labs(title = "QQ Plots: Normality Check by Rural Status") +
  theme_minimal()

# Equal variance check
var.test(after_covid_pct ~ rural_group, data = post_covid_df)
## 
##  F test to compare two variances
## 
## data:  after_covid_pct by rural_group
## F = 1.0891, num df = 1192, denom df = 922, p-value = 0.1705
## alternative hypothesis: true ratio of variances is not equal to 1
## 95 percent confidence interval:
##  0.9639197 1.2293918
## sample estimates:
## ratio of variances 
##           1.089115
# If normality assumption is reasonably met, use the two-sample t-test:
t.test(after_covid_pct ~ rural_group, data = post_covid_df, var.equal = FALSE)
## 
##  Welch Two Sample t-test
## 
## data:  after_covid_pct by rural_group
## t = -5.6789, df = 2020.7, p-value = 1.552e-08
## alternative hypothesis: true difference in means between group Non-Rural and group Rural is not equal to 0
## 95 percent confidence interval:
##  -3.906875 -1.901152
## sample estimates:
## mean in group Non-Rural     mean in group Rural 
##                26.48900                29.39302
# If normality is violated (common with skewed rate data), use the
# non-parametric Wilcoxon rank-sum test instead:
wilcox.test(after_covid_pct ~ rural_group, data = post_covid_df)
## 
##  Wilcoxon rank sum test with continuity correction
## 
## data:  after_covid_pct by rural_group
## W = 463871, p-value = 4.958e-10
## alternative hypothesis: true location shift is not equal to 0

Interpretation: The F-test for equal variances was not significant (F = 1.089, df = 1192, 922, p = 0.171), so the equal-variance assumption is reasonably met. The QQ plots show some deviation from normality in the tails (typical for a rate variable bounded at 0), but with a sample this large (n = 2,116 schools) the Central Limit Theorem makes the t-test robust to that deviation, and the non-parametric Wilcoxon rank-sum test was run alongside it as a check.

Both tests agree: the Welch two-sample t-test found a statistically significant difference (t = -5.679, df = 2020.7, p < .001), confirmed by the Wilcoxon rank-sum test (W = 463,871, p < .001). Rural schools have a mean post-COVID chronic absenteeism rate of 29.39%, compared to 26.49% for non-rural schools — a difference of about 2.9 percentage points (95% CI for the difference: 1.90 to 3.91 points). We reject \(H_0\): rural schools have significantly higher chronic absenteeism than non-rural schools post-COVID. The gap is real and statistically robust, though more modest in size than the income-based gap found below, suggesting rurality alone is a smaller driver of the state’s absenteeism problem than economic disadvantage.

6 Hypothesis Test 2: High-Income vs. Low-Income Schools (Post-COVID)

Hypotheses

  • \(H_0\): There is no difference in mean post-COVID chronic absenteeism rates between high-income and low-income schools (\(\mu_{high} = \mu_{low}\)).
  • \(H_A\): There is a difference in mean post-COVID chronic absenteeism rates between high-income and low-income schools (\(\mu_{high} \neq \mu_{low}\)).
post_covid_df %>%
  group_by(income_group) %>%
  summarise(shapiro_p = shapiro.test(after_covid_pct)$p.value)
## # A tibble: 2 × 2
##   income_group shapiro_p
##   <chr>            <dbl>
## 1 High Income   1.82e-11
## 2 Low Income    4.11e- 7
ggplot(post_covid_df, aes(sample = after_covid_pct)) +
  stat_qq() + stat_qq_line() +
  facet_wrap(~income_group) +
  labs(title = "QQ Plots: Normality Check by Income Group") +
  theme_minimal()

var.test(after_covid_pct ~ income_group, data = post_covid_df)
## 
##  F test to compare two variances
## 
## data:  after_covid_pct by income_group
## F = 0.66854, num df = 1057, denom df = 1057, p-value = 6.931e-11
## alternative hypothesis: true ratio of variances is not equal to 1
## 95 percent confidence interval:
##  0.5925632 0.7542534
## sample estimates:
## ratio of variances 
##          0.6685378
t.test(after_covid_pct ~ income_group, data = post_covid_df, var.equal = FALSE)
## 
##  Welch Two Sample t-test
## 
## data:  after_covid_pct by income_group
## t = -16.351, df = 2033.7, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group High Income and group Low Income is not equal to 0
## 95 percent confidence interval:
##  -8.864609 -6.965879
## sample estimates:
## mean in group High Income  mean in group Low Income 
##                  23.79811                  31.71336
wilcox.test(after_covid_pct ~ income_group, data = post_covid_df)
## 
##  Wilcoxon rank sum test with continuity correction
## 
## data:  after_covid_pct by income_group
## W = 339392, p-value < 2.2e-16
## alternative hypothesis: true location shift is not equal to 0

Interpretation: Unlike the rural comparison, the F-test for equal variances here was significant (F = 0.669, df = 1057, 1057, p < .001), meaning the income groups have unequal variances. This is exactly why the Welch t-test (which does not assume equal variances) was the appropriate choice rather than a pooled-variance t-test — and it was used by default here.

The results are decisive: the Welch two-sample t-test found a highly significant difference (t = -16.351, df = 2033.7, p < .001), and the Wilcoxon rank-sum test agrees (W = 339,392, p < .001). Low-income schools have a mean post-COVID chronic absenteeism rate of 31.71%, compared to 23.80% for high-income schools — a gap of about 7.9 percentage points (95% CI: 6.97 to 8.86 points), nearly three times the size of the rural/non-rural gap. We reject \(H_0\): low-income schools have significantly and substantially higher chronic absenteeism than high-income schools post-COVID. This is the single largest disparity identified in this analysis and squarely confirms the research finding that chronic absenteeism disproportionately burdens economically disadvantaged students.

7 Hypothesis Test 3: Pre-COVID vs. Post-COVID Chronic Absenteeism

Hypotheses

  • \(H_0\): There is no difference in mean chronic absenteeism rates between the pre-COVID and post-COVID periods (\(\mu_{pre} = \mu_{post}\)).
  • \(H_A\): Mean chronic absenteeism rates differ (likely increased) between the pre-COVID and post-COVID periods (\(\mu_{pre} \neq \mu_{post}\), or \(\mu_{post} > \mu_{pre}\) for a directional test).
# Because pre- and post-COVID rates are measured on the SAME schools, this is
# a paired comparison. Check normality of the paired differences.
paired_df <- school_df %>%
  filter(!is.na(pre_covid_pct), !is.na(after_covid_pct)) %>%
  mutate(diff = after_covid_pct - pre_covid_pct)

shapiro.test(paired_df$diff)
## 
##  Shapiro-Wilk normality test
## 
## data:  paired_df$diff
## W = 0.98631, p-value = 2.335e-13
ggplot(paired_df, aes(sample = diff)) +
  stat_qq() + stat_qq_line() +
  labs(title = "QQ Plot: Normality Check of Pre/Post-COVID Differences") +
  theme_minimal()

# Paired t-test if the differences are approximately normal:
t.test(paired_df$after_covid_pct, paired_df$pre_covid_pct, paired = TRUE)
## 
##  Paired t-test
## 
## data:  paired_df$after_covid_pct and paired_df$pre_covid_pct
## t = 81.898, df = 2115, p-value < 2.2e-16
## alternative hypothesis: true mean difference is not equal to 0
## 95 percent confidence interval:
##  12.55646 13.17256
## sample estimates:
## mean difference 
##        12.86451
# Wilcoxon signed-rank test if normality of differences is violated:
wilcox.test(paired_df$after_covid_pct, paired_df$pre_covid_pct, paired = TRUE)
## 
##  Wilcoxon signed rank test with continuity correction
## 
## data:  paired_df$after_covid_pct and paired_df$pre_covid_pct
## V = 2203080, p-value < 2.2e-16
## alternative hypothesis: true location shift is not equal to 0
school_long %>%
  ggplot(aes(x = period, y = chronic_rate, fill = period)) +
  geom_boxplot() +
  labs(title = "Chronic Absenteeism Before vs. After COVID-19",
       x = NULL, y = "Chronic Absenteeism Rate (%)") +
  theme_minimal() + theme(legend.position = "none")

Interpretation: The Shapiro-Wilk test and QQ plot of the paired differences show a right-skewed, heavy-tailed distribution rather than a perfectly normal one — but again, with n = 2,116 matched schools, the paired t-test is robust to this via the Central Limit Theorem, and the Wilcoxon signed-rank test was run as a distribution-free confirmation.

Both tests strongly agree: the paired t-test found a highly significant increase (t = 81.898, df = 2,115, p < .001), confirmed by the Wilcoxon signed-rank test (V = 2,203,080, p < .001). The mean chronic absenteeism rate rose by 12.86 percentage points from before to after COVID-19 (95% CI: 12.56 to 13.17 points) — from an estimated pre-COVID average of roughly 14.9% to a post-COVID average of roughly 27.8%, essentially nearly doubling. We reject \(H_0\): chronic absenteeism increased significantly, and by a large, policy-relevant margin, after the pandemic. This mirrors the national pattern reported by NC DPI and EPIC at Carolina, in which statewide chronic absenteeism rose from about 15% pre-pandemic to about 28% in 2021-22 and has not fully receded since.

8 Discussion and Policy Implications

This analysis produced three consistent, statistically robust findings. First, rural schools have significantly higher post-COVID chronic absenteeism than non-rural schools (29.39% vs. 26.49%, p < .001), though this gap (2.9 points) is comparatively modest. Second, low-income schools have significantly and substantially higher post-COVID chronic absenteeism than high-income schools (31.71% vs. 23.80%, p < .001) — a 7.9-point gap that is nearly three times the size of the rural/non-rural gap and the largest disparity identified in this report. Third, chronic absenteeism rose sharply and significantly across the board after COVID-19, increasing by an average of 12.86 percentage points per school (p < .001), consistent with the roughly-doubling pattern documented statewide and nationally.

Together, these results confirm the broader research finding that chronic absenteeism disproportionately affects economically disadvantaged students (NC DPI, 2026) — income, not rurality, is the stronger predictor of which North Carolina schools are struggling most with attendance post-COVID. This matters because even modest increases in absenteeism are associated with measurable achievement declines: North Carolina-specific research estimates roughly a 0.006 standard deviation drop in math achievement per additional day absent (Swiderski et al., 2025), meaning the 12.86-point statewide increase in chronic absenteeism translates into a meaningful, quantifiable drag on academic recovery — concentrated most heavily in low-income and, to a lesser extent, rural schools.

The pre/post-COVID comparison confirms the statewide and national pattern of a sharp, sustained rise in chronic absenteeism after the pandemic, consistent with EPIC at Carolina’s finding of stalled post-pandemic recovery in North Carolina schools.

8.1 Policy Recommendation

Given that the income-based gap is the largest and most policy-actionable finding in this report, the recommended policy response is:

Targeted, tiered attendance support prioritized for low-income schools, with a secondary focus on rural schools. This should be implemented through a Multi-Tiered System of Supports (MTSS) framework that pairs data-driven early identification of chronically absent students with academic, behavioral, and social-emotional supports (IES/REL, 2025). Within that framework, two specific, evidence-based interventions are worth piloting first in the highest-absenteeism low-income and rural districts identified by this analysis:

  1. Family engagement and home-visiting programs, modeled on Connecticut’s Learner Engagement and Attendance Program (LEAP), which used coordinated home visits to re-engage chronically absent students (Fordham Institute, 2024).
  2. Automated, low-cost family communication (text-message nudges), which IES/NCEE research has shown can meaningfully reduce chronic absence at scale and low marginal cost (Kurki, Heppen, & Brown, 2021) — a practical first step given the size of the post-COVID increase documented here.

Because the income gap (7.9 points) dwarfs the rural gap (2.9 points), funding and staffing for these interventions should be weighted toward low-income schools first, with rural schools as a secondary priority. The State Board should also require continued monitoring using the same rural/income breakdowns established in this report, so future State Board reviews can verify whether the post-COVID equity gap is actually narrowing rather than persisting.

9 References

  • NC Department of Public Instruction. (2026). Chronic Absenteeism: A Review of the Research, AttendNC Counts.
  • Swiderski, T., Fuller, S.C., & Bastian, K.C. (2025). The Relationship Between Student Attendance and Achievement, Pre- and Post-COVID. AERA Open.
  • Education Policy Initiative at Carolina (EPIC). (2026). Stalled Progress: Post-Pandemic Absenteeism in North Carolina.
  • Institute of Education Sciences (IES) / Regional Educational Laboratory Program. (2025). Helping Educators Address Chronic Absence Through Evidence-Based Interventions.
  • Fordham Institute. (2024). Sustainable, Promising Interventions to Reduce Chronic Absenteeism (2024 Wonkathon).
  • Kurki, A., Heppen, J.B., & Brown, S. (2021). How to Text Message Parents to Reduce Chronic Absence Using an Evidence-Based Approach (NCEE 2022-001). U.S. Department of Education.