Untitled
Anonymous
plain_text
02/24/2026 7:53 PM
10.4 KB
12
Indexable
############################################
# Replicate Table 1B (Panel B): Annual sample firm characteristics
# Self-contained script (fresh R session)
#
# Key choices:
# - Close-election cycles: [-1, 0, +1, +2]
# - Merge by firm HQ state and fiscal year
# - Annual Compustat sample 1976-2021
# - Require nonmissing R&D expense and annual controls
# - Require AT >= 5 ($5m cutoff; AT is in millions)
# - Compute HHI and HHI^2 from 3-digit SIC-year sales shares
# - Exclude utilities (4900-4999) and financials (6000-6999)
# - No winsorization for Table 1B summary (matched published SDs better)
############################################
# -----------------------------
# Package checks
# -----------------------------
stopifnot(requireNamespace("readxl", quietly = TRUE))
stopifnot(requireNamespace("dplyr", quietly = TRUE))
stopifnot(requireNamespace("tidyr", quietly = TRUE))
stopifnot(requireNamespace("stringr", quietly = TRUE))
stopifnot(requireNamespace("tibble", quietly = TRUE))
library(readxl)
library(dplyr)
library(tidyr)
library(stringr)
library(tibble)
# -----------------------------
# Paths (edit if needed)
# -----------------------------
gub_path <- "/Users/admin/Downloads/GubDataRecreate2.xlsx"
comp_path <- "/Users/admin/Downloads/CompustatOutput.xlsx"
# -----------------------------
# Helpers
# -----------------------------
clean_comp_names <- function(nms) {
close_paren <- regexpr(")", nms, fixed = TRUE)
ifelse(
startsWith(nms, "(") & close_paren > 1,
substring(nms, 2, close_paren - 1),
nms
)
}
panel_b_summary <- function(df) {
tibble(
Variable = c("R&D intensity", "q", "Cash flow"),
N = c(nrow(df), nrow(df), nrow(df)),
Mean = c(mean(df$rd_intensity, na.rm = TRUE),
mean(df$q, na.rm = TRUE),
mean(df$cash_flow, na.rm = TRUE)),
Median = c(median(df$rd_intensity, na.rm = TRUE),
median(df$q, na.rm = TRUE),
median(df$cash_flow, na.rm = TRUE)),
SD = c(sd(df$rd_intensity, na.rm = TRUE),
sd(df$q, na.rm = TRUE),
sd(df$cash_flow, na.rm = TRUE))
) %>%
mutate(
Mean = round(Mean, 4),
Median = round(Median, 4),
SD = round(SD, 4)
)
}
# -----------------------------
# 1) Gubernatorial close elections (margin <= 5)
# -----------------------------
gub <- read_excel(gub_path, sheet = 1) %>%
rename(year = Year, state = State, margin = Margin) %>%
transmute(
year = as.integer(year),
state = str_to_upper(str_trim(as.character(state))),
margin = as.numeric(margin)
) %>%
filter(!is.na(year), !is.na(state), !is.na(margin)) %>%
filter(margin <= 5) %>%
distinct(state, year, .keep_all = TRUE)
# -----------------------------
# 2) Build close-election cycles [-1,0,+1,+2]
# -----------------------------
close_cycles <- gub %>%
rename(election_year = year) %>%
crossing(rel = c(-1L, 0L, 1L, 2L)) %>%
mutate(
year = election_year + rel,
close_election_cycle = 1L,
close_election_dummy = as.integer(rel == 0L)
) %>%
group_by(state, year) %>%
summarise(
close_election_cycle = 1L,
close_election_dummy = as.integer(any(close_election_dummy == 1L)),
.groups = "drop"
)
# -----------------------------
# 3) Load and clean Compustat annual data
# -----------------------------
comp_raw <- read_excel(comp_path, sheet = 1)
names(comp_raw) <- clean_comp_names(names(comp_raw))
comp <- comp_raw %>%
mutate(
gvkey = as.character(gvkey),
fyear = as.integer(fyear),
state = str_to_upper(str_trim(as.character(state))),
sic = suppressWarnings(as.numeric(sic)),
at = as.numeric(at),
ceq = as.numeric(ceq),
dlc = as.numeric(dlc),
dltt = as.numeric(dltt),
ppent = as.numeric(ppent),
txdb = as.numeric(txdb),
dp = as.numeric(dp),
ebitda = as.numeric(ebitda),
ib = as.numeric(ib),
sale = as.numeric(sale),
xrd = as.numeric(xrd),
csho = as.numeric(csho),
prcc_f = as.numeric(prcc_f)
) %>%
filter(
# Standard Compustat annual industrial/consolidated screens
is.na(indfmt) | indfmt == "INDL",
is.na(datafmt) | datafmt == "STD",
is.na(consol) | consol == "C",
!is.na(fyear), fyear >= 1976, fyear <= 2021
)
# -----------------------------
# 4) Construct annual firm variables (Appendix A1 formulas)
# Using stricter missing handling (matched published moments better)
# -----------------------------
comp_vars <- comp %>%
arrange(gvkey, fyear) %>%
group_by(gvkey) %>%
mutate(
has_price = !is.na(prcc_f),
first_price_year = suppressWarnings(min(fyear[has_price], na.rm = TRUE)),
first_price_year = ifelse(is.infinite(first_price_year), NA_real_, first_price_year),
list_age_years = fyear - first_price_year
) %>%
ungroup() %>%
mutate(
year = fyear,
sic3 = floor(sic / 10),
rd_intensity = ifelse(!is.na(at) & at > 0, xrd / at, NA_real_),
q = ifelse(!is.na(at) & at > 0,
((prcc_f * csho) + at - ceq - txdb) / at,
NA_real_),
cash_flow = ifelse(!is.na(at) & at > 0,
(ib + dp) / at,
NA_real_),
ln_sales = ifelse(!is.na(sale) & sale > 0, log1p(pmax(sale, 0)), NA_real_),
ln_age = ifelse(!is.na(list_age_years) & list_age_years >= 0, log1p(list_age_years), NA_real_),
tangibility = ifelse(!is.na(at) & at > 0, ppent / at, NA_real_),
profitability = ifelse(!is.na(at) & at > 0, ebitda / at, NA_real_),
leverage = ifelse(!is.na(at) & at > 0,
(dltt + dlc) / at,
NA_real_)
)
# -----------------------------
# 5) Compute HHI and HHI^2 (3-digit SIC-year, sales shares)
# -----------------------------
hhi_df <- comp_vars %>%
filter(!is.na(year), !is.na(sic3), !is.na(sale), sale >= 0) %>%
group_by(year, sic3) %>%
mutate(ind_sales = sum(sale, na.rm = TRUE)) %>%
filter(ind_sales > 0) %>%
mutate(market_share = sale / ind_sales) %>%
summarise(
herfindahl = sum(market_share^2, na.rm = TRUE),
herfindahl2 = herfindahl^2,
.groups = "drop"
)
comp_vars <- comp_vars %>%
left_join(hhi_df, by = c("year", "sic3"))
# -----------------------------
# 6) Merge close-election cycles to Compustat firm-years
# -----------------------------
annual_close <- comp_vars %>%
left_join(close_cycles, by = c("state", "year")) %>%
filter(close_election_cycle == 1)
# -----------------------------
# 7) Build Panel B final sample (annual empirical analysis sample)
# -----------------------------
t1b_panelB <- annual_close %>%
filter(
year >= 1976, year <= 2021,
!is.na(at), at >= 5, # $5m cutoff; AT in millions
!is.na(xrd), # nonmissing R&D expense
!is.na(rd_intensity),
!is.na(q),
!is.na(cash_flow),
!is.na(ln_age),
!is.na(ln_sales),
!is.na(tangibility),
!is.na(profitability),
!is.na(leverage),
!is.na(herfindahl),
!is.na(herfindahl2),
!(sic >= 4900 & sic <= 4999), # utilities
!(sic >= 6000 & sic <= 6999) # financials
)
# -----------------------------
# 8) Table 1B (Panel B) summary
# -----------------------------
table1b_panelB <- panel_b_summary(t1b_panelB)
cat("Final Panel B sample N:", nrow(t1b_panelB), "\n")
print(table1b_panelB)
# -----------------------------
# 9) Comparison vs paper (diagnostic output)
# -----------------------------
paper_table1b <- tibble(
Variable = c("R&D intensity", "q", "Cash flow"),
N_paper = c(26262L, 26262L, 26262L),
Mean_paper = c(0.0876, 2.2201, -0.0389),
Median_paper = c(0.0356, 1.5143, 0.0652),
SD_paper = c(0.1503, 2.4729, 0.3727)
)
comparison <- table1b_panelB %>%
left_join(paper_table1b, by = "Variable") %>%
mutate(
dN = N - N_paper,
dMean = round(Mean - Mean_paper, 4),
dMedian = round(Median - Median_paper, 4),
dSD = round(SD - SD_paper, 4)
)
cat("\nComparison vs paper Table 1B (Panel B):\n")
print(comparison)
Editor is loading...
Leave a Comment