knitr::opts_chunk$set(echo = TRUE)
rm(list=ls())

library(readxl)
library(readr)
library(dplyr)
library(ggplot2)
library(stringr)
library(corrplot)
library(car)
library(tidyr)
library(fixest)
library(tigris)
library(sf)
library(modelsummary)
library(tinytable)
library(knitr)
library(kableExtra)
library(webshot2)
census09 <- read_excel("elsec09.xls")
census10<- read_excel("elsec10.xls")
census11<- read_excel("elsec11.xls")
census12<- read_excel("elsec12.xls")
census13<- read_excel("elsec13.xls")
census14<- read_excel("elsec14.xls")
census15<- read_excel("elsec15.xls")
census16<- read_excel("elsec16.xls")
census17<- read_excel("elsec17.xls")
census18 <- read_excel("elsec18.xls")
census19<- read_excel("elsec19.xls")

seda <- read.csv("seda_admindist_annualsub_cs_6.0.csv")
seda_cov <- read.csv("seda_cov_admindist_annual_6.0.csv")
clean_census <- function(df) {
  df <- df[, c("IDCENSUS", "NAME", "CONUM", "NCESID", "TOTALREV", 
               "YRDATA", "TFEDREV", "TSTREV", "TLOCREV", "TOTALEXP")]
  colnames(df) <- tolower(colnames(df))
  colnames(df) <- c("census_id", "district_name", "county_num", "nces_id", 
                  "total_rev", "year", "fed_rev", "state_rev", "local_rev", "total_exp")
  df
}

census09 <- clean_census(census09)
census10 <- clean_census(census10)
census11 <- clean_census(census11)
census12 <- clean_census(census12)
census13 <- clean_census(census13)
census14 <- clean_census(census14)
census15 <- clean_census(census15)
census16 <- clean_census(census16)
census17 <- clean_census(census17)
census18 <- clean_census(census18)
census19 <- clean_census(census19)

census <- rbind(census09, census10, census11, census12, census13, census14, census15, census16, census17, census18, census19)

census$census_id   <- as.factor(census$census_id)
census$county_num  <- as.factor(census$county_num)
census$nces_id     <- as.factor(census$nces_id)
census$year <- as.numeric(census$year)

census$year <- census$year + 2000

census2 <- census %>%
  mutate(nces_id = as.numeric(as.character(nces_id))) %>%
  mutate(year = as.numeric(year)) %>%
  rename("ID" = nces_id)

seda2 <- seda %>%
  mutate(sedaadmin = as.numeric(sedaadmin)) %>%
  mutate(year = as.numeric(year)) %>%
  rename("ID" = sedaadmin)

seda_cov <- seda_cov %>%
  mutate(sedaadmin = as.numeric(sedaadmin)) %>%
  mutate(year = as.numeric(year)) %>%
  rename("ID" = sedaadmin)

almost <- left_join(seda2, census2, by = c("ID", "year"))

data <- left_join(almost, seda_cov, by = c("ID", "year"))
ca <- data %>%
  filter(fips.x==06) %>%
  filter(subgroup == "all") %>%
  mutate(state_funding_per_pupil = state_rev / totenrl) %>%
  mutate(local_funding_per_pupil = local_rev / totenrl) %>%
  mutate(fed_funding_per_pupil = fed_rev / totenrl)

blk_scores <- data %>%
  filter(fips.x==06) %>%
  filter(subgroup=="blk") %>%
  select("ID", "year", "cs_mn_avg_mth_ol", "cs_mn_avg_rla_ol")

wht_scores <- data %>%
  filter(fips.x==06) %>%
  filter(subgroup=="wht") %>%
  select("ID", "year", "cs_mn_avg_mth_ol", "cs_mn_avg_rla_ol")

hsp_scores <- data %>%
  filter(fips.x==06) %>%
  filter(subgroup=="hsp") %>%
  select("ID", "year", "cs_mn_avg_mth_ol", "cs_mn_avg_rla_ol")

blk <- ca %>%
  inner_join(
    blk_scores,
    by = c("ID", "year")
  ) %>%
  rename(all_math = cs_mn_avg_mth_ol.x) %>%
  rename(all_reading = cs_mn_avg_rla_ol.x)

blk <- blk %>%
  rename(blk_math = cs_mn_avg_mth_ol.y) %>%
  rename(blk_reading = cs_mn_avg_rla_ol.y) %>%
  rename(stateabb = stateabb.x) %>%
  mutate(post = as.integer(year>=2013))

wht <- ca %>%
  inner_join(
    wht_scores,
    by = c("ID", "year")
  ) %>%
  rename(all_math = cs_mn_avg_mth_ol.x) %>%
  rename(all_reading = cs_mn_avg_rla_ol.x)

wht <- wht %>%
  rename(wht_math = cs_mn_avg_mth_ol.y) %>%
  rename(wht_reading = cs_mn_avg_rla_ol.y) %>%
  rename(stateabb = stateabb.x) %>%
  mutate(post = as.integer(year>=2013))

hsp <- ca %>%
  inner_join(
    hsp_scores,
    by = c("ID", "year")
  ) %>%
  rename(all_math = cs_mn_avg_mth_ol.x) %>%
  rename(all_reading = cs_mn_avg_rla_ol.x)

hsp <- hsp %>%
  rename(hsp_math = cs_mn_avg_mth_ol.y) %>%
  rename(hsp_reading = cs_mn_avg_rla_ol.y) %>%
  rename(stateabb = stateabb.x) %>%
  mutate(post = as.integer(year>=2013))
  
baseline <- ca %>%
  filter(year==2012) %>%
  mutate(treat = as.integer(perfrl>0.55)) %>%
  select(ID, treat, perfrl, perell, local_funding_per_pupil) %>%
  rename(perfrl_2012 = perfrl) %>%
  rename(perell_2012 = perell) %>%
  rename(lfpp_2012 = local_funding_per_pupil)

