R Econometrics: IV, DiD, RDD

Run IV, DiD, and RDD causal analyses in R with proper diagnostics, panel data visualization, and robust standard errors.

Sby Skills Guide Bot
Data & AIAdvanced
408/12/2026
Claude CodeCursorCodex
#R#econometrics#causal-inference#fixest#regression

Recommended for


name: r-econometrics description: Run IV, DiD, and RDD analyses in R with proper diagnostics workflow_stage: analysis compatibility:

  • claude-code
  • cursor
  • codex
  • gemini-cli author: Awesome Econ AI Community version: 1.1.0 tags:
  • R
  • econometrics
  • causal-inference
  • fixest
  • regression

R Econometrics

Purpose

This skill helps economists run rigorous econometric analyses in R, including Instrumental Variables (IV), Difference-in-Differences (DiD), and Regression Discontinuity Design (RDD). It generates publication-ready code with proper diagnostics and robust standard errors.

When to Use

  • Running causal inference analyses
  • Estimating treatment effects with panel data
  • Creating publication-ready regression tables
  • Implementing modern econometric methods (two-way fixed effects, event studies)

Instructions

Step 1: Understand the Research Design

Before generating code, ask the user:

  1. What is your identification strategy? (IV, DiD, RDD, or simple regression)
  2. What is the unit of observation? (individual, firm, country-year, etc.)
  3. What fixed effects do you need? (entity, time, two-way)
  4. How should standard errors be clustered?

Step 2: Look at the Panel Data Before Estimating

"When I have not looked at my data, I have little imagination about the DGP. No model, however good, can fix that." — Yiqing Xu, Please look at your (panel) data

Visualizing the panel structure should be routine, not optional. Before generating any regression code, produce and read a treatment-status plot with the panelView package. Every cell must be uniquely identified by a unit-time pair — if it is not, stop and resolve the unit of analysis first.

library(panelView)

# Treatment-status pattern: who is treated, when they switch on, and what's missing
panelview(outcome ~ treatment,
          data = df, index = c("unit", "time"),
          main = "Treatment status pattern",
          by.timing = TRUE)        # sort units by adoption timing

# Outcome trajectories: distribution, outliers, treated/control overlap, persistence
panelview(outcome ~ treatment,
          data = df, index = c("unit", "time"),
          type = "outcome")

Use these plots to answer three questions before trusting any estimate:

  1. Where does the identifying variation actually come from? With fixed effects, the coefficients come from variation left after the FEs are absorbed. If treatment varies almost entirely along one dimension (only across units, or only across time), TWFE will soak up nearly all of it and leave very thin identifying variation. Watch for singletons — observations alone in a FE group; they have no within-group variation, get dropped, and contribute nothing.
  2. What is the effective number of observations? A panel of 1,001 units with 1 treated unit does not have an effective sample size of 1,001. Uncertainty is driven by the treated cells; gauge how many cells truly carry the estimate.
  3. What does the missing-data pattern look like? Plot treatment status alongside missingness. Severe or systematic missingness means MCAR/MAR assumptions are far stronger than usually acknowledged — flag this as a threat to the results rather than ignoring it.

For modern DID estimators, visualize which cells actually participate in estimation (supported by fect and PanelMatch), since only a strict subset of the panel contributes once you fix matching/lag/lead windows:

# PanelMatch example: mask the panel to the cells that drive the estimate
panelview(data = pd, formula = y ~ treatment,
          index = c("unit", "time"), type = "treat",
          by.timing = TRUE, sample = pm_object)

Caution on terminology: a treatment-status plot is not a treatment assignment, let alone a design. The same staggered pattern can arise from very different assignment processes — treat staggered adoption as a setting, not a "design," unless it really is a stepped-wedge trial.

Step 3: Generate Analysis Code

Based on the research design, generate R code that:

  1. Uses the fixest package - Modern, fast, and feature-rich for panel data
  2. Includes proper diagnostics:
    • For IV: First-stage F-statistics, weak instrument tests
    • For DiD: Parallel trends visualization, event study plots
    • For RDD: Bandwidth selection, density tests
  3. Uses robust/clustered standard errors appropriate for the data structure
  4. Creates publication-ready output using modelsummary or etable

