Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

When do bills actually get filed, and what kind of bills are they? This chapter follows the pipeline of a single congress — the monthly filing rhythm, the split between House and Senate bills, and whether each bill is national or local in scope.

Source
# --- COLLAPSIBLE SETUP BLOCK ---
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, congress_range
Source
# --- COLLAPSIBLE PARAMETER BLOCK ---
# Change SELECTED_CONGRESS to any congress the API serves (8-20). Leave it as the
# default to analyze the most recent congress. We clamp to the valid range so a
# typo cannot break the build.
available = congress_range()
SELECTED_CONGRESS = available[-1]          # <-- edit this (e.g. 13, 17, 19)
SELECTED_CONGRESS = min(max(SELECTED_CONGRESS, available[0]), available[-1])
print(f"Available congresses: {available}")
print(f"Analyzing the {SELECTED_CONGRESS}th Congress")
Available congresses: [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Analyzing the 20th Congress
Source
# --- COLLAPSIBLE CONTEXT BLOCK ---
# Show where the selected congress sits in the overall archive.
stats, _ = api_get("stats")
bills_by_congress = pd.DataFrame(stats["bills_by_congress"]).sort_values("congress")
bills_by_congress.tail()
Loading...
Source
# --- COLLAPSIBLE INGESTION BLOCK ---
# Pull the full selected congress, sorted by filing date so the monthly timeline
# reads chronologically. Deriving a month bucket lets us group filings by month.
documents = pd.DataFrame(
    fetch_all("documents", congress=SELECTED_CONGRESS, sort="date_filed", dir="asc")
)
documents["date_filed"] = pd.to_datetime(documents["date_filed"], errors="coerce")
documents["month"] = documents["date_filed"].dt.to_period("M").dt.to_timestamp()
documents[["name", "subtype", "scope", "date_filed", "title"]].head()
Loading...

Monthly Filing Pace

Each line is a bill type. Watch the opening months: a spike there is the “opening-day dump” of bills that legislators queue up before the session even formally gets going.

Source
# --- COLLAPSIBLE MONTHLY-PACE BLOCK ---
# Count bills per (month, subtype). Dropping rows without a parsed month keeps
# undated bills from collapsing into a bogus bucket.
monthly = (
    documents.dropna(subset=["month"])
    .groupby(["month", "subtype"], as_index=False)
    .size()
    .rename(columns={"size": "bills"})
)

px.line(
    monthly,
    x="month",
    y="bills",
    color="subtype",
    markers=True,
    title=f"Monthly Filing Pace in the {SELECTED_CONGRESS}th Congress",
    labels={"month": "Month filed", "bills": "Bills filed", "subtype": "Bill type"},
)
Loading...

Cumulative Filing Curve

The same data as a running total. A steep early slope that flattens later is the signature of front-loaded filing — direct evidence for H1.

Source
# --- COLLAPSIBLE CUMULATIVE BLOCK ---
# Build a cumulative count over time per bill type using a running sum.
cumulative = (
    documents.dropna(subset=["month"])
    .groupby(["month", "subtype"], as_index=False)
    .size()
    .rename(columns={"size": "bills"})
    .sort_values("month")
)
cumulative["cumulative_bills"] = cumulative.groupby("subtype")["bills"].cumsum()

px.area(
    cumulative,
    x="month",
    y="cumulative_bills",
    color="subtype",
    title=f"Cumulative Bills Filed in the {SELECTED_CONGRESS}th Congress",
    labels={"month": "Month filed", "cumulative_bills": "Cumulative bills", "subtype": "Bill type"},
)
Loading...

National vs. Local Scope

Local bills name a specific place — a school, road, hospital, or cityhood conversion. National bills change law for the whole country. The chart breaks the scope mix out by chamber.

Source
# --- COLLAPSIBLE SCOPE BLOCK ---
# Normalize the scope label (fill blanks, title-case) and count by (subtype, scope).
scope_mix = (
    documents.assign(scope=documents["scope"].fillna("Unknown").str.title())
    .groupby(["subtype", "scope"], as_index=False)
    .size()
    .rename(columns={"size": "bills"})
)

px.bar(
    scope_mix,
    x="scope",
    y="bills",
    color="subtype",
    barmode="group",
    title=f"National and Local Scope in the {SELECTED_CONGRESS}th Congress",
    labels={"scope": "Scope", "bills": "Bills", "subtype": "Bill type"},
)
Loading...

If local bars are almost entirely House-colored, that is H2 confirmed for this congress. Flip SELECTED_CONGRESS to a couple of different values to see whether the pattern holds across eras (remember: congresses 8–12 have no Senate data).

Questions to Investigate