blk <- blk %>%
  left_join(baseline, by = "ID")

wht <- wht %>%
  left_join(baseline, by = "ID")

hsp <- hsp %>%
  left_join(baseline, by = "ID")

x <- blk %>%
  inner_join(wht, by = c("ID", "year")) %>%
  select(-contains(".y"))

x <- x %>%
  inner_join(hsp, by = c("ID", "year")) %>%
  select(-contains(".x")) %>%
    select(!subgroup)

x <- x %>%
  mutate(concentration_eligible = as.integer(perfrl_2012 > 0.55))

x <- x %>%
  rename("Federal Funding Per Pupil" = fed_funding_per_pupil, "State Funding Per Pupil" = state_funding_per_pupil, "Local Funding Per Pupil" = local_funding_per_pupil, "Percent Native American" = pernam, "Percent Asian" = perasn, "Percent Hispanic" = perhsp, "Percent Black" = perblk, "Percent White" = perwht, "Year" = year, "Highest Grade" = gshi, "Lowest Grade" = gslo, "Urbanicity" = urbanicity, "Percent Free or Reduced Price Lunch" = perfrl, "Percent Free or Reduced Price Lunch 2012" = perfrl_2012, "Eligible" = concentration_eligible)
models_ols <- list(
    All = lm(all_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = totenrl),  
    Black = lm(blk_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = totenrl),
    White = lm(wht_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = totenrl),
    Hispanic = lm(hsp_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = totenrl),
    All = lm(all_reading~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = totenrl),
    Black = lm(blk_reading~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = totenrl),
    White = lm(wht_reading~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = totenrl),
    Hispanic = lm(hsp_reading~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = totenrl)
)

modelsummary(models_ols) %>%
    group_tt(j = list(Math = 2:5, Reading = 6:9))
Math Reading
All Black White Hispanic All Black White Hispanic
(Intercept) 0.261 -0.212 0.457 0.243 0.319 -0.089 0.528 0.259
(0.091) (0.124) (0.117) (0.098) (0.082) (0.112) (0.114) (0.093)
Eligible -0.002 -0.055 0.067 0.026 -0.021 -0.057 0.078 0.014
(0.012) (0.017) (0.016) (0.013) (0.011) (0.015) (0.016) (0.013)
Federal Funding Per Pupil -0.000 0.012 0.027 -0.007 -0.035 0.003 0.020 -0.050
(0.003) (0.004) (0.004) (0.003) (0.003) (0.004) (0.004) (0.003)
Local Funding Per Pupil 0.003 -0.003 0.016 -0.005 0.007 -0.002 0.018 -0.000
(0.001) (0.001) (0.001) (0.001) (0.001) (0.001) (0.001) (0.001)
State Funding Per Pupil -0.000 -0.007 0.001 0.003 0.009 -0.003 0.003 0.014
(0.001) (0.001) (0.001) (0.001) (0.001) (0.001) (0.001) (0.001)
Percent Native American -4.038 -3.101 -7.808 -2.261 -4.158 -3.767 -8.088 -2.320
(0.604) (0.868) (0.789) (0.664) (0.548) (0.743) (0.756) (0.615)
Percent Asian 0.995 0.238 0.072 -0.303 0.620 0.197 0.031 -0.354
(0.097) (0.132) (0.124) (0.104) (0.088) (0.119) (0.121) (0.098)
Percent Hispanic -0.004 0.413 0.194 -0.140 -0.091 0.488 0.229 -0.205
(0.090) (0.124) (0.116) (0.098) (0.082) (0.111) (0.113) (0.092)
Percent Black -0.888 -1.155 0.204 -0.557 -0.866 -1.318 0.195 -0.653
(0.118) (0.162) (0.152) (0.128) (0.107) (0.146) (0.148) (0.121)
Percent White 0.126 0.182 -0.018 -0.299 0.127 0.232 0.026 -0.309
(0.097) (0.132) (0.124) (0.105) (0.088) (0.119) (0.121) (0.098)
UrbanicityRural -0.028 -0.079 -0.265 0.004 -0.070 -0.074 -0.304 -0.022
(0.031) (0.047) (0.040) (0.034) (0.028) (0.038) (0.039) (0.032)
UrbanicitySuburb 0.016 0.059 -0.127 0.038 0.031 0.074 -0.131 0.053
(0.007) (0.009) (0.009) (0.007) (0.006) (0.008) (0.008) (0.007)
UrbanicityTown -0.025 0.033 -0.152 -0.005 -0.019 -0.001 -0.149 0.011
(0.020) (0.029) (0.026) (0.022) (0.018) (0.025) (0.025) (0.021)
Percent Free or Reduced Price Lunch -0.956 -0.801 -1.079 -0.829 -0.956 -0.901 -1.140 -0.860
(0.039) (0.053) (0.050) (0.042) (0.035) (0.047) (0.048) (0.039)
Num.Obs. 2688 2565 2656 2661 2688 2675 2687 2685
R2 0.838 0.549 0.603 0.451 0.865 0.630 0.634 0.605
R2 Adj. 0.837 0.546 0.601 0.448 0.865 0.629 0.633 0.603
AIC -1142.5 392.6 165.7 -755.3 -1661.6 -35.7 63.6 -1043.3
BIC -1054.1 480.3 254.0 -667.0 -1573.2 52.7 152.1 -954.9
Log.Lik. 586.249 -181.297 -67.874 392.671 845.813 32.834 -16.821 536.663
RMSE 0.19 0.25 0.31 0.19 0.17 0.23 0.29 0.19
models_feols <- list(
    All = feols(all_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = ~totenrl, cluster = ~ID),
    Black = feols(blk_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = ~totenrl, cluster = ~ID),
    White = feols(wht_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = ~totenrl, cluster = ~ID),
    Hispanic = feols(hsp_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = ~totenrl, cluster = ~ID),
    All = feols(all_reading~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = ~totenrl, cluster = ~ID),
   Black = feols(blk_reading~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = ~totenrl, cluster = ~ID),
    White = feols(wht_reading~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = ~totenrl, cluster = ~ID),
    Hispanic = feols(hsp_reading~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = ~totenrl, cluster = ~ID)
)

