Who files the most bills? This chapter ranks legislators by authored bills in a
chosen congress. It uses the authors attached to each /documents record, so a
co-authored bill counts for every listed author.
Source
# --- COLLAPSIBLE SETUP BLOCK ---
from collections import Counter
import pandas as pd
import plotly.express as px
import sys
from pathlib import Path
cwd = Path.cwd().resolve()
sys.path[:0] = [str(cwd), str(cwd.parent)]
from book_utils import api_get, fetch_all, full_name, wrap_label, searchable_tableSource
# --- COLLAPSIBLE PARAMETER BLOCK ---
# Pick the congress to analyze (8-20). Author linkage improves sharply from the
# 13th Congress onward, so the most recent congress is the default.
stats, _ = api_get("stats")
available = sorted(int(row["congress"]) for row in stats["bills_by_congress"])
SELECTED_CONGRESS = available[-1] # <-- edit this (e.g. 17, 18, 19)
SELECTED_CONGRESS = min(max(SELECTED_CONGRESS, available[0]), available[-1])
print(f"Analyzing the {SELECTED_CONGRESS}th Congress")Analyzing the 20th Congress
Source
# --- COLLAPSIBLE INGESTION BLOCK ---
documents = pd.DataFrame(fetch_all("documents", congress=SELECTED_CONGRESS))
documents[["name", "subtype", "date_filed", "authors"]].head()Source
# --- COLLAPSIBLE AUTHOR-TALLY BLOCK ---
# Walk every bill's author list. Each author gets credit for the bill (co-authors
# included). We keep two counters: overall bills per author, and bills per author
# split by chamber (subtype), keyed by a stable (id, name) tuple.
author_counts = Counter()
author_chamber_counts = Counter()
for _, row in documents.iterrows():
for author in row.get("authors") or []:
author_id = author.get("id")
name = full_name(author)
if author_id and name:
key = (author_id, name)
author_counts[key] += 1
author_chamber_counts[(author_id, name, row.get("subtype"))] += 1
top_authors = pd.DataFrame(
[{"author_id": key[0], "author": key[1], "authored_bills": value} for key, value in author_counts.items()]
).sort_values("authored_bills", ascending=False)
print(f"Distinct authors with at least one linked bill: {len(top_authors)}")
top_authors.head(20)Distinct authors with at least one linked bill: 315
If top_authors is nearly empty here, you have almost certainly selected an early
congress where author links were never digitized — H3 in action.
Search Every Author¶
The table below is fully searchable and sortable in your browser. Type a
surname to find a legislator, or sort by authored_bills to see the leaders.
Source
# Client-side searchable roster of every author and their bill count.
roster = top_authors.rename(
columns={"author": "Legislator", "authored_bills": "Authored bills", "author_id": "ID"}
)[["Legislator", "Authored bills", "ID"]]
searchable_table(roster, caption=f"All bill authors in the {SELECTED_CONGRESS}th Congress (searchable)")Top Authors¶
Long Philippine names — compound surnames, quoted nicknames, and suffixes such as
Jr. or III — are wrapped onto two lines so they stay readable on the axis; the
full name is preserved on hover.
Source
# --- COLLAPSIBLE TOP-AUTHOR CHART BLOCK ---
# wrap_label breaks long names across lines so they do not overflow the y-axis;
# we keep the untruncated name available for the hover tooltip.
top25 = top_authors.head(25).sort_values("authored_bills").copy()
top25["label"] = top25["author"].map(lambda n: wrap_label(n, width=24))
fig = px.bar(
top25,
x="authored_bills",
y="label",
orientation="h",
custom_data=["author"],
title=f"Top Bill Authors in the {SELECTED_CONGRESS}th Congress",
labels={"authored_bills": "Authored bills", "label": "Legislator"},
)
# Show the full, unwrapped name in the tooltip.
fig.update_traces(hovertemplate="%{customdata[0]}<br>Authored bills: %{x}<extra></extra>")
# Extra left margin gives the wrapped names room to breathe.
fig.update_layout(margin={"l": 160}, yaxis_title="")
figThe steep drop-off from the top author downward is the concentration predicted by H1.
Top Authors by Chamber¶
Do the leaders sit in one chamber? Color splits each author’s output into House and Senate bills.
Source
# --- COLLAPSIBLE CHAMBER-SPLIT CHART BLOCK ---
# Expand the (author, chamber) counter into a tidy frame, keep only the top 15
# authors, and wrap their names for the axis.
by_type = pd.DataFrame(
[
{"author_id": author_id, "author": author, "bill_type": subtype, "bills": count}
for (author_id, author, subtype), count in author_chamber_counts.items()
]
)
top_author_ids = top_authors.head(15)["author_id"]
by_type = by_type[by_type["author_id"].isin(top_author_ids)].copy()
by_type["label"] = by_type["author"].map(lambda n: wrap_label(n, width=24))
fig = px.bar(
by_type,
x="bills",
y="label",
color="bill_type",
orientation="h",
custom_data=["author"],
title=f"Top Authors by Bill Type ({SELECTED_CONGRESS}th Congress)",
labels={"bills": "Authored bills", "label": "Legislator", "bill_type": "Bill type"},
)
fig.update_traces(hovertemplate="%{customdata[0]}<br>Bills: %{x}<extra></extra>")
fig.update_layout(margin={"l": 160}, yaxis_title="")
figIf nearly all the top bars are a single color, that is H2 confirmed for this congress.
Questions to Investigate¶
Are the highest-volume authors concentrated in one chamber?
Do prolific authors focus on local bills, national bills, or both?
Which authors appear frequently across multiple congresses?