3  Chapter 5: TWFE Estimators Outside of the Classical Design

3.1 Overview

Dataset: Gentzkow, Shapiro & Sinkinson (2011)gentzkowetal_didtextbook.dta

This chapter investigates what the TWFE estimator \(\hat{\beta}^{fe}\) estimates outside the classical design, using the effect of newspapers on voter turnout. The design features a non-binary treatment (number of newspapers), non-absorbing treatment changes, and variation in treatment timing across 1,195 US counties from 1872 to 1928.

Key tool: the twowayfeweights package (Stata/R) decomposes \(\hat{\beta}^{fe}\) into a weighted sum of treatment effects, where some weights may be negative — meaning \(\hat{\beta}^{fe}\) may not estimate a convex combination of effects.

No event-study plots in this chapter — only numerical outputs from regressions and decompositions.


3.2 Panel View

* ssc install panelview, replace
copy "https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.dta" "gentzkowetal_didtextbook.dta", replace
use "gentzkowetal_didtextbook.dta", clear

panelview prestout has_newspaper, i(cnty90) t(year) type(treat) title("Treatment Status: Gentzkow, Shapiro & Sinkinson (2011)") legend(label(1 "No newspaper") label(2 "Has newspaper")) ylabel(none) ytitle("")
graph export "figures/ch05_panelview_stata.png", replace width(1200)

Treatment Status (Stata)

library(panelView)
load(url("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.RData"))
png("figures/ch05_panelview_R.png", width = 1000, height = 600)
panelview(prestout ~ numdailies, data = df, index = c("cnty90", "year"), type = "treat",
          main = "Gentzkow, Shapiro & Sinkinson (2011)", ylab = "")
dev.off()

Treatment Status (R)

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.patches import Patch

df = pd.read_parquet("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.parquet")
df["has_newspaper"] = (df["numdailies"] > 0).astype(int)
pv = df.pivot_table(index="cnty90", columns="year", values="has_newspaper", aggfunc="first")
pv_sorted = pv.loc[pv.mean(axis=1).sort_values(ascending=False).index]
cmap = mcolors.ListedColormap(["#D4E6F1", "#2171B5"])
fig, ax = plt.subplots(figsize=(12, 10))
ax.imshow(pv_sorted.values, aspect="auto", cmap=cmap, interpolation="nearest", vmin=0, vmax=1)
for i in range(0, len(pv_sorted), 10):
    ax.axhline(y=i - 0.5, color="white", linewidth=0.15)
ax.set_xticks(range(0, len(pv_sorted.columns), 2))
ax.set_xticklabels([int(c) for c in pv_sorted.columns[::2]], rotation=45, ha="right", fontsize=8)
ax.set_yticks([])
ax.set_xlabel("year")
ax.set_title("Treatment Status: Gentzkow, Shapiro & Sinkinson (2011)", fontsize=16)
ax.legend(handles=[Patch(facecolor="#D4E6F1", edgecolor="gray", label="No newspaper"),
                   Patch(facecolor="#2171B5", edgecolor="gray", label="Has newspaper")],
          loc="lower center", bbox_to_anchor=(0.5, -0.12), ncol=2)
plt.tight_layout()
plt.savefig("figures/ch05_panelview_Python.png", dpi=150, bbox_inches="tight")
plt.show()

Treatment Status (Python)


3.3 CQ#18–19: Basic TWFE Regression & Weight Decomposition

Regress turnout on number of newspapers and county and year FEs, clustering standard errors at the county level. Then decompose \(\hat{\beta}^{fe}\) using twowayfeweights.

copy "https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.dta" "gentzkowetal_didtextbook.dta", replace
use "gentzkowetal_didtextbook.dta", clear
areg prestout i.year numdailies, absorb(cnty90) cluster(cnty90)
Linear regression, absorbing indicators             Number of obs     = 16,872
Absorbed variable: cnty90                           No. of categories =  1,195
                                                    F(16, 1194)       = 310.47
                                                    Prob > F          = 0.0000
                                                    R-squared         = 0.7006
                                                    Adj R-squared     = 0.6774
                                                    Root MSE          = 0.1255

                             (Std. err. adjusted for 1,195 clusters in cnty90)