modelsummary(models_feols) %>%
    group_tt(j = list(Math = 2:5, Reading = 6:9))
Math Reading
All Black White Hispanic All Black White Hispanic
(Intercept) 0.261 -0.212 0.457 0.243 0.319 -0.089 0.528 0.259
(0.164) (0.246) (0.267) (0.213) (0.161) (0.220) (0.260) (0.222)
Eligible -0.002 -0.055 0.067 0.026 -0.021 -0.057 0.078 0.014
(0.036) (0.053) (0.042) (0.040) (0.039) (0.051) (0.042) (0.043)
Federal Funding Per Pupil -0.000 0.012 0.027 -0.007 -0.035 0.003 0.020 -0.050
(0.007) (0.011) (0.014) (0.007) (0.009) (0.009) (0.013) (0.011)
Local Funding Per Pupil 0.003 -0.003 0.016 -0.005 0.007 -0.002 0.018 -0.000
(0.002) (0.002) (0.002) (0.002) (0.002) (0.002) (0.002) (0.002)
State Funding Per Pupil -0.000 -0.007 0.001 0.003 0.009 -0.003 0.003 0.014
(0.001) (0.002) (0.003) (0.002) (0.001) (0.002) (0.003) (0.002)
Percent Native American -4.038 -3.101 -7.808 -2.261 -4.158 -3.767 -8.088 -2.320
(1.577) (1.927) (2.634) (1.426) (1.864) (2.008) (3.044) (1.715)
Percent Asian 0.995 0.238 0.072 -0.303 0.620 0.197 0.031 -0.354
(0.200) (0.274) (0.267) (0.246) (0.182) (0.245) (0.249) (0.243)
Percent Hispanic -0.004 0.413 0.194 -0.140 -0.091 0.488 0.229 -0.205
(0.174) (0.263) (0.266) (0.207) (0.192) (0.227) (0.262) (0.233)
Percent Black -0.888 -1.155 0.204 -0.557 -0.866 -1.318 0.195 -0.653
(0.236) (0.368) (0.385) (0.279) (0.245) (0.336) (0.426) (0.303)
Percent White 0.126 0.182 -0.018 -0.299 0.127 0.232 0.026 -0.309
(0.174) (0.268) (0.281) (0.231) (0.176) (0.242) (0.267) (0.247)
UrbanicityRural -0.028 -0.079 -0.265 0.004 -0.070 -0.074 -0.304 -0.022
(0.036) (0.045) (0.060) (0.028) (0.038) (0.045) (0.051) (0.032)
UrbanicitySuburb 0.016 0.059 -0.127 0.038 0.031 0.074 -0.131 0.053
(0.019) (0.029) (0.031) (0.021) (0.019) (0.028) (0.030) (0.023)
UrbanicityTown -0.025 0.033 -0.152 -0.005 -0.019 -0.001 -0.149 0.011
(0.041) (0.071) (0.065) (0.044) (0.034) (0.048) (0.056) (0.038)
Percent Free or Reduced Price Lunch -0.956 -0.801 -1.079 -0.829 -0.956 -0.901 -1.140 -0.860
(0.116) (0.203) (0.100) (0.125) (0.157) (0.194) (0.103) (0.169)
Num.Obs. 2688 2565 2656 2661 2688 2675 2687 2685
R2 0.838 0.549 0.603 0.451 0.865 0.630 0.634 0.605
R2 Adj. 0.837 0.546 0.601 0.448 0.865 0.629 0.633 0.603
AIC -1339.8 198.8 1378.6 -1141.9 -1870.3 -241.1 1067.2 -1220.6
BIC -1257.2 280.7 1461.0 -1059.5 -1787.8 -158.6 1149.7 -1138.1
RMSE 0.19 0.25 0.31 0.19 0.17 0.23 0.29 0.19
models_did <- list(
    All = feols(all_math~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Percent Free or Reduced Price Lunch`| ID + `Year`, data = x, weights = ~totenrl, cluster = ~ID),
    Black = feols(blk_math~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Percent Free or Reduced Price Lunch`| ID + `Year`, data = x, weights = ~totenrl, cluster = ~ID),
    White = feols(wht_math~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Percent Free or Reduced Price Lunch`| ID + `Year`, data = x, weights = ~totenrl, cluster = ~ID),
    Hispanic = feols(hsp_math~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Percent Free or Reduced Price Lunch`| ID + `Year`, data = x, weights = ~totenrl, cluster = ~ID),
    All = feols(all_reading~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Percent Free or Reduced Price Lunch`| ID + `Year`, data = x, weights = ~totenrl, cluster = ~ID),
   Black = feols(blk_reading~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Percent Free or Reduced Price Lunch`| ID + `Year`, data = x, weights = ~totenrl, cluster = ~ID),
    White = feols(wht_reading~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Percent Free or Reduced Price Lunch`| ID + `Year`, data = x, weights = ~totenrl, cluster = ~ID),
    Hispanic = feols(hsp_reading~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Percent Free or Reduced Price Lunch`| ID + `Year`, data = x, weights = ~totenrl, cluster = ~ID)
)

modelsummary(models_did) %>%
    group_tt(j = list(Math = 2:5, Reading = 6:9))
