Replicating Kogan, Papanikolaou, Seru & Stoffman (2017): Technological Innovation, Resource Allocation, and Growth¶
U.S.E. Finance Data Hub / WRDS Database: US Patents¶
Database used: WRDS US Patents (Data Hub guide)
What this notebook does¶
Kogan et al. (2017) construct a measure of the economic value of patents using stock market reactions to patent grants. When the USPTO grants a patent to a publicly traded firm, the firm's stock price reaction on the grant date provides a market-based estimate of the patent's economic value. Aggregating these patent values to the firm level and across the economy, the authors study how innovation drives economic growth and resource allocation.
The WRDS US Patents database provides patent-level data linked to publicly traded companies, including patent numbers, grant dates, assignees (matched to GVKEY), citation counts, and technology classifications.
- Pull patent metadata and firm linkages from WRDS US Patents (
wrdsapps_patents) via the WRDS API - Construct firm-level innovation measures (patent counts, citation-weighted counts)
- Examine the distribution of patenting across firms and over time
- Merge with Compustat to relate innovation output to firm characteristics
- Compare our findings to the published 2017 results
Learning objectives¶
- Practice connecting to WRDS and querying the US Patents database
- Understand how patents are linked to publicly traded firms (GVKEY matching)
- Learn how to measure innovation output at the firm level using patent data
- Build intuition for the relationship between technological innovation and firm value
Requirements to run this notebook¶
- A valid WRDS account with WRDS US Patents access (ask the Finance Data Hub if you don't have one yet)
pip install wrds pandas numpy matplotlib seaborn- You will be prompted for your WRDS username/password the first time you connect (or set up a
.pgpassfile, see the WRDS Python guide)
1. Setup and WRDS connection¶
import wrds
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_theme(style="whitegrid", palette="deep")
pd.set_option("display.max_columns", 50)
db = wrds.Connection()
2. Identifying the US Patents variables we need¶
The US Patents data on WRDS lives in the wrdsapps_patents library with three tables.
uspatents_meta (~2.8 million rows) contains patent-level metadata.
| Field | Description |
|---|---|
patnum |
USPTO patent number |
grantdate |
Date the patent was granted |
appldate |
Date the patent application was filed |
ptype |
Patent type (utility, design, plant, etc.) |
ee_name |
Assignee (entity) name |
ee_country |
Assignee country |
ee_state |
Assignee U.S. state |
backward_cites |
Number of backward citations (prior art) |
forward_cites |
Number of forward citations (impact measure) |
uspatents_gvkey_linking (~1.5 million rows) links patents to Compustat firms.
| Field | Description |
|---|---|
patnum |
USPTO patent number |
gvkey |
Compustat GVKEY of the assignee firm |
wrds_score |
Quality score of the patent-firm link |
initial_assign |
Whether the firm was the original assignee |
subsidiary_flag |
Whether the assignee is a subsidiary |
uspatents_citations (~54 million rows) contains patent-to-patent citation links.
We join uspatents_meta with uspatents_gvkey_linking on patnum to get patents matched to publicly traded firms.
# Pull all patents matched to publicly traded firms.
# We join the metadata table with the GVKEY linking table.
query = """
SELECT m.patnum, m.grantdate, m.appldate, m.ptype,
m.ee_name, m.ee_country, m.ee_state,
m.backward_cites, m.forward_cites,
l.gvkey, l.wrds_score, l.initial_assign, l.subsidiary_flag
FROM wrdsapps_patents.uspatents_meta m
INNER JOIN wrdsapps_patents.uspatents_gvkey_linking l
ON m.patnum = l.patnum
"""
df = db.raw_sql(query, date_cols=["grantdate", "appldate"])
print(f"Rows pulled: {len(df):,}")
print(f"Unique patents: {df['patnum'].nunique():,}")
print(f"Unique firms (gvkey): {df['gvkey'].nunique():,}")
print(f"Grant date range: {df['grantdate'].min()} to {df['grantdate'].max()}")
df.head()
3. Cleaning the sample¶
# Keep only utility patents (the standard in the innovation literature)
if "ptype" in df.columns:
print(f"Patent types: {df['ptype'].value_counts().to_dict()}")
df = df[df["ptype"] == "utility"] if "utility" in df["ptype"].values else df
# Keep only patents with valid grant dates
df = df.dropna(subset=["grantdate", "gvkey"])
# Extract grant year
df["grant_year"] = df["grantdate"].dt.year
# Keep high-quality firm links (wrds_score >= 3 if available)
if "wrds_score" in df.columns and df["wrds_score"].notna().any():
before = len(df)
df = df[df["wrds_score"] >= 3]
print(f"Kept {len(df):,} / {before:,} rows with wrds_score >= 3")
# Fill missing citation counts with zero
df["forward_cites"] = df["forward_cites"].fillna(0)
df["backward_cites"] = df["backward_cites"].fillna(0)
# One row per patent (drop duplicates from multi-gvkey matches)
df = df.drop_duplicates(subset=["patnum"])
print(f"\nCleaned dataset: {len(df):,} patents")
print(f"Unique firms: {df['gvkey'].nunique():,}")
print(f"Grant years: {int(df['grant_year'].min())} to {int(df['grant_year'].max())}")
4. Measuring innovation output¶
Kogan et al. (2017) propose a market-value-based measure of patent importance. Without stock return data (which requires CRSP), we construct simpler but widely used measures of innovation output.
Simple patent count per firm per year is the most basic measure. However, patent counts treat all patents equally, even though patent value is highly skewed.
Citation-weighted patent count adjusts for quality by weighting each patent by its forward citation count (the number of subsequent patents that cite it). More-cited patents are more impactful innovations.
$$\text{Citation-weighted patents}_{i,t} = \sum_{p \in \text{patents}_{i,t}} (1 + \text{forward\_cites}_p)$$
The key empirical regularity is that patenting is highly concentrated: a small number of firms account for a large share of all patents (and an even larger share of citation-weighted patents).
# Compute firm-year level innovation measures
firm_year = df.groupby(["gvkey", "grant_year"]).agg(
n_patents=("patnum", "nunique"),
total_forward_cites=("forward_cites", "sum"),
avg_forward_cites=("forward_cites", "mean"),
total_backward_cites=("backward_cites", "sum"),
).reset_index()
# Citation-weighted patent count
firm_year["cite_weighted"] = firm_year["n_patents"] + firm_year["total_forward_cites"]
print(f"Firm-year observations: {len(firm_year):,}")
print(f"Unique firms: {firm_year['gvkey'].nunique():,}")
print(f"\nFirm-Year Innovation Summary Statistics:\n")
print(firm_year[["n_patents", "total_forward_cites", "avg_forward_cites",
"cite_weighted"]].describe().round(2))
# Concentration: top 10% of firms by patent count
top10_cutoff = firm_year["n_patents"].quantile(0.90)
top10_share = firm_year.loc[firm_year["n_patents"] >= top10_cutoff, "n_patents"].sum() / firm_year["n_patents"].sum()
print(f"\nTop 10% of firm-years account for {top10_share:.1%} of all patents")
# Merge with Compustat to relate innovation to firm characteristics
compustat = db.raw_sql("""
SELECT gvkey, fyear, at, sale, xrd, sich
FROM comp.funda
WHERE indfmt = 'INDL' AND datafmt = 'STD'
AND consol = 'C' AND popsrc = 'D'
AND at > 0
""")
firm_year = firm_year.merge(
compustat, left_on=["gvkey", "grant_year"],
right_on=["gvkey", "fyear"], how="left"
)
matched = firm_year["at"].notna().sum()
print(f"Matched to Compustat: {matched:,} / {len(firm_year):,}")
# R&D intensity and patent productivity
firm_year["rd_intensity"] = firm_year["xrd"] / firm_year["at"]
firm_year["log_at"] = np.log(firm_year["at"].clip(lower=0.01))
# Summary by R&D intensity quartile
rd_firms = firm_year[firm_year["xrd"].notna() & (firm_year["xrd"] > 0)]
rd_firms["rd_quartile"] = pd.qcut(rd_firms["rd_intensity"], 4,
labels=["Q1 (low R&D)", "Q2", "Q3", "Q4 (high R&D)"])
rd_stats = rd_firms.groupby("rd_quartile").agg(
median_patents=("n_patents", "median"),
median_cites=("avg_forward_cites", "median"),
n_obs=("gvkey", "count")
)
print(f"\nPatenting by R&D Intensity Quartile:\n")
print(rd_stats.round(2))
5. Visualizing the results¶
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Aggregate patent grants per year
yearly = df.groupby("grant_year")["patnum"].nunique()
yearly = yearly[(yearly.index >= 1976) & (yearly.index <= 2023)]
axes[0].plot(yearly.index, yearly.values, "o-", color="steelblue", markersize=3)
axes[0].set_title("U.S. Patent Grants to Public Firms Per Year")
axes[0].set_xlabel("Grant Year")
axes[0].set_ylabel("Number of Patents")
# Distribution of patents per firm (log scale)
firm_totals = df.groupby("gvkey")["patnum"].nunique()
axes[1].hist(np.log10(firm_totals.clip(lower=1)), bins=50,
color="darkorange", edgecolor="white")
axes[1].set_title("Distribution of Total Patents per Firm (log10 scale)")
axes[1].set_xlabel("log10(Number of Patents)")
axes[1].set_ylabel("Number of Firms")
plt.tight_layout()
plt.show()
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Top 20 patenting firms
top20 = df.groupby(["gvkey"]).agg(
ee_name=("ee_name", "first"),
n_patents=("patnum", "nunique")
).nlargest(20, "n_patents")
axes[0].barh(top20["ee_name"].values[::-1], top20["n_patents"].values[::-1],
color="steelblue")
axes[0].set_title("Top 20 Patenting Firms (all years)")
axes[0].set_xlabel("Number of Patents")
# Firm size vs. innovation scatter
if "log_at" in firm_year.columns:
mask = firm_year["at"].notna()
axes[1].scatter(firm_year.loc[mask, "log_at"],
np.log10(firm_year.loc[mask, "n_patents"].clip(lower=1)),
alpha=0.1, s=5, color="steelblue")
axes[1].set_title("Firm Size vs. Patenting Activity")
axes[1].set_xlabel("log(Total Assets)")
axes[1].set_ylabel("log10(Number of Patents)")
plt.tight_layout()
plt.show()
6. Comparing to Kogan, Papanikolaou, Seru & Stoffman (2017): What's the same, what's different¶
What replicates cleanly
The WRDS US Patents database provides patent-level data matched to publicly traded firms, which is the starting point for Kogan et al.'s analysis. The basic distribution of patents across firms should confirm the extreme concentration of innovation output: a small number of firms (pharmaceutical companies, tech giants) account for the vast majority of patents. The time trend of annual patent grants should show the well-documented explosion in patenting since the 1980s. The positive relationship between firm size (total assets) and patent counts should be clearly visible, and the link between R&D spending and patenting intensity confirms that inputs to innovation translate into measurable outputs.
Where a modern WRDS-based replication necessarily differs from the original
Patent valuation. The paper's key innovation is measuring patent value using stock market reactions on the patent grant date. This requires daily stock returns from CRSP, which is not available at UU. Without CRSP, we use patent counts and citation-weighted counts as alternative measures of innovation, but we cannot compute the market-based value measure that is the paper's main contribution.
Citation truncation. Forward citation counts are right-censored because recently granted patents have not yet accumulated their full citation count. The paper addresses this by adjusting for the expected citation trajectory. Without this adjustment, recent patents will appear less important than they actually are. Students should restrict the sample to patents at least 5 years old for citation analysis.
GVKEY linking quality. The
wrdsapps_patentslinking table includes awrds_scorefield that measures the quality of the patent-to-firm match. We filter onwrds_score >= 3to keep reliable matches, but some valid links may be lost. Thesubsidiary_flagfield indicates whether the patent was assigned to a subsidiary rather than the parent firm.Aggregate implications. The paper's second contribution links firm-level patent values to macroeconomic growth. This requires combining patent data with aggregate economic indicators (GDP, TFP), which goes beyond the patent database alone.
References¶
- Kogan, L., Papanikolaou, D., Seru, A. & Stoffman, N. (2017). Technological Innovation, Resource Allocation, and Growth. The Quarterly Journal of Economics, 132(2), 665–712.
- WRDS US Patents guide: https://uufinance.github.io/data/wrds/databases/us-patents/
- WRDS US Patents access page: https://wrds-www.wharton.upenn.edu/pages/get-data/wrds-us-patents/
- WRDS Python/API setup guide: https://uufinance.github.io/data/wrds/notebook/
Prepared for the U.S.E. Finance Data Hub as a database-tutorial template.