Step 4: Structure the Output

Always include:

# 1. Setup and packages
# 2. Data loading and preparation
# 3. Look at the panel (panelView: treatment status, outcomes, missingness)
# 4. Descriptive statistics
# 5. Main specification
# 6. Robustness checks
# 7. Visualization
# 8. Export results

Step 5: Add Documentation

Include comments explaining:

  • Why each specification choice was made
  • Interpretation of key coefficients
  • Limitations and assumptions

Example Prompts

  • "Run a DiD analysis with state and year fixed effects, clustering at the state level"
  • "Estimate the effect of X on Y using Z as an instrument"
  • "Create an event study plot showing treatment effects by year"
  • "Run a sharp RDD with optimal bandwidth selection"

Example Output

# ============================================
# Difference-in-Differences Analysis
# ============================================

# Setup
library(tidyverse)
library(fixest)
library(modelsummary)

# Load data
df <- read_csv("data.csv")

# Prepare treatment variable
df <- df %>%
  mutate(
    post = year >= treatment_year,
    treated = state %in% treatment_states,
    treat_post = treated * post
  )

# ----------------------------------------
# Main DiD Specification
# ----------------------------------------

# Two-way fixed effects
did_model <- feols(
  outcome ~ treat_post | state + year,
  data = df,
  cluster = ~state
)

# View results
summary(did_model)

# ----------------------------------------
# Event Study
# ----------------------------------------

# Create relative time variable
df <- df %>%
  mutate(rel_time = year - treatment_year)

# Event study regression
event_study <- feols(
  outcome ~ i(rel_time, treated, ref = -1) | state + year,
  data = df,
  cluster = ~state
)

# Plot coefficients
iplot(event_study, 
      main = "Event Study: Effect on Outcome",
      xlab = "Years Relative to Treatment")

# ----------------------------------------
# Robustness: Alternative Specifications
# ----------------------------------------

# Different clustering
did_robust <- feols(
  outcome ~ treat_post | state + year,
  data = df,
  cluster = ~state + year  # Two-way clustering
)

# ----------------------------------------
# Export Results
# ----------------------------------------

modelsummary(
  list("Main" = did_model, "Two-way Cluster" = did_robust),
  stars = c('*' = 0.1, '**' = 0.05, '***' = 0.01),
  output = "results/did_table.tex"
)

Requirements

Software

  • R 4.0+

Packages

  • panelView - Visualize panel/treatment structure before estimating
  • fixest - Fast fixed effects estimation
  • modelsummary - Publication-ready tables
  • tidyverse - Data manipulation
  • ggplot2 - Visualization

Install with:

install.packages(c("panelView", "fixest", "modelsummary", "tidyverse"))

Best Practices

  1. Look at the panel before you estimate — plot treatment status, outcome trajectories, and missingness with panelView; include these plots for reviewers, who usually can't see the data
  2. Ensure every cell is uniquely identified by a unit-time pair before running anything
  3. Always cluster standard errors at the level of treatment assignment
  4. Run pre-trend tests for DiD designs
  5. Report first-stage F-statistics for IV (should be > 10)
  6. Use feols over lm for panel data (faster and more features)
  7. Document all specification choices in your code comments

Common Pitfalls

  • ❌ Running regressions without ever looking at the panel structure
  • ❌ Fitting TWFE when treatment varies along only one dimension (identifying variation is nearly absorbed)
  • ❌ Ignoring singletons and severe/systematic missingness (MCAR/MAR assumptions are stronger than they look)
  • ❌ Calling a staggered-adoption setting a "design" when assignment is observational
  • ❌ Not clustering standard errors at the right level
  • ❌ Ignoring weak instruments in IV estimation
  • ❌ Using TWFE with staggered treatment timing (use did or sunab() instead)
  • ❌ Not reporting robustness checks

References

Changelog

v1.1.0

  • Added "Look at the panel data before estimating" step (panelView): treatment-status, outcome-trajectory, and missingness plots; participating-cells visualization for fect/PanelMatch. Based on Yiqing Xu (2026).

v1.0.0

  • Initial release with IV, DiD, RDD support
Related skills