Author: Gregory Faletto
Date: 2026-05-23

# Simulation Vignette for FETWFE: From Coefficients to True Treatment Effects

#### Gregory Faletto

#### 2026-05-23

    # Load necessary libraries
    library(dplyr)
    #>
    #> Attaching package: 'dplyr'
    #> The following objects are masked from 'package:stats':
    #>
    #>     filter, lag
    #> The following objects are masked from 'package:base':
    #>
    #>     intersect, setdiff, setequal, union
    library(fetwfe)

# Introduction

This vignette demonstrates how to conduct simulation studies with the `{fetwfe}` package. In particular, we will:

- Generate a vector of coefficients with `genCoefs()`. These coefficients produce unit‐and‐time specific responses that respect difference‐in‐differences assumptions (e.g., conditional parallel trends) and the sparsity assumptions behind FETWFE.
- Simulate a panel data set with `simulateData()`.
- Estimate treatment effects via the FETWFE estimator with `fetwfeWithSimulatedData()`.
- Extract the true treatment effects using `getTes()`.

The workflow here follows the simulation‐study design outlined in the paper, so you may wish to skim its setup section for additional context.

# Simulation Workflow Using Piping

Below is a complete simulation pipeline, step by step.

## Step 1: Generate Simulation Coefficients

The `genCoefs()` function returns an object of class `"FETWFE_coefs"`, containing both the coefficient vector and its simulation parameters. In this example we set:

- R (number of treated cohorts) = 3
- T (number of time periods) = 6
- d (number of covariates) = 2
- density (sparsity level) = 0.1
- eff_size (effect‐size multiplier) = 2

    # Generate the coefficient object for simulation
    sim_coefs <- genCoefs(
      R         = 3,
      T         = 4,
      d         = 2,
      density   = 0.1,
      eff_size  = 2,
      seed      = 101
    )

(Again, for more details on the meaning of these parameters, see the simulation study section of the paper.)

## Step 2: Simulate Panel Data

Next, we simulate a panel data set using the generated coefficient object with the `simulateData()` function. With `simulateData()`, we generate:

- `N` units, each assigned to one of the cohorts
- Time‐invariant covariates drawn from a specified distribution
- Outcomes at times 1 through T, using our simulated coefficients

Here we choose:

- `N` (number of units) as 60,
- `sig_eps_sq` (observation-level noise variance) as 1,
- `sig_eps_c_sq` (unit-level noise variance) as 1, and
- use the default `"gaussian"` distribution for the covariates.

    # Simulate panel data based on the coefficients
    sim_data <- simulateData(
      sim_coefs,
      N = 60,
      sig_eps_sq = 1,
      sig_eps_c_sq = 1,
      distribution = "gaussian"
      )

The dataframe is stored in `sim_data$pdata`, so we can take a quick look at the results:

    head(sim_data$pdata)
    #>   time   unit treatment            y       cov1      cov2
    #> 1    1 unit01         0  0.001990395  0.4061679 0.1262211
    #> 2    2 unit01         0 -0.019280260  0.4061679 0.1262211
    #> 3    3 unit01         0 -1.424784613  0.4061679 0.1262211
    #> 4    4 unit01         0 -0.737871496  0.4061679 0.1262211
    #> 5    1 unit02         0  0.203581047 -0.5242428 0.8761651
    #> 6    2 unit02         0  0.213280262 -0.5242428 0.8761651

## Step 3: Run the FETWFE Estimator on Simulated Data

We then run the estimator on the simulated data using `fetwfeWithSimulatedData()`. (We could get the same results by manually unpacking `sim_data` and passing the arguments appropriately to `fewtfe()`. `fetwfeWithSimulatedData()` is just a wrapper function that takes care of this for us.)

    result <- fetwfeWithSimulatedData(sim_data)

We can now extract the results from `result` in the same way that we can with the standard `fetwfe()` function.

    summary(result)
    #> Summary of Fused Extended Two-Way Fixed Effects
    #> ================================================
    #>
    #> Overall ATT: 0.0862  (SE = 0.1501, p = 0.5659, 95% CI = [-0.2081, 0.3805])
    #> Selected: TRUE
    #>
    #> CATT (preview):
    #>  Cohort Estimated TE        SE ConfIntLow ConfIntHigh      P_value selected
    #>       2    -1.513251 0.1972121 -1.8997792   -1.126722 1.677405e-14     TRUE
    #>       3     1.014200 0.1264405  0.7663809    1.262019 1.047502e-15     TRUE
    #>       4     0.000000 0.0000000  0.0000000    0.000000           NA    FALSE
    #>
    #> Model Details:
    #>   Units (N)           : 60
    #>   Time periods (T)    : 4
    #>   Treated cohorts (R) : 3
    #>   Covariates (d)      : 2
    #>   Features (p)        : 38
    #>   Selected size       : 6
    #>   Lambda*             : 0.0301

## Step 4: Extract True Treatment Effects

To evaluate the estimated ATT, we can compute the true treatment effects using the original coefficient object. The `getTes()` function extracts both the overall average treatment effect and the cohort-specific effects.

    # Extract the true treatment effects
    true_tes <- getTes(sim_coefs)

    # Print the true overall treatment effect
    cat("True Overall ATT:", true_tes$att_true, "\n")
    #> True Overall ATT: -0.1111111

    # Print the cohort-specific treatment effects
    print(true_tes$actual_cohort_tes)
    #> [1] -1.333333  1.000000  0.000000

We can use this to calculate metrics to evaluate our estimated treatment effect, like squared error:

    squared_error <- (result$att_hat - true_tes$att_true)^2

    cat("Squared error of ATT estimate:", squared_error, "\n")
    #> Squared error of ATT estimate: 0.03892912

## Combining the Workflow in One Pipeline

You can also chain the simulation functions together with the pipe operator. The following code generates the coefficients, simulates the data, and runs the estimator all in one pipeline:

    coefs <- genCoefs(R = 3, T = 4, d = 2, density = 0.1, eff_size = 2, seed = 2025)

    result_piped <- coefs |>
      simulateData(N = 60, sig_eps_sq = 1, sig_eps_c_sq = 1) |>
      fetwfeWithSimulatedData()

    cat("Estimated Overall ATT from piped workflow:", result_piped$att_hat, "\n")
    #> Estimated Overall ATT from piped workflow: -0.7045089

    true_tes_piped <- coefs |> getTes()

    # Print the true overall treatment effect
    cat("True Overall ATT:", true_tes_piped$att_true, "\n")
    #> True Overall ATT: -0.6666667

    # Print the squared estimation error
    squared_error_piped = (result_piped$att_hat - true_tes_piped$att_true)^2

    cat("Squared estimation error:", squared_error_piped, "\n")
    #> Squared estimation error: 0.001432032

# Conclusion

In this vignette, we walked through how to use the simulation functions in the `{fetwfe}` package to simulate data and run simulations similar to the ones in the simulation studies section of the FETWFE paper.

This pipeline streamlines simulation experiments so you can rapidly evaluate FETWFE’s performance under varying scenarios. For more details, consult the package documentation or reach out to the author.