F2-dev-01 — Two-way fixed-effects slope with firm-clustered uncertainty
Question
What is the within-firm, within-year slope β of y on x in the model y_it = a_i + g_t + β x_it + u_it? Its uncertainty should allow arbitrary dependence of u_it within a firm over time.
Data and exclusions
Input: data/firm_panel.csv (simulated). There are 1200 rows (n_input_rows), 100 firms (n_firms) and 12 years, 2008–2019 (n_years). The panel is balanced (panel_balanced). There are no missing values, no duplicate rows and no duplicate firm–year keys. The SHA-256 matches the bundle (input_sha256). The exclusion rules in out/exclusions.csv removed nothing, so 1200 observations were analysed (n_analysed). data_issues is empty.
Method
- Baseline: pooled OLS of y on x with an intercept and no fixed effects (
beta_pooled). - Main estimate: OLS with a dummy for each firm and each year (LSDV). This gives
beta_fe. A Frisch–Waugh check confirms the slope. - Conventional SE: homoskedastic OLS standard error from the LSDV fit. It has 1088 residual degrees of freedom (
df_resid_conventional). - Clustered SE: CR1 sandwich clustered by
firm_id, built from the scores of the FE-residualised x times the FE residuals. The small-sample factor is G/(G−1)·(N−1)/(N−K) (cluster_small_sample_factor). Firm effects sit inside the clusters, so they are not counted in K; this follows the Stataxtreg, fe vce(cluster)andreghdfeconvention. So K = intercept + 11 year effects + slope = 13. - 95% CI:
beta_fe± t(G−1 = 99, 0.975) ×se_cluster. The critical value is 1.984 (ci_t_critical_df_G_minus_1). - No randomness is involved (
random_seed= null).
Results
- Pooled OLS slope:
beta_pooled= 1.268. - Two-way FE slope:
beta_fe= 0.604. - The two estimates differ by a factor of about 2. This means the firm and/or year effects are correlated with x, so the pooled estimate is confounded by them.
Uncertainty
se_conventional= 0.0326.se_cluster= 0.0451, about 1.38 times the conventional SE. This suggests positive within-firm dependence in the residuals and/or x.- 95% CI from the clustered SE: [
beta_ci_low,beta_ci_high] = [0.515, 0.694]. - Sensitivity to the degrees-of-freedom convention:
- With no correction (CR0),
se_cluster_unadjusted_CR0= 0.0446. - Counting all 112 parameters, including firm effects, in K (the statsmodels LSDV default) gives
se_cluster_allK_dfadj= 0.0471 (=se_cluster_statsmodels_lsdv). - None of these choices changes the conclusion.
- With no correction (CR0),
Limitations and open issues
- β is causal only if x is strictly exogenous once the two sets of fixed effects are controlled for. The data cannot test this.
- Clustered inference relies on asymptotics in the number of firms. 100 clusters is usually enough, but it is not exact.
- The choice of small-sample correction is a convention (see Uncertainty); reasonable choices move the SE by about ±5%.
- The data are simulated.