------------------------------------------------------------------------------
             |               Robust
    prestout | Coefficient  std. err.      t    P>|t|     [95% conf. interval]
-------------+----------------------------------------------------------------
        year |
       1872  |  -.0117399   .0084998    -1.38   0.167    -.0284162    .0049364
       1876  |   .0682968   .0079161     8.63   0.000     .0527658    .0838278
       1880  |   .0707524   .0084285     8.39   0.000     .0542160    .0872888
       1884  |   .0743330   .0093760     7.93   0.000     .0559377    .0927284
       1888  |   .0837409   .0095892     8.73   0.000     .0649273    .1025545
       1892  |   .0409980   .0096998     4.23   0.000     .0219675    .0600285
       1896  |   .0730286   .0106514     6.86   0.000     .0521311    .0939262
       1900  |   .0143816   .0112282     1.28   0.200    -.0076475    .0364107
       1904  |  -.0782734   .0109829    -7.13   0.000    -.0998212   -.0567255
       1908  |  -.0663333   .0111551    -5.95   0.000    -.0882192   -.0444474
       1912  |  -.1294217   .0110474   -11.72   0.000    -.1510962   -.1077473
       1916  |  -.0910842   .0111088    -8.20   0.000    -.1128791   -.0692893
       1920  |  -.1519845   .0108909   -13.96   0.000    -.1733520   -.1306170
       1924  |  -.1649369   .0109407   -15.08   0.000    -.1864020   -.1434718
       1928  |  -.1102546   .0108236   -10.19   0.000    -.1314900   -.0890193
             |
  numdailies |   .0029393   .0016283     1.81   0.071    -.0002553    .0061339
       _cons |   .6764326   .0082986    81.51   0.000     .6601511    .6927140
------------------------------------------------------------------------------
* ssc install twowayfeweights, replace
copy "https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.dta" "gentzkowetal_didtextbook.dta", replace
use "gentzkowetal_didtextbook.dta", clear
twowayfeweights prestout cnty90 year numdailies, type(feTR)
Under the common trends assumption,
the TWFE coefficient beta, equal to 0.0029, estimates a weighted sum of 10378 ATTs.
6180 ATTs receive a positive weight, and 4198 receive a negative weight.
------------------------------------------------
Treat. var: numdailies  # ATTs      Σ weights
------------------------------------------------
Positive weights        6180        1.4740
Negative weights        4198        -0.4740
------------------------------------------------
Total                   10378       1.0000
------------------------------------------------
library(haven); library(fixest)
load(url("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.RData"))
model1 <- feols(prestout ~ numdailies + i(year) | cnty90,
                data = df, cluster = ~cnty90,
                ssc = ssc(fixef.K = "full"))
Dep. var.: prestout | Obs: 16872 | FE: cnty90 | Cluster: cnty90
------------------------------------------------------------------------------------------
Variable                   Coefficient    Std. Err.    t value   Pr(>|t|)         2.5%        97.5%
------------------------------------------------------------------------------------------
numdailies                   0.0029393    0.0016283       1.81      0.071   -0.0002553    0.0061339
year::1872                  -0.0117399    0.0084998      -1.38      0.167   -0.0284162    0.0049364
year::1876                   0.0682968    0.0079161       8.63      0.000    0.0527658    0.0838278
year::1880                   0.0707524    0.0084285       8.39      0.000    0.0542160    0.0872888
year::1884                   0.0743330    0.0093760       7.93      0.000    0.0559377    0.0927284
year::1888                   0.0837409    0.0095892       8.73      0.000    0.0649273    0.1025545
year::1892                   0.0409980    0.0096998       4.23      0.000    0.0219675    0.0600285
year::1896                   0.0730286    0.0106514       6.86      0.000    0.0521311    0.0939262
year::1900                   0.0143816    0.0112282       1.28      0.200   -0.0076475    0.0364107
year::1904                  -0.0782734    0.0109829      -7.13      0.000   -0.0998212   -0.0567255
year::1908                  -0.0663333    0.0111551      -5.95      0.000   -0.0882192   -0.0444474
year::1912                  -0.1294217    0.0110474     -11.72      0.000   -0.1510962   -0.1077473
year::1916                  -0.0910842    0.0111088      -8.20      0.000   -0.1128791   -0.0692893
year::1920                  -0.1519845    0.0108909     -13.96      0.000   -0.1733520   -0.1306170
year::1924                  -0.1649369    0.0109407     -15.08      0.000   -0.1864020   -0.1434718
year::1928                  -0.1102546    0.0108236     -10.19      0.000   -0.1314900   -0.0890193
------------------------------------------------------------------------------------------
library(haven); library(TwoWayFEWeights)
load(url("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.RData"))
decomp1 <- twowayfeweights(df, "prestout", "cnty90", "year",
                            "numdailies", type = "feTR")
