Skip to contents

The get_*() metrics answer “how does this organization look on this ratio?” The fiscal-health index answers a harder question: “how does this organization look overall?” It combines many standardized indicators into a small number of empirical dimensions and, optionally, a single composite score.

Rather than hard-code a formula, fiscal builds a reusable measurement model in explicit stages. Each stage is a separate function that returns an inspectable object, so the pipeline is auditable and the fitted model can be re-applied to later panel years without re-estimating anything.

fiscal_diagnostics()  ->  fiscal_triage()  ->  fiscal_dimensions()
        (screen indicators)     (decide keep/drop)   (discover structure)
                                                            |
fiscal_health_index() <- fiscal_weights() <- fiscal_health_score() <- fiscal_health_fit()
   (composite score)       (combine)          (apply model)            (freeze the model)

Two stages deliberately require human judgment — fiscal_triage() recommends but lets you override, and fiscal_dimensions() discovers but does not name dimensions. The wrapper fiscal_health() chains the mechanical stages once you have reviewed the structure.

This subsystem uses the psych package for factor-analytic diagnostics; make sure it is installed.

Prepare indicators

Indicators should be the standardized (_z) versions of the metrics, so they share a common scale. Compute them with compute_all() and select a curated, non-redundant set spanning the fiscal-health domains:

scored <- compute_all(dat10k, metrics = "z", verbose = FALSE)

indicators <- scored[, c(
  "current_z", "cash_assets_z", "cash_burn_z",      # liquidity
  "debt_assets_z", "equity_z",                       # solvency
  "surplus_margin_z", "op_reserve_z",                # performance / reserves
  "prog_exp_z", "overhead_z",                        # expense structure
  "donations_rev_z", "earned_income_z",              # revenue structure
  "netassets_growth_z"                               # growth
)]

dim(indicators)
#> [1] 10000    12

Stage 1 — diagnose

fiscal_diagnostics() computes variable-level diagnostics that distinguish indicators participating in a shared multivariate structure from those that are redundant, standalone, or noisy (correlation summaries, KMO/MSA, communalities, and so on).

diagnostics <- fiscal_diagnostics(indicators)
diagnostics
#> Fiscal indicator diagnostics
#>   Indicators: 12 
#>   Redundant pairs: 1 
#>   Near-zero variance: 0

Stage 2 — triage

fiscal_triage() turns those diagnostics into an auditable recommendation: which indicators to retain for dimension estimation, which to keep as standalone measures, and which to drop as redundant or noise. Recommendations can be overridden with the overrides argument.

triage <- fiscal_triage(diagnostics)
triage
#> Fiscal indicator triage
#>   Dimension candidates: 7 
#>   Standalone indicators: 3 
#>   Redundant exclusions: 1 
#>   Noise exclusions: 1

Stage 3 — discover dimensions

fiscal_dimensions() uses PCA (or exploratory factor analysis) to propose empirical dimensions from the retained indicators. It discovers and describes groupings; it does not name them or orient the scores — that is your call.

dimensions <- fiscal_dimensions(
  indicators,
  triage   = triage,
  method   = "pca",
  nfactors = 3,
  seed     = 42
)
dimensions
#> Fiscal dimension discovery
#>   Method: PCA 
#>   Dimensions: 3 ( specified )
#>   Multi-indicator dimensions: 3 
#>   Cross-loading indicators: 0 
#>   Unassigned indicators: 0

Stage 4 — fit a reusable model

fiscal_health_fit() freezes the reviewed dimensions into a scoring specification. The fitted object stores indicator orientation, centers, scales, missing-value treatment, and dimension coefficients — everything needed to score new data identically later.

fit <- fiscal_health_fit(indicators, dimensions = dimensions)
fit
#> Fitted fiscal-health measurement model
#>   Dimensions: 3 
#>   Standalone indicators: 3 
#>   Reference observations: 10000 
#>   Missing-value rule: median 
#>   Direction warnings: 1

Stage 5 — score

fiscal_health_score() applies the fitted model. No parameters are re-estimated; it just transforms and combines indicators into dimension scores (prefixed fh_). Because the model is frozen, the same fit can score a different panel year on the same scale.

scored_dims <- fiscal_health_score(indicators, fit)

head(grep("^fh_Dimension", names(scored_dims), value = TRUE))
#> [1] "fh_Dimension1"          "fh_Dimension2"          "fh_Dimension3"         
#> [4] "fh_Dimension1_coverage" "fh_Dimension2_coverage" "fh_Dimension3_coverage"

Stage 6 — weight

fiscal_weights() defines how dimension scores and standalone indicators combine into the composite. The default is equal weighting; min_coverage controls how much of an observation’s weighted inputs must be present before it receives a score.

weights <- fiscal_weights(fit, min_coverage = 0.5)
weights
#> Fiscal-health weights
#>   Method: equal 
#>   Measures: 6 
#>   Missing-score rule: reweight 
#>        measure measure_type original_weight    weight
#>     Dimension1    dimension               1 0.1666667
#>     Dimension2    dimension               1 0.1666667
#>     Dimension3    dimension               1 0.1666667
#>  cash_assets_z   standalone               1 0.1666667
#>    cash_burn_z   standalone               1 0.1666667
#>     overhead_z   standalone               1 0.1666667

Stage 7 — the composite index

fiscal_health_index() produces the single composite score (and a coverage column recording how much information each score is based on).

indexed <- fiscal_health_index(scored_dims, weights)

summary(indexed$fiscal_health)
#>      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
#> -2.235332 -0.233509  0.007322  0.000000  0.202435  1.984951

The shortcut: fiscal_health()

Once you have reviewed the triage and dimensions, fiscal_health() runs the mechanical stages — fit, score, weight, index — in one call:

result <- fiscal_health(indicators, dimensions = dimensions)
result
#> Fiscal-health result
#>   Mode: fit_and_score 
#>   Observations: 10000 
#>   Dimensions: 3 
#>   Standalone indicators: 3 
#>   Composite index: yes

It deliberately does not automate diagnostics, triage, or dimension discovery, because those stages require substantive review.

Scoring new data with a fitted model

The payoff of separating fit from score is reuse. Fit the model once on a reference sample (say, one panel year), then score every other year on the same scale by passing the stored fit:

fit <- fiscal_health_fit(indicators_2019, dimensions = reviewed_dims)

scores_2020 <- fiscal_health_score(indicators_2020, fit)
scores_2021 <- fiscal_health_score(indicators_2021, fit)

This keeps scores comparable across years — exactly what you need when the index feeds a longitudinal analysis of the panel you built in vignette("panel-workflow").