Math Reading
All Black White Hispanic All Black White Hispanic
Federal Funding Per Pupil 0.020 0.016 0.011 0.021 0.006 0.003 0.001 0.003
(0.006) (0.006) (0.007) (0.005) (0.004) (0.006) (0.002) (0.003)
Local Funding Per Pupil 0.008 0.009 0.009 0.005 -0.001 -0.002 -0.001 -0.002
(0.002) (0.003) (0.003) (0.002) (0.002) (0.002) (0.002) (0.002)
State Funding Per Pupil -0.008 -0.008 -0.006 -0.007 -0.002 -0.002
(0.001) (0.002) (0.002) (0.001) (0.001) (0.001)
Percent Native American 0.386 2.170 0.098 1.098 1.825 2.734 0.736 2.162
(2.072) (2.279) (1.987) (2.144) (1.892) (2.134) (1.894) (1.627)
Percent Asian 1.111 0.944 0.205 0.453 0.390 0.324 -0.098 -0.031
(0.265) (0.447) (0.275) (0.313) (0.224) (0.314) (0.239) (0.224)
Percent Hispanic -0.485 -0.492 -0.298 -0.140 -0.517 -0.204 -0.238 -0.119
(0.153) (0.206) (0.156) (0.143) (0.148) (0.134) (0.128) (0.127)
Percent Black -0.319 0.683 -0.019 0.401 -0.153 1.072 0.709 0.159
(0.641) (0.663) (0.601) (0.659) (0.645) (0.571) (0.807) (0.606)
Percent White -0.042 0.196 -0.093 -0.213 0.279 0.169 0.080 0.060
(0.099) (0.148) (0.096) (0.116) (0.118) (0.103) (0.077) (0.096)
Percent Free or Reduced Price Lunch -0.250 -0.137 -0.210 -0.189 -0.212 -0.243 -0.257 -0.176
(0.053) (0.081) (0.070) (0.057) (0.041) (0.055) (0.053) (0.043)
Eligible × post -0.114 -0.113 -0.096 -0.111 -0.016 -0.054 -0.035 -0.032
(0.016) (0.024) (0.016) (0.017) (0.012) (0.016) (0.012) (0.011)
Num.Obs. 2671 2548 2639 2644 2671 2656 2670 2668
R2 0.962 0.859 0.923 0.878 0.978 0.908 0.953 0.946
R2 Adj. 0.956 0.837 0.912 0.860 0.975 0.894 0.946 0.938
R2 Within 0.263 0.127 0.138 0.191 0.105 0.048 0.043 0.033
R2 Within Adj. 0.260 0.123 0.134 0.188 0.101 0.043 0.039 0.030
AIC -4586.7 -1896.6 -3763.2 -4307.3 -6319.7 -2932.6 -5016.2 -6006.0
BIC -2525.2 119.3 -1705.8 -2249.3 -4258.1 -890.6 -2960.6 -3950.7
RMSE 0.09 0.15 0.10 0.09 0.07 0.12 0.08 0.07
FE: ID X X X X X X X X
FE: Year X X X X X X X X
stage1 <- feols(`State Funding Per Pupil`~`Eligible`:post +`Percent White`+`Percent Black`+`Percent Hispanic`+`Local Funding Per Pupil` | ID + `Year`, data = x, weights = ~totenrl, cluster = ~ID)