Under the common trends assumption,
the TWFE coefficient beta, equal to 0.0029, estimates a weighted sum of 10378 ATTs.
6180 ATTs receive a positive weight, and 4198 receive a negative weight.

Treat. var: numdailies     ATTs    Σ weights
Positive weights           6180        1.474
Negative weights           4198       -0.474
Total                     10378            1
import pandas as pd
import pyfixest as pf
df = pd.read_parquet("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.parquet")
m1 = pf.feols(
    "prestout ~ numdailies + C(year) | cnty90",
    data=df,
    vcov={"CRV1": "cnty90"},
    ssc=pf.ssc(adj=True, fixef_k="full", cluster_adj=True)
)
Dep. var.:      prestout
Observations:   16872
Fixed effects:  cnty90
Cluster var:    cnty90
--------------------------------------------------------------------------------
Variable              Coefficient    Std. Err.          t      P>|t|                 [95% CI]
--------------------------------------------------------------------------------
numdailies              0.0029393    0.0016283       1.81      0.071 [-0.0002521,  0.0061308]
year::1872             -0.0117399    0.0084998      -1.38      0.167 [-0.0283996,  0.0049198]
year::1876              0.0682968    0.0079161       8.63      0.000 [ 0.0527813,  0.0838123]
year::1880              0.0707524    0.0084285       8.39      0.000 [ 0.0542325,  0.0872723]
year::1884              0.0743330    0.0093760       7.93      0.000 [ 0.0559560,  0.0927101]
year::1888              0.0837409    0.0095892       8.73      0.000 [ 0.0649460,  0.1025357]
year::1892              0.0409980    0.0096998       4.23      0.000 [ 0.0219864,  0.0600096]
year::1896              0.0730286    0.0106514       6.86      0.000 [ 0.0521519,  0.0939054]
year::1900              0.0143816    0.0112282       1.28      0.200 [-0.0076256,  0.0363888]
year::1904             -0.0782734    0.0109829      -7.13      0.000 [-0.0997998, -0.0567469]
year::1908             -0.0663333    0.0111551      -5.95      0.000 [-0.0881974, -0.0444692]
year::1912             -0.1294217    0.0110474     -11.72      0.000 [-0.1510746, -0.1077688]
year::1916             -0.0910842    0.0111088      -8.20      0.000 [-0.1128574, -0.0693110]
year::1920             -0.1519845    0.0108909     -13.96      0.000 [-0.1733307, -0.1306383]
year::1924             -0.1649369    0.0109407     -15.08      0.000 [-0.1863807, -0.1434932]
year::1928             -0.1102546    0.0108236     -10.19      0.000 [-0.1314688, -0.0890405]
--------------------------------------------------------------------------------
import pandas as pd
from twowayfeweights import twowayfeweights, print_twowayfeweights
df = pd.read_parquet("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.parquet")
result = twowayfeweights(df, "prestout", "cnty90", "year", "numdailies",
                          type="feTR")
