governance and panel990
The governance package scores nonprofit governance; it does not retrieve data. Retrieving, normalizing, and assembling IRS 990 efile tables is the job of the companion panel990 package.
| Package | Role |
|---|---|
| panel990 | Download efile tables, filter to a sample frame, merge to one row per filing, build panels. |
| governance | Normalize the governance fields into a feature matrix (get_features()) and compute the governance index (get_scores()). |
governance calls panel990 for you through get_governance_data() and get_governance_scores(), so most users never touch panel990 directly. This vignette shows both the one-call path and the underlying panel990 workflow for when you want more control. Install panel990 first:
remotes::install_github("Nonprofit-Open-Data-Collective/panel990")Required 990 tables
The governance index is built from four efile tables — Form 990 Parts IV, VI, and XII and Schedule M. Each is a panel990 table name you pass to panelize() (Part XII also has the short alias "P12").
| Part / Schedule | Governance information it contains | panel990 table name |
|---|---|---|
| Part IV | Required-schedule triggers: audited financials, interested-person business transactions, large non-cash and art/historical contributions | F9-P04-T00-REQUIRED-SCHEDULES |
| Part VI | Governing body composition (voting / independent members) and governance & management policies (conflict-of-interest, whistleblower, document retention, CEO compensation process, document availability) | F9-P06-T00-GOVERNANCE |
| Part XII | Financial-reporting method (cash / accrual / other) and audit oversight | F9-P12-T00-FINANCIAL-REPORTING |
| Schedule M | Review process for unusual non-cash gifts | SM-P01-T00-NONCASH-CONTRIBUTIONS |
Full 990 filers only. The governance index does not apply to 990EZ returns — they omit these Parts. Restrict the data to
RETURN_TYPE == "990"(shown below).get_governance_data()does this for you.
Option 1: one call (recommended)
get_governance_data() builds the full-990 sample frame, downloads the four tables, keeps the needed columns, and merges them to one row per filing — ready for get_features().
library(governance)
# Assemble the raw fields for a tax year (full 990 filers only)
dat <- get_governance_data(years = 2022)
features <- get_features(dat)
scores <- get_scores(features)
# ...or the whole pipeline in one call
scores <- get_governance_scores(years = 2022)Restrict to specific organizations with eins (formatted EIN2 values), or read tables you have already downloaded by pointing the source at a directory:
scores <- get_governance_scores(years = 2022, eins = c("EIN-43-2031361"))
src <- panel990::data_source(root = "path/to/efile/csvs")
dat <- get_governance_data(years = 2022, source = src)Option 2: the panel990 workflow
Under the hood, get_governance_data() assembles a sample frame and calls panel990::panelize(). Doing it yourself gives full control — additional filters, multiple years, BMF traits, DuckDB backends, and so on.
The form_type filter is how you keep only full 990 filers: it is a built-in sample-frame alias mapping to the RETURN_TYPE column, so form_type = "990" keeps only RETURN_TYPE == "990".
library(panel990)
gov_tables <- c(
"F9-P04-T00-REQUIRED-SCHEDULES",
"F9-P06-T00-GOVERNANCE",
"F9-P12-T00-FINANCIAL-REPORTING",
"SM-P01-T00-NONCASH-CONTRIBUTIONS"
)
# Sample frame: keep full 990 filers (RETURN_TYPE == "990") via the SFW filter
sfw <- create_sfw("governance panel", form_type = "990")
# Download, filter, and merge the tables into one row per filing
panel <- panelize(sfw = sfw, tables = gov_tables, years = 2022)
dat <- as.data.frame(panel)
scores <- get_scores(get_features(dat))create_sfw() accepts other convenience filters too — for example state = "CA" or years = 2018:2022 — which panelize() pushes down to the download step. See ?panel990::create_sfw and ?panel990::panelize.
A note on normalization
get_features() performs the governance-specific normalization itself (blank checkboxes and counts become 0 regardless of how the source encoded them), so the tables are imported raw.
panel990::panel_normalize() only rewrites blank financial fields (revenue, expenses, balance sheet) to zero; it leaves the governance checkbox and count fields untouched. It is therefore safe to include in a shared pipeline but is neither required nor sufficient for the governance features — get_features() is what normalizes them. (Do not run the general panel990::normalize() on the governance checkboxes before get_features(): it converts them to logicals, which get_features() does not expect.)
The input contract
However you obtain the data, get_features() expects a data frame with one row per filing containing ORG_EIN plus the raw fields below. Values may be encoded as "true"/"false", "1"/"0", "X" flags, or blanks (NA or ""); get_features() normalizes all of these.
| Part/Schedule | Variable | 990 line | Question |
|---|---|---|---|
| Part IV | F9_04_AFS_IND_X |
12a | Independent audited financial statements? |
| Part IV | F9_04_BIZ_TRANSAC_DTK_X |
28a | Business transaction with a current/former officer, director, trustee, or key employee (DTK)? |
| Part IV | F9_04_BIZ_TRANSAC_DTK_FAM_X |
28b | …with a family member of a DTK? |
| Part IV | F9_04_BIZ_TRANSAC_DTK_ENTITY_X |
28c | …with a 35%-controlled entity of a DTK? |
| Part IV | F9_04_CONTR_NONCSH_MT_25K_X |
29 | Received > $25,000 in non-cash contributions? |
| Part IV | F9_04_CONTR_ART_HIST_X |
30 | Received contributions of art, historical treasures, etc.? |
| Part VI | F9_06_GVRN_NUM_VOTING_MEMB |
1a | Number of voting governing body members |
| Part VI | F9_06_GVRN_NUM_VOTING_MEMB_IND |
1b | Number of independent voting members |
| Part VI | F9_06_GVRN_DTK_FAMBIZ_RELATION_X |
2 | Family/business relationship among officers, directors, trustees, key employees? |
| Part VI | F9_06_GVRN_DELEGATE_MGMT_DUTY_X |
3 | Delegation of management duties? |
| Part VI | F9_06_GVRN_DOC_GVRN_BODY_X |
8a | Contemporaneous documentation of governing body meetings? |
| Part VI | F9_06_POLICY_FORM990_GVRN_BODY_X |
11a | Form 990 provided to governing body? |
| Part VI | F9_06_POLICY_COI_X |
12a | Conflict of interest policy? |
| Part VI | F9_06_POLICY_COI_DISCLOSURE_X |
12b | Annual disclosure of interests? |
| Part VI | F9_06_POLICY_COI_MONITOR_X |
12c | Regular monitoring and enforcement of COI policy? |
| Part VI | F9_06_POLICY_WHSTLBLWR_X |
13 | Whistleblower policy? |
| Part VI | F9_06_POLICY_DOC_RETENTION_X |
14 | Document retention and destruction policy? |
| Part VI | F9_06_POLICY_COMP_PROCESS_CEO_X |
15a | Independent compensation process for CEO/top official? |
| Part VI | F9_06_DISCLOSURE_AVBL_OTH_X |
18 | Governing documents available: other |
| Part VI | F9_06_DISCLOSURE_AVBL_OTH_WEB_X |
18 | …on another organization’s website |
| Part VI | F9_06_DISCLOSURE_AVBL_REQUEST_X |
18 | …upon request |
| Part VI | F9_06_DISCLOSURE_AVBL_OWN_WEB_X |
18 | …on the organization’s own website |
| Part XII | F9_12_FINSTAT_METHOD_ACC_ACCRU_X |
1 | Accounting method: accrual |
| Part XII | F9_12_FINSTAT_METHOD_ACC_CASH_X |
1 | Accounting method: cash |
| Part XII | F9_12_FINSTAT_METHOD_ACC_OTH |
1 | Accounting method: other (free text) |
| Schedule M | SM_01_REVIEW_PROCESS_UNUSUAL_X |
31 | Review process for unusual noncash gifts? |
For a full data dictionary see the irs990efile data dictionary. The tables themselves live in the NCCS efile v2.1 archive (https://nccs-efile.s3.us-east-1.amazonaws.com/public/efile_v2_1/{TABLE}-{YEAR}.CSV, years 2009–2024); browse them in the efile v2.1 catalog.