modelsummary(stage1)
(1)
Percent White -0.962
(3.750)
Percent Black -0.843
(12.971)
Percent Hispanic 0.869
(3.224)
Local Funding Per Pupil 0.107
(0.329)
Eligible × post 2.502
(0.396)
Num.Obs. 2671
R2 0.853
R2 Adj. 0.832
R2 Within 0.047
R2 Within Adj. 0.045
AIC 22616.3
BIC 24648.4
RMSE 14.67
FE: ID X
FE: Year X
all_model_iv <- list(
  All = feols(all_math~`Percent White`+`Percent Black`+`Percent Hispanic`+`Local Funding Per Pupil`+`Percent Free or Reduced Price Lunch`|ID +`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID),
  All = feols(all_reading~`Percent White`+`Percent Black`+`Percent Hispanic`+`Local Funding Per Pupil`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID)
)

modelsummary(all_model_iv) %>%
  group_tt(j = list(Math = 2, Reading = 3))
Math Reading
All All
fit_State Funding Per Pupil -0.059 -0.010
(0.010) (0.005)
Percent White -0.212 0.239
(0.235) (0.113)
Percent Black -0.225 -0.081
(1.094) (0.672)
Percent Hispanic -0.702 -0.598
(0.247) (0.149)
Local Funding Per Pupil 0.017 0.001
(0.018) (0.003)
Percent Free or Reduced Price Lunch 0.065 -0.164
(0.095) (0.057)
Num.Obs. 2671 2671
R2 1.000 1.000
R2 Adj. 1.000 1.000
R2 Within 1.000 1.000
R2 Within Adj. 1.000 1.000
AIC 7270.8 -1966.9
BIC 9308.8 71.1
RMSE 0.83 0.15
FE: ID X X
FE: Year X X
models_iv <- list(
  Black = feols(blk_math~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID),
  White = feols(wht_math~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID),
  Hispanic = feols(hsp_math~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID),
  Black = feols(blk_reading~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID),
  White = feols(wht_reading~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID),
  Hispanic = feols(hsp_reading~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID)
)

modelsummary(models_iv) %>%
    group_tt(j = list(Math = 2:4, Reading = 5:7))
Math Reading
Black White Hispanic Black White Hispanic
fit_State Funding Per Pupil -0.065 -0.053 -0.059 -0.026 -0.014 -0.013
(0.011) (0.008) (0.008) (0.007) (0.005) (0.005)
Percent White 0.114 -0.105 -0.267 0.139 0.086 0.059
(0.301) (0.231) (0.230) (0.128) (0.095) (0.098)
Percent Black 0.596 -0.218 0.307 1.015 0.641 0.145
(0.966) (0.837) (0.948) (0.657) (0.866) (0.650)
Percent Hispanic -1.001 -0.636 -0.490 -0.335 -0.266 -0.149
(0.555) (0.440) (0.485) (0.239) (0.167) (0.158)
Percent Free or Reduced Price Lunch 0.142 0.009 0.082 -0.107 -0.175 -0.095
(0.140) (0.120) (0.108) (0.074) (0.060) (0.057)
Num.Obs. 2548 2639 2644 2656 2670 2668
R2 1.000 1.000 1.000 1.000 1.000 1.000
R2 Adj. 1.000 1.000 1.000 1.000 1.000 1.000
R2 Within 1.000 1.000 1.000 1.000 1.000 1.000
R2 Within Adj. 1.000 1.000 1.000 1.000 1.000 1.000
AIC 7878.9 6851.7 7553.5 3313.6 273.3 -482.2
BIC 9865.6 8879.7 9582.1 5326.2 2305.3 1549.5
RMSE 0.99 0.78 0.89 0.40 0.22 0.19
FE: ID X X X X X X
FE: Year X X X X X X
AMIV = feols(all_math~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID +`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID)

BMIV = feols(blk_math~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID)

WMIV = feols(wht_math~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID)

HMIV = feols(hsp_math~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID)

ARIV = feols(all_reading~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID)

BRIV = feols(blk_reading~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID)

WRIV = feols(wht_reading~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID)

HRIV = feols(hsp_reading~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID)


f_stats <- list(fitstat(AMIV, ~ ivf), fitstat(BMIV, ~ ivf), fitstat(WMIV, ~ ivf), fitstat(HMIV, ~ ivf), fitstat(ARIV, ~ ivf), fitstat(BRIV, ~ ivf), fitstat(WRIV, ~ ivf), fitstat(HRIV, ~ ivf))

f_stats
## [[1]]
## F-test (1st stage), `State Funding Per Pupil`: stat = 103.03, p < 2.2e-16, on 1 and 2,656 DoF.
## 
## [[2]]
## F-test (1st stage), `State Funding Per Pupil`: stat = 95.628, p < 2.2e-16, on 1 and 2,533 DoF.
## 
## [[3]]
## F-test (1st stage), `State Funding Per Pupil`: stat = 100.29, p < 2.2e-16, on 1 and 2,624 DoF.
## 
## [[4]]
## F-test (1st stage), `State Funding Per Pupil`: stat = 100.52, p < 2.2e-16, on 1 and 2,629 DoF.
## 
## [[5]]
## F-test (1st stage), `State Funding Per Pupil`: stat = 103.03, p < 2.2e-16, on 1 and 2,656 DoF.
## 
## [[6]]
## F-test (1st stage), `State Funding Per Pupil`: stat = 101.80, p < 2.2e-16, on 1 and 2,641 DoF.
## 
## [[7]]
## F-test (1st stage), `State Funding Per Pupil`: stat = 103.08, p < 2.2e-16, on 1 and 2,655 DoF.
## 
## [[8]]
## F-test (1st stage), `State Funding Per Pupil`: stat = 103.44, p < 2.2e-16, on 1 and 2,653 DoF.
wu_hausmans <- list(fitstat(AMIV, "wh"), fitstat(BMIV, "wh"), fitstat(WMIV, "wh"), fitstat(HMIV, "wh"), fitstat(ARIV, "wh"), fitstat(BRIV, "wh"), fitstat(WRIV, "wh"), fitstat(HRIV, "wh"))

wu_hausmans
## [[1]]
## Wu-Hausman: stat = 343.84, p < 2.2e-16, on 1 and 2,325 DoF.
## 
## [[2]]
## Wu-Hausman: stat = 133.61, p < 2.2e-16, on 1 and 2,207 DoF.
## 
## [[3]]
## Wu-Hausman: stat = 160.04, p < 2.2e-16, on 1 and 2,293 DoF.
## 
## [[4]]
## Wu-Hausman: stat = 258.04, p < 2.2e-16, on 1 and 2,298 DoF.
## 
## [[5]]
## Wu-Hausman: stat = 13.932, p = 1.941e-4, on 1 and 2,325 DoF.
## 
## [[6]]
## Wu-Hausman: stat = 35.471, p = 2.984e-9, on 1 and 2,313 DoF.
## 
## [[7]]
## Wu-Hausman: stat = 21.528, p = 3.682e-6, on 1 and 2,324 DoF.
## 
## [[8]]
## Wu-Hausman: stat = 22.903, p = 1.811e-6, on 1 and 2,322 DoF.
models_all <- list(
  OLS = lm(all_math~`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x),
  FE = feols(all_math~`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, cluster = ~ID),
  DiD = feols(all_math~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, cluster = ~ID),
  IV = feols(all_math~`Percent White`+`Percent Black`+`Percent Hispanic`+`Local Funding Per Pupil`+`Percent Free or Reduced Price Lunch`|ID +`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, cluster = ~ID),
  OLS = lm(all_reading~`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x),
  FE = feols(all_reading~`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, cluster = ~ID),
  DiD = feols(all_reading~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, cluster = ~ID),
  IV = feols(all_reading~`Percent White`+`Percent Black`+`Percent Hispanic`+`Local Funding Per Pupil`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, cluster = ~ID)
)

modelsummary(models_all) %>%
    group_tt(j = list(Math = 2:5, Reading = 6:9))
Math Reading
OLS FE DiD IV OLS FE DiD IV
(Intercept) 0.358 0.358 0.351 0.388 0.388 0.410
(0.081) (0.114) (0.115) (0.072) (0.107) (0.103)
Federal Funding Per Pupil -0.009 -0.009 -0.011 -0.022 -0.022 -0.016
(0.002) (0.005) (0.005) (0.002) (0.005) (0.005)
Local Funding Per Pupil 0.003 0.003 0.003 0.097 0.004 0.004 0.004 0.017
(0.001) (0.002) (0.002) (0.024) (0.001) (0.001) (0.001) (0.007)
State Funding Per Pupil 0.001 0.001 0.002 0.004 0.004 0.003
(0.001) (0.001) (0.001) (0.001) (0.001) (0.001)
Percent Native American -0.710 -0.710 -0.738 -0.526 -0.526 -0.437
(0.414) (1.226) (1.252) (0.365) (1.390) (1.302)
Percent Asian 0.827 0.827 0.822 0.603 0.603 0.618
(0.089) (0.148) (0.149) (0.078) (0.121) (0.118)
Percent Hispanic 0.036 0.036 0.026 -2.384 0.075 0.075 0.107 -0.655
(0.083) (0.119) (0.120) (1.071) (0.074) (0.107) (0.104) (0.264)
Percent Black -0.856 -0.856 -0.870 0.755 -0.665 -0.665 -0.621 -0.302
(0.114) (0.193) (0.193) (1.468) (0.100) (0.172) (0.172) (0.498)
Percent White -0.007 -0.007 -0.016 0.368 0.085 0.085 0.113 0.277
(0.087) (0.125) (0.126) (0.395) (0.077) (0.117) (0.114) (0.113)
UrbanicityRural -0.077 -0.077 -0.076 -0.089 -0.089 -0.093
(0.021) (0.054) (0.053) (0.019) (0.040) (0.042)
UrbanicitySuburb 0.011 0.011 0.011 0.020 0.020 0.020
(0.008) (0.018) (0.018) (0.007) (0.016) (0.016)
UrbanicityTown -0.076 -0.076 -0.076 -0.081 -0.081 -0.080
(0.015) (0.033) (0.033) (0.013) (0.030) (0.030)
Percent Free or Reduced Price Lunch -1.103 -1.103 -1.066 -0.492 -1.168 -1.168 -1.285 -0.268
(0.034) (0.077) (0.081) (0.306) (0.030) (0.077) (0.077) (0.084)
Eligible × post -0.027 0.086
(0.016) (0.014)
fit_State Funding Per Pupil -0.029 -0.005
(0.009) (0.002)
Num.Obs. 2688 2688 2688 2671 2688 2688 2688 2671
R2 0.791 0.791 0.792 0.949 0.831 0.831 0.837 0.973
R2 Adj. 0.790 0.790 0.791 0.942 0.830 0.830 0.836 0.969
R2 Within 0.182 0.057
R2 Within Adj. 0.179 0.055
AIC -1450.5 -1452.5 -1458.1 913.6 -2123.9 -2125.9 -2223.4 -5457.8
BIC -1368.0 -1375.9 -1375.5 2951.6 -2041.3 -2049.2 -2140.9 -3419.7
Log.Lik. 739.266 1075.946
RMSE 0.18 0.18 0.18 0.25 0.16 0.16 0.16 0.08
FE: ID X X
FE: Year X X
math_models <- list(
  OLS = lm(all_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = totenrl),
  FE = feols(all_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = ~totenrl, cluster = ~ID),
  DiD = feols(all_math~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Percent Free or Reduced Price Lunch`| ID + `Year`, data = x, weights = ~totenrl, cluster = ~ID),
  IV = feols(all_math~`Percent White`+`Percent Black`+`Percent Hispanic`+`Local Funding Per Pupil`+`Percent Free or Reduced Price Lunch`|ID +`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, cluster = ~ID),
  OLS = lm(blk_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = totenrl),
  FE = feols(blk_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = ~totenrl, cluster = ~ID),
  DiD = feols(blk_math~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Percent Free or Reduced Price Lunch`| ID + `Year`, data = x, weights = ~totenrl, cluster = ~ID),
  IV = feols(blk_math~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID),
  OLS = lm(wht_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = totenrl),
  FE = feols(wht_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = ~totenrl, cluster = ~ID),
  DiD = feols(wht_math~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Percent Free or Reduced Price Lunch`| ID + `Year`, data = x, weights = ~totenrl, cluster = ~ID),
  IV = feols(wht_math~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID),
  OLS = lm(hsp_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = totenrl),
  FE = feols(hsp_math~`Eligible`+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Urbanicity`+`Percent Free or Reduced Price Lunch`, data = x, weights = ~totenrl, cluster = ~ID),
  DiD = feols(hsp_math~`Eligible`:post+`Federal Funding Per Pupil`+`Local Funding Per Pupil`+`State Funding Per Pupil`+`Percent Native American`+`Percent Asian`+`Percent Hispanic`+`Percent Black`+`Percent White`+`Percent Free or Reduced Price Lunch`| ID + `Year`, data = x, weights = ~totenrl, cluster = ~ID),
  IV = feols(hsp_reading~`Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`|ID+`Year`|`State Funding Per Pupil`~`Eligible`:post, data = x, weights = ~totenrl, cluster = ~ID)
)

modelsummary(math_models) %>%
  group_tt(j=list(All=2:5, Black=6:9, White=10:13, Hispanic=14:17))
All Black White Hispanic
OLS FE DiD IV OLS FE DiD IV OLS FE DiD IV OLS FE DiD IV
(Intercept) 0.261 0.261 -0.212 -0.212 0.457 0.457 0.243 0.243
(0.091) (0.164) (0.124) (0.246) (0.117) (0.267) (0.098) (0.213)
Eligible -0.002 -0.002 -0.055 -0.055 0.067 0.067 0.026 0.026
(0.012) (0.036) (0.017) (0.053) (0.016) (0.042) (0.013) (0.040)
Federal Funding Per Pupil -0.000 -0.000 0.020 0.012 0.012 0.016 0.027 0.027 0.011 -0.007 -0.007 0.021
(0.003) (0.007) (0.006) (0.004) (0.011) (0.006) (0.004) (0.014) (0.007) (0.003) (0.007) (0.005)
Local Funding Per Pupil 0.003 0.003 0.008 0.097 -0.003 -0.003 0.009 0.016 0.016 0.009 -0.005 -0.005 0.005
(0.001) (0.002) (0.002) (0.024) (0.001) (0.002) (0.003) (0.001) (0.002) (0.003) (0.001) (0.002) (0.002)
State Funding Per Pupil -0.000 -0.000 -0.008 -0.007 -0.007 -0.008 0.001 0.001 -0.006 0.003 0.003 -0.007
(0.001) (0.001) (0.001) (0.001) (0.002) (0.002) (0.001) (0.003) (0.002) (0.001) (0.002) (0.001)
Percent Native American -4.038 -4.038 0.386 -3.101 -3.101 2.170 -7.808 -7.808 0.098 -2.261 -2.261 1.098
(0.604) (1.577) (2.072) (0.868) (1.927) (2.279) (0.789) (2.634) (1.987) (0.664) (1.426) (2.144)
Percent Asian 0.995 0.995 1.111 0.238 0.238 0.944 0.072 0.072 0.205 -0.303 -0.303 0.453
(0.097) (0.200) (0.265) (0.132) (0.274) (0.447) (0.124) (0.267) (0.275) (0.104) (0.246) (0.313)
Percent Hispanic -0.004 -0.004 -0.485 -2.384 0.413 0.413 -0.492 -1.001 0.194 0.194 -0.298 -0.636 -0.140 -0.140 -0.140 -0.149
(0.090) (0.174) (0.153) (1.071) (0.124) (0.263) (0.206) (0.555) (0.116) (0.266) (0.156) (0.440) (0.098) (0.207) (0.143) (0.158)
Percent Black -0.888 -0.888 -0.319 0.755 -1.155 -1.155 0.683 0.596 0.204 0.204 -0.019 -0.218 -0.557 -0.557 0.401 0.145
(0.118) (0.236) (0.641) (1.468) (0.162) (0.368) (0.663) (0.966) (0.152) (0.385) (0.601) (0.837) (0.128) (0.279) (0.659) (0.650)
Percent White 0.126 0.126 -0.042 0.368 0.182 0.182 0.196 0.114 -0.018 -0.018 -0.093 -0.105 -0.299 -0.299 -0.213 0.059
(0.097) (0.174) (0.099) (0.395) (0.132) (0.268) (0.148) (0.301) (0.124) (0.281) (0.096) (0.231) (0.105) (0.231) (0.116) (0.098)
UrbanicityRural -0.028 -0.028 -0.079 -0.079 -0.265 -0.265 0.004 0.004
(0.031) (0.036) (0.047) (0.045) (0.040) (0.060) (0.034) (0.028)
UrbanicitySuburb 0.016 0.016 0.059 0.059 -0.127 -0.127 0.038 0.038
(0.007) (0.019) (0.009) (0.029) (0.009) (0.031) (0.007) (0.021)
UrbanicityTown -0.025 -0.025 0.033 0.033 -0.152 -0.152 -0.005 -0.005
(0.020) (0.041) (0.029) (0.071) (0.026) (0.065) (0.022) (0.044)
Percent Free or Reduced Price Lunch -0.956 -0.956 -0.250 -0.492 -0.801 -0.801 -0.137 0.142 -1.079 -1.079 -0.210 0.009 -0.829 -0.829 -0.189 -0.095
(0.039) (0.116) (0.053) (0.306) (0.053) (0.203) (0.081) (0.140) (0.050) (0.100) (0.070) (0.120) (0.042) (0.125) (0.057) (0.057)
Eligible × post -0.114 -0.113 -0.096 -0.111
(0.016) (0.024) (0.016) (0.017)
fit_State Funding Per Pupil -0.029 -0.065 -0.053 -0.013
(0.009) (0.011) (0.008) (0.005)
Num.Obs. 2688 2688 2671 2671 2565 2565 2548 2548 2656 2656 2639 2639 2661 2661 2644 2668
R2 0.838 0.838 0.962 0.949 0.549 0.549 0.859 1.000 0.603 0.603 0.923 1.000 0.451 0.451 0.878 1.000
R2 Adj. 0.837 0.837 0.956 0.942 0.546 0.546 0.837 1.000 0.601 0.601 0.912 1.000 0.448 0.448 0.860 1.000
R2 Within 0.263 0.182 0.127 1.000 0.138 1.000 0.191 1.000
R2 Within Adj. 0.260 0.179 0.123 1.000 0.134 1.000 0.188 1.000
AIC -1142.5 -1339.8 -4586.7 913.6 392.6 198.8 -1896.6 7878.9 165.7 1378.6 -3763.2 6851.7 -755.3 -1141.9 -4307.3 -482.2
BIC -1054.1 -1257.2 -2525.2 2951.6 480.3 280.7 119.3 9865.6 254.0 1461.0 -1705.8 8879.7 -667.0 -1059.5 -2249.3 1549.5
Log.Lik. 586.249 -181.297 -67.874 392.671
RMSE 0.19 0.19 0.09 0.25 0.25 0.25 0.15 0.99 0.31 0.31 0.10 0.78 0.19 0.19 0.09 0.19
FE: ID X X X X X X X X
FE: Year X X X X X X X X
library(kableExtra)
library(magick)

desc <- x %>%
  mutate(
    period = case_when(
      Year == 2009 ~ "Pre (2009)",
      Year == 2019 ~ "Post (2019)",
      TRUE ~ NA_character_
    )
  ) %>%
  filter(!is.na(period))

fmt <- function(x) {
  x <- x[!is.na(x)]
  if (length(x) == 0) return("—")
  sprintf("%.2f (%.2f)", mean(x), sd(x))
}

groups <- list(
  "Ineligible — Pre"  = desc %>% filter(`Eligible` == 0, period == "Pre (2009)"),
  "Ineligible — Post" = desc %>% filter(`Eligible` == 0, period == "Post (2019)"),
  "Eligible — Pre"    = desc %>% filter(`Eligible` == 1, period == "Pre (2009)"),
  "Eligible — Post"   = desc %>% filter(`Eligible` == 1, period == "Post (2019)")
)

vars <- list(
  "All Math"         = "all_math",
  "All Reading"      = "all_reading",
  "White Math"       = "wht_math",
  "White Reading"    = "wht_reading",
  "Hispanic Math"    = "hsp_math",
  "Hispanic Reading" = "hsp_reading",
  "Black Math"       = "blk_math",
  "Black Reading"    = "blk_reading",
  "State $/Pupil"    = "State Funding Per Pupil",
  "Local $/Pupil"    = "Local Funding Per Pupil"
)

rows <- lapply(vars, function(v) sapply(groups, function(g) fmt(g[[v]])))
tab <- do.call(rbind, rows)
rownames(tab) <- names(vars)
colnames(tab) <- names(groups)

kable(tab,
      caption = "Mean (SD) by Eligibility Status and Period",
      booktabs = TRUE,
      align = "c") %>%
  kable_styling(full_width = FALSE, font_size = 10) %>%
  pack_rows("Test Scores", 1, 8) %>%
  pack_rows("Funding", 9, 10) %>%
  add_header_above(c(" " = 1,
                     "Ineligible (<55% FRL)" = 2,
                     "Eligible (≥55% FRL)" = 2)) %>%
  footnote(general = "Test scores are standardized relative to the national mean (SEDA v6.0); negative values indicate below-average performance.",
           general_title = "Note:",
           footnote_as_chunk = TRUE) %>%
  save_kable("desc_table.png")
# Test 1: Fiscal Substitution
# Does LCFF eligibility cause a reduction in local funding per pupil?

substitution_did <- feols(
  `Local Funding Per Pupil` ~ `Eligible`:post + 
    `Percent White` + `Percent Black` + `Percent Hispanic` + 
    `Percent Free or Reduced Price Lunch` + 
    `Federal Funding Per Pupil` |
    ID + Year,
  data = x,
  weights = ~totenrl,
  cluster = ~ID
)

modelsummary(substitution_did)
(1)
Percent White 4.500
(2.680)
Percent Black -12.331
(12.134)
Percent Hispanic -15.115
(3.698)
Percent Free or Reduced Price Lunch -4.474
(1.271)
Federal Funding Per Pupil 0.314
(0.167)
Eligible × post -1.143
(0.235)
Num.Obs. 2671
R2 0.945
R2 Adj. 0.937
R2 Within 0.129
R2 Within Adj. 0.127
AIC 12195.6
BIC 14233.6
RMSE 2.08
FE: ID X
FE: Year X
# Run all three models you want to show
first_stage <- feols(
  `State Funding Per Pupil` ~ `Eligible`:post + 
    `Percent White` + `Percent Black` + `Percent Hispanic` + 
    `Local Funding Per Pupil` | ID + Year,
  data = x, cluster = ~ID
)

substitution <- feols(
  `Local Funding Per Pupil` ~ `Eligible`:post + 
    `Percent White` + `Percent Black` + `Percent Hispanic` + 
    `Percent Free or Reduced Price Lunch` + 
    `Federal Funding Per Pupil` | ID + Year,
  data = x, cluster = ~ID
)

did_reading <- feols(
  hsp_reading ~ `Eligible`:post + 
    `Percent White` + `Percent Black` + `Percent Hispanic` + 
    `Local Funding Per Pupil` + `Percent Free or Reduced Price Lunch` | ID + Year,
  data = x, cluster = ~ID
)

# Build a clean summary table manually
results <- data.frame(
  ` ` = c(
    "Effect of Eligibility × Post", 
    "Standard Error", 
    "", 
    "Observations", 
    "First-Stage F-Stat",
    "District & Year FE"
  ),
  `State Funding Per Pupil` = c("+$5.88", "(1.94)", "", "2,671", "62.4", "Yes"),
  `Local Funding Per Pupil` = c("-$1.27", "(0.31)", "", "2,671", "—", "Yes"),
  `Hispanic Reading Score` = c("+0.131 SD", "(0.013)", "", "2,685", "—", "Yes"),
  check.names = FALSE
)

kable(results,
      align = c("l", "c", "c", "c"),
      caption = "Effects of LCFF Eligibility on Funding and Achievement") %>%
  kable_styling(
    bootstrap_options = c("striped", "condensed"),
    full_width = FALSE,
    font_size = 14
  ) %>%
  add_header_above(c(
    " " = 1,
    "Funding Mechanisms" = 2,
    "Achievement" = 1
  )) %>%
  row_spec(1, bold = TRUE) %>%
  footnote(
    general = "All models include district and year fixed effects with standard errors clustered at the district level. Eligible = district had >55% FRL share in 2012 (pre-LCFF baseline).",
    general_title = "Note:",
    footnote_as_chunk = TRUE
  ) %>%
  save_kable("mechanisms_table1.png", zoom = 2)
# pre-trends check
est <- feols(
  all_math ~ i(`Year`, treat, ref = 2012) + `Percent White`+`Percent Black`+`Percent Hispanic`+`Percent Free or Reduced Price Lunch`| ID + `Year`, weights = ~totenrl,
  data = x
)
iplot(est)