print_twowayfeweights(result, D_name="numdailies", type="feTR")
Under the common trends assumption,
beta estimates a weighted sum of 10378 ATTs.
6180 ATTs receive a positive weight, and 4198 receive a negative weight.
------------------------------------------------
Treat. var: numdailies  # ATTs      Σ weights
------------------------------------------------
Positive weights        6180        1.4740
Negative weights        4198        -0.4740
------------------------------------------------
Total                   10378       1.0000
------------------------------------------------

3.4 CQ#20–21: TWFE Regression with State-Year FEs & Weight Decomposition

Regress turnout on number of newspapers and county, year, and state-year FEs. Then decompose using twowayfeweights with the controls option.

copy "https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.dta" "gentzkowetal_didtextbook.dta", replace
use "gentzkowetal_didtextbook.dta", clear
qui tab styr, gen(styr)
qui areg prestout i.year i.styr numdailies, absorb(cnty90) cluster(cnty90)
display "numdailies:  coef = " %12.7f _b[numdailies] "  se = " %12.7f _se[numdailies]
numdailies:  coef =   -0.0012122  se =    0.0010962  t =   -1.10578
* ssc install twowayfeweights, replace
copy "https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.dta" "gentzkowetal_didtextbook.dta", replace
use "gentzkowetal_didtextbook.dta", clear
twowayfeweights prestout cnty90 year numdailies, type(feTR) controls(styr1-styr683)
Under the common trends assumption,
the TWFE coefficient beta, equal to -0.0012, estimates a weighted sum of 10342 ATTs.
6195 ATTs receive a positive weight, and 4147 receive a negative weight.
10378 (g,t) cells receive the treatment, but the ATTs of 36 cells receive a weight equal to zero.
------------------------------------------------
Treat. var: numdailies  # ATTs      Σ weights
------------------------------------------------
Positive weights        6195        1.5331
Negative weights        4147        -0.5331
------------------------------------------------
Total                   10342       1.0000
------------------------------------------------
library(haven); library(fixest)
load(url("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.RData"))
model2 <- feols(prestout ~ numdailies + i(year) + i(styr) | cnty90,
                data = df, cluster = ~cnty90,
                ssc = ssc(fixef.K = "full"))
numdailies:  coef =   -0.0012122  se =    0.0010962  t =   -1.10578
library(haven); library(TwoWayFEWeights)
load(url("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.RData"))
styr_dummies <- model.matrix(~ factor(styr) - 1, data = df)
colnames(styr_dummies) <- paste0("styr", 1:ncol(styr_dummies))
df <- cbind(df, styr_dummies)
styr_cols <- paste0("styr", 1:ncol(styr_dummies))
decomp2 <- twowayfeweights(df, "prestout", "cnty90", "year",
                            "numdailies", type = "feTR",
                            controls = styr_cols)
Under the common trends assumption,
the TWFE coefficient beta, equal to -0.0012, estimates a weighted sum of 10342 ATTs.
6195 ATTs receive a positive weight, and 4147 receive a negative weight.
10378 (g,t) cells receive the treatment, but the ATTs of 36 cells receive a weight equal to zero.

Treat. var: numdailies     ATTs    Σ weights
Positive weights           6195       1.5331
Negative weights           4147      -0.5331
Total                     10342            1
import pandas as pd
import pyfixest as pf
df = pd.read_parquet("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.parquet")
m2 = pf.feols(
    "prestout ~ numdailies + C(year) + C(styr) | cnty90",
    data=df,
    vcov={"CRV1": "cnty90"},
    ssc=pf.ssc(adj=True, fixef_k="full", cluster_adj=True)
)
numdailies:  coef =   -0.0012122  se =    0.0010962  t =   -1.10578
(year and state-year FEs absorbed, not shown)
import pandas as pd
from twowayfeweights import twowayfeweights, print_twowayfeweights
df = pd.read_parquet("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.parquet")
# Decomposition with state-year controls
styr_cols = sorted([c for c in df.columns if c.startswith("styr") and c != "styr"])
result = twowayfeweights(df, "prestout", "cnty90", "year", "numdailies",
                          type="feTR", controls=styr_cols)
