Session 06 - Regression Discontinuity Design (RDD)

Resources

  1. R packages for RDD: https://eric.ed.gov/?id=EJ1141190
  2. Short overview of RDD with examples: https://www.youtube.com/watch?v=tWRsYWSP3fM
  3. A good introduction to local regression methods is also available in Chapters 1 and 2 of Keele, Luke. 2008. "Semiparametric regression for the social sciences". Chichester, UK: John Wiley and Sons.
  4. local regression introduction: https://www.youtube.com/watch?v=Vf7oJ6z2LCc
  5. A more technical introduction: https://www.youtube.com/watch?v=ncF7ArjJFqM&t=3s
  6. Imbens & Kalyanaraman (2012) Optimal bandwidth choice: https://academic.oup.com/restud/article/79/3/933/1533189






Example 2. Measuring the long term effects of a conditional cash transfer program on educational achievement

Imagine that you work as a technical adviser for the Ministry of Education in your country. You are tasked to assess whether a Conditional Cash Transfer (CCT) program established decades before yields positive results on the beneficiaries' educational attainment. There is a large amount of evidence which suggests that CCTs encourage households to increase the use of educational services.

You read the guidelines for the program. Families receive a stipend per child provided they keep their them in school and take them for health checks. Additionally, you note that under the rules of the program, beneficiaries are selected based on a household income threshold of €20000. You decide to dive into the data with the idea that a discontinuity is created based on the income threshold. (This example utilizes simulated data)


cct_data <- readr::read_csv("https://raw.githubusercontent.com/seramirezruiz/stats-ii-lab/master/Session%206/data/cct_data.csv") # loading simulated data frame of the program


hh_income: household income in euros

years_of_schooling: years of schooling for respondent

treatment: binary variable indicating whether respondent was a beneficiary of the program


Checking visually whether a sharp-RDD makes sense for the analysis

What we are looking for in this case is whether our €20000 threshold is in fact the cut-off for treatment. That is to say, that only those who had a household income of equal or less than €20000 received the cash transfer.

ggplot(cct_data, 
       aes(x = hh_income, 
           y = years_of_schooling, 
           color = factor(treatment))) + 
  geom_point() + 
  labs(x = "Household Income", 
       y = "Years of Schooling") +
  scale_color_discrete(name = " ", 
                       labels = c("No treatment", "Treatment")) +
  geom_vline(xintercept = 20000, linetype = "dotted") +
  theme_minimal()

ggplot(cct_data, 
       aes(x = hh_income, 
           y = treatment, 
           color = factor(treatment))) + 
  geom_point() + 
  labs(x = "Household Income", 
       y = "Treatment") +
  scale_color_discrete(name = " ", 
                       labels = c("No treatment", "Treatment")) +
  geom_vline(xintercept = 20000, linetype = "dotted") +
  theme_minimal()


We can see from the graph that our €20000 threshold is in fact cutting off the distribution of the treatment. This would make household income a viable forcing variable for a sharp-RDD set-up.


Estimating our model

We can see that the relationship is fairly linear, so we decide to run a linear model with common slope.

# running linear model with common slope
ed_achievement <- lm(years_of_schooling ~ treatment + hh_income, data = cct_data)
summary(ed_achievement)
## 
## Call:
## lm(formula = years_of_schooling ~ treatment + hh_income, data = cct_data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.43214 -0.54823 -0.01033  0.54269  2.99567 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -2.648e+00  1.106e-01  -23.94   <2e-16 ***
## treatment    2.460e+00  3.785e-02   64.99   <2e-16 ***
## hh_income    6.274e-04  4.720e-06  132.91   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.8167 on 4997 degrees of freedom
## Multiple R-squared:  0.815,  Adjusted R-squared:  0.815 
## F-statistic: 1.101e+04 on 2 and 4997 DF,  p-value: < 2.2e-16
WHAT DO THESE RESULTS TELL US?

In line with our assumptions for linear models with common slope, we consider that treatment effect, \(D_i\), does not depend on the forcing \(X_i\). Hence, we can expect that students who received the treatment get on average 2.4 more years of schooling. We also see that for every €1,000 increase in the household income, students are expected to attain 0.6274 more years of education. (Our \(\beta = -6.274e-04*1000\)).




Getting familiar with LOESS

Locally weighted smoothing is a popular tool used in regression analysis that creates a smooth line through a scatter plot to help you to see relationship between variables and foresee trends. We can introduce it to our ggplot as a part of geom_smooth by calling method "loess".

ggplot(cct_data, 
       aes(x = hh_income, 
           y = years_of_schooling, 
           color = factor(treatment))) + 
  geom_point(alpha = 0.1) + 
  labs(x = "Household Income", 
       y = "Years of Schooling") +
  geom_smooth(method = "loess") +
  scale_color_discrete(name = " ", 
                       labels = c("No treatment", "Treatment")) +
  geom_vline(xintercept = 20000, linetype = "dotted") +
  theme_minimal()

The LOESS smoothing is not very visible in this relationship because of the way we defined the simulated data. Let's look at how it would look in our drinking age example:

ggplot(mlda_df,
       aes(x = agecell,  
           y = outcome, 
           col = factor(treatment))) +
  geom_point() +
  geom_smooth(method = "loess") +
  labs(title = "LOESS smoothing",
       x = "Forcing variable (Age)",
       y = "Mortality rate from motor vehicle \naccidents (per 100,000)") +
  scale_color_manual(name = "",
                     values = c("#F8766D", "#00BFC4"),
                     labels = c("Control", "Treatment")) +
  theme_minimal()




Violations to the assumptions

You are made aware by a tax expert from your unit that €20000 is the upper-boundary for a very well known tax concession. You are afraid that people may be sorting themselves before the household income cut-off to become beneficiaries of multiple programs. You decide to check your data.

ggplot(cct_data, 
       aes(x = hh_income)) +
  geom_histogram(bins = 25) +
  labs(title = "Income distribution",
       x = "Household Income",
       y = "Number of respondents") +
  geom_vline(xintercept = 20000, linetype = "dotted") +
  theme_minimal()


This case looks a bit ambiguous. Do you think people are sorting out just before the cut-off? If sorting were to exist which assumptions would be challenged? Would the existence of other programs that have the same threshold affect a causal reading of our results?

We can also apply an exact binomial test (like in the lecture). In this case, we can check ±1000

cct_data %>% 
  dplyr::filter(dplyr::between(hh_income, 19000, 21000)) %>%
  dplyr::group_by(treatment) %>%
  dplyr::summarize(n = n()) %>%
  knitr::kable() %>%
  kableExtra::kable_styling()
treatment n
0 520
1 530
binom.test(530, 1050, p = 0.5)
## 
##  Exact binomial test
## 
## data:  530 and 1050
## number of successes = 530, number of trials = 1050, p-value = 0.7812
## alternative hypothesis: true probability of success is not equal to 0.5
## 95 percent confidence interval:
##  0.4740669 0.5354301
## sample estimates:
## probability of success 
##              0.5047619