Skip to main content

Hubbard Hall — stakeholder analysis

Author

Don Boyd

Published

September 24, 2026

1 Headline findings

This book explores Hubbard Hall’s stakeholders — donors, patrons, and class-takers. All charts are aggregates; no individual is identified. Data sources, definitions, and methods are in the Methodology appendix.

Code
import pandas as pd
import plotly.express as px
from hh.io import read_parquet
from hh.viz import style, CATEGORICAL, BLUE, SEQUENTIAL_BLUE, INK, MUTED

households = read_parquet("processed", "households_summary.parquet")
regs = read_parquet("processed", "registrations_enriched.parquet")

# headline figures (computed, not hardcoded)
_order = ["[0, 1)", "[1, 5)", "[5, 10)", "[10, 20)", "[20, ∞)"]
_d = (
    households[households["is_donor"]]
    .groupby("distance_band", observed=True)["total_donations"]
    .sum()
    .reindex(_order)
    .fillna(0)
)
_c = _d.cumsum() / _d.sum()
within5, within10, within20 = (
    f"{_c.loc[b]:.0%}" for b in ("[1, 5)", "[5, 10)", "[10, 20)")
)
n_hh = f"{len(households):,}"
n_donors = f"{int(households['is_donor'].sum()):,}"
n_patrons = f"{int(households['is_patron'].sum()):,}"
n_class = f"{int(households['is_class_taker'].sum()):,}"
n_donor_class = f"{int((households['is_donor'] & households['is_class_taker']).sum()):,}"
n_all3 = f"{int((households['is_donor'] & households['is_patron'] & households['is_class_taker']).sum()):,}"

from hh.analytics.productions import is_excluded, count_succeeded_attendees

_near = ["[0, 1)", "[1, 5)", "[5, 10)"]
_cl = regs[regs["event_majorcat"] == "class"].dropna(subset=["distance_band"])
class_w10 = f"{_cl['distance_band'].isin(_near).mean():.0%}"
_th = regs[regs["event_minorcat"] == "theater"].dropna(subset=["distance_band"])
theater_w10 = f"{_th['distance_band'].isin(_near).mean():.0%}"
_perf = regs[(regs["event_majorcat"] == "performance") & ~regs["event_name"].map(is_excluded)].copy()
_perf["attendees"] = _perf["tickets"].map(count_succeeded_attendees)
_don = _perf.merge(households[["id", "is_donor"]], on="id", how="left")
donor_att_share = f"{_don.loc[_don['is_donor'].fillna(False), 'attendees'].sum() / _don['attendees'].sum():.0%}"

# donor concentration & persistence (new donor chapters)
from hh.clean.accounts import clean_accounts
from hh.clean.donations import clean_donations
from hh.analytics.donors import annual_gifts, donor_retention

_dt = households.loc[households["is_donor"], "total_donations"]
large_donor_share = f"{(_dt >= 1000).mean():.0%}"
large_dollar_share = f"{_dt[_dt >= 1000].sum() / _dt.sum():.0%}"

_ag = annual_gifts(clean_donations(accounts=clean_accounts()))
_max_year = int(_ag["year"].max())
_ret = donor_retention(_ag)
_ret = _ret[_ret["year"] < _max_year - 1]   # drop the row whose next year is the partial current year
_ret_year = int(_ret["year"].iloc[-1])
_recent_ret = f"{_ret['retention'].iloc[-1]:.0%}"

1.1 Sunday is becoming the best-attended day

Within a theater run that plays Friday, Saturday, and Sunday, Saturday has long looked busiest — but mainly because it usually stages two shows. Per performance the three days are essentially tied, and since 2021 Sunday has drawn the most attendees per show in nearly every full-weekend run. Details in When do people come to Hubbard Hall?.

  • Donors are local. About 54% of donated dollars come from within 10 miles of Hubbard Hall (40% within 5 miles; 76% within 20 miles).
  • Giving is concentrated. Donors who have given $1,000 or more lifetime are just 13% of donor households, but account for 90% of all donated dollars. See How has giving by donation size changed over time?.
  • Donor retention: of donors active in 2024, 52% gave again the next year. See Are large donors going away?.
  • Theater draws on Saturday; music on Sunday. Saturday looks busiest mainly because it usually stages two shows, and Saturday matinees are the weekend’s weakest slot. Details are in When do people come to Hubbard Hall?.
  • Classes are local; performances are regional. 76% of class registrations come from within 10 miles, versus 55% for theater — and Sunday draws the farthest-traveling theater audience. See How far do people travel to Hubbard Hall?.
  • Strong overlap: of 4,654 households, 1,332 donate, 2,831 attend performances, and 1,853 take classes; 650 both donate and take classes, 437 do all three.
  • Donor households fill most seats: they account for 69% of all performance attendance. See What performances do donors attend?.