print_twowayfeweights(result, D_name="numdailies", type="feTR")
Under the common trends assumption,
beta estimates a weighted sum of 10342 ATTs.
6195 ATTs receive a positive weight, and 4147 receive a negative weight.
36 ATTs receive a weight equal to 0.
------------------------------------------------
Treat. var: numdailies  # ATTs      Σ weights
------------------------------------------------
Positive weights        6195        1.5331
Negative weights        4147        -0.5331
------------------------------------------------
Total                   10342       1.0000
------------------------------------------------

3.5 CQ#22–24: FD Regression, Weight Decomposition & Random Weights Test

Regress the change in turnout on the change in number of newspapers and state-year FEs. Decompose using twowayfeweights with type(fdTR), and test if weights are correlated with the year variable.

copy "https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.dta" "gentzkowetal_didtextbook.dta", replace
use "gentzkowetal_didtextbook.dta", clear
areg changeprestout changedailies, absorb(styr) cluster(cnty90)
Linear regression, absorbing indicators             Number of obs     = 15,627
Absorbed variable: styr                             No. of categories =    634
                                                    F(1, 1194)        =   7.68
                                                    Prob > F          = 0.0057
                                                    R-squared         = 0.5690
                                                    Adj R-squared     = 0.5507
                                                    Root MSE          = 0.0816

                              (Std. err. adjusted for 1,195 clusters in cnty90)
-------------------------------------------------------------------------------
              |               Robust
changeprest~t | Coefficient  std. err.      t    P>|t|     [95% conf. interval]
--------------+----------------------------------------------------------------
changedailies |   .0025907   .0009351     2.77   0.006     .0007561    .0044252
        _cons |  -.0072455   .0003658   -19.81   0.000    -.0079632   -.0065278
-------------------------------------------------------------------------------
* ssc install twowayfeweights, replace
copy "https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.dta" "gentzkowetal_didtextbook.dta", replace
use "gentzkowetal_didtextbook.dta", clear
twowayfeweights changeprestout cnty90 year changedailies numdailies, type(fdTR) controls(styr1-styr683)
Under the common trends assumption,
the TWFE coefficient beta, equal to 0.0026, estimates a weighted sum of 9876 ATTs.
5371 ATTs receive a positive weight, and 4505 receive a negative weight.
10378 (g,t) cells receive the treatment, but the ATTs of 502 cells receive a weight equal to zero.
------------------------------------------------
Treat. var: changedailies  # ATTs      Σ weights
------------------------------------------------
Positive weights           5371        2.4271
Negative weights           4505        -1.4271
------------------------------------------------
Total                      9876        1.0000
------------------------------------------------
* ssc install twowayfeweights, replace
copy "https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.dta" "gentzkowetal_didtextbook.dta", replace
use "gentzkowetal_didtextbook.dta", clear
twowayfeweights changeprestout cnty90 year changedailies numdailies, type(fdTR) controls(styr1-styr683) test_random_weights(year)
Regression of variables possibly correlated with the treatment effect on the weights

             Coef           SE       t-stat  Correlation
year    -.1674271    .05101171   -3.2821307    -.0631614
library(haven); library(fixest)
load(url("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.RData"))
model3 <- feols(changeprestout ~ changedailies | styr,
                data = df, cluster = ~cnty90,
                ssc = ssc(fixef.K = "full"))
Dep. var.: changeprestout | Obs: 15607 | FE: styr | Cluster: cnty90
------------------------------------------------------------------------------------------
Variable                   Coefficient    Std. Err.    t value   Pr(>|t|)         2.5%        97.5%
------------------------------------------------------------------------------------------
changedailies                0.0025907    0.0009345       2.77     0.0057    0.0007573    0.0044241
------------------------------------------------------------------------------------------
library(haven); library(TwoWayFEWeights)
load(url("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.RData"))
styr_dummies <- model.matrix(~ factor(styr) - 1, data = df)
colnames(styr_dummies) <- paste0("styr", 1:ncol(styr_dummies))
df <- cbind(df, styr_dummies)
styr_cols <- paste0("styr", 1:ncol(styr_dummies))
decomp3 <- twowayfeweights(df, "changeprestout", "cnty90", "year",
                            "changedailies", D0 = "numdailies",
                            type = "fdTR", controls = styr_cols)
Under the common trends assumption,
the TWFE coefficient beta, equal to 0.0026, estimates a weighted sum of 9876 ATTs.
5371 ATTs receive a positive weight, and 4505 receive a negative weight.
10378 (g,t) cells receive the treatment, but the ATTs of 502 cells receive a weight equal to zero.

Treat. var: changedailies    ATTs    Σ weights
Positive weights             5371       2.4271
Negative weights             4505      -1.4271
Total                        9876            1
library(haven); library(TwoWayFEWeights)
load(url("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.RData"))
styr_dummies <- model.matrix(~ factor(styr) - 1, data = df)
colnames(styr_dummies) <- paste0("styr", 1:ncol(styr_dummies))
df <- cbind(df, styr_dummies)
styr_cols <- paste0("styr", 1:ncol(styr_dummies))
decomp3_test <- twowayfeweights(df, "changeprestout", "cnty90", "year",
                                 "changedailies", D0 = "numdailies",
                                 type = "fdTR", controls = styr_cols,
                                 test_random_weights = "year")
Regression of variables possibly correlated with the treatment effect on the weights:
              Coef         SE    t-stat Correlation
RW_year -0.1674271 0.05101171 -3.282131  -0.0631614
import pandas as pd
import pyfixest as pf
df = pd.read_parquet("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.parquet")
m3 = pf.feols(
    "changeprestout ~ changedailies | styr",
    data=df,
    vcov={"CRV1": "cnty90"},
    ssc=pf.ssc(adj=True, fixef_k="full", cluster_adj=True),
    fixef_rm="none"
)
Dep. var.:      changeprestout
Observations:   15627
Fixed effects:  styr
Cluster var:    cnty90
--------------------------------------------------------------------------------
Variable              Coefficient    Std. Err.          t      P>|t|                 [95% CI]
--------------------------------------------------------------------------------
changedailies           0.0025907    0.0009351       2.77     0.0057 [ 0.0007580,  0.0044234]
--------------------------------------------------------------------------------
import pandas as pd
from twowayfeweights import twowayfeweights, print_twowayfeweights
df = pd.read_parquet("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.parquet")
# FD weight decomposition (fdTR type)
styr_cols = sorted([c for c in df.columns if c.startswith("styr") and c != "styr"])
result3 = twowayfeweights(df, "changeprestout", "cnty90", "year",
                           "changedailies", type="fdTR", D0="numdailies",
                           controls=styr_cols)
print_twowayfeweights(result3, D_name="changedailies", type="fdTR")
Under the common trends assumption,
beta estimates a weighted sum of 9876 ATTs.
5371 ATTs receive a positive weight, and 4505 receive a negative weight.
502 ATTs receive a weight equal to 0.
------------------------------------------------
Treat. var: changedailies # ATTs     Σ weights
------------------------------------------------
Positive weights        5371        2.4271
Negative weights        4505        -1.4271
------------------------------------------------
Total                   9876        1.0000
------------------------------------------------
import pandas as pd
from twowayfeweights import twowayfeweights, print_twowayfeweights
df = pd.read_parquet("https://raw.githubusercontent.com/anzonyquispe/did_book/main/cc_xd_didtextbook_2025_9_30/Data%20sets/Gentzkow%20et%20al%202011/gentzkowetal_didtextbook.parquet")
# Test random weights (year)
styr_cols = sorted([c for c in df.columns if c.startswith("styr") and c != "styr"])
result3b = twowayfeweights(df, "changeprestout", "cnty90", "year",
                            "changedailies", type="fdTR", D0="numdailies",
                            controls=styr_cols, test_random_weights="year")
print_twowayfeweights(result3b, D_name="changedailies", type="fdTR")
Regression of variables possibly correlated with the treatment effect on the weights

             Coef           SE       t-stat  Correlation
year   -0.1674271    0.0510117   -3.2821306   -0.0631614