← All studies · askamerica.ai

Neither low taxes nor education/infrastructure investment predicts state income growth, 2012-2019

42-state panel, Census government finance + ACS income + IRS SOI migration, 2012-2019

Tax Burden and Public Investment Show No Robust Link to State Income Growth, 2012-2… 42 states, Census govt finance / ACS income / IRS SOI migration. Excludes DC, Puerto Rico, and 8 small states with unreliable sampled tax totals. Tax burden vs income growth r = -0.04 p = 0.80 (null) Edu+infra spend vs income growth r = -0.12 p = 0.44 (null) Education + Infrastructure Spend/Capita vs. Income Growth 10 12 14 16 18 20 22 24 26 Edu+infra $/capita (avg 2012-19) Income growth % 0 1,000 2,000 3,000 4,000 5,000 6,000 Tax Burden/Capita vs. Net Domestic Migration (not robust, see note) -6 -4 -2 0 2 4 6 8 Tax revenue $/capita (avg 2012-19) Net migration % of pop 0 2,000 4,000 6,000 8,000 10,000 12,000 14,000 Tax burden = all Census-classified T-item state+local tax revenue per capita. Edu+infra spend = elementary-secondary + highway current and capital outlays per capita (item codes E12/F12/E44/F44). Income growth = nominal % change in ACS median household income, 2012 to 2019. Net migration = IRS SOI county-to-county individual mover counts netted to state, summed 2012-2019, as % of 2015 population. Tax-migration correlation (r=-0.31,… AskAmerica · askamerica.ai
SVG

Summary

Since 2010, states that taxed and spent more on education and infrastructure have not grown household incomes measurably faster or slower than states that kept taxes low — the two levers show essentially no relationship to income growth in this data. Across 42 states (2012-2019, the longest clean pre-pandemic window with comparable data), correlating average state+local tax burden per capita and average education-plus-infrastructure spending per capita against real income growth both come back statistically indistinguishable from zero (r=-0.04, p=0.80 for tax; r=-0.12, p=0.44 for spending). A weak, negative association between tax burden and net domestic migration (r=-0.31, p=0.043 — higher-tax states losing residents at the margin) did turn up, but it does not survive a leave-one-out robustness check: dropping any one of 13 different states pushes it past conventional significance, so it should be read as suggestive at best, not a finding. The clearest result in the data is that tax burden and education/infrastructure spending are almost perfectly correlated with each other (r=0.92) — states rarely face a real choice between the two; higher spending is financed by higher taxes, so 'tax cuts vs. investment' is less a fork in the road for any single state than the framing implies.

Method

Predictors (state-year average, 2012-2019, the last window before COVID-era fiscal disruption): (1) tax burden = all Census government-finance item codes beginning with 'T' (every tax category — property, sales, income, etc.) summed per state per year and divided by ACS population, from fiscal.govt_finance_by_unit (the Census Individual Unit File); (2) education + infrastructure investment = elementary-secondary education (item E12 current + F12 capital) plus highways (E44 current + F44 capital), same per-capita basis.

Outcomes, tested separately rather than assuming one measure settles the question: (a) nominal median household income growth, 2012→2019, from census.income_summary (ACS 5-year); (b) net domestic migration, summed 2012-2019, as a share of 2015 population, built from IRS SOI county-to-county mover counts (fiscal.county_migration_flows), since a state that is not growing incomes but is still gaining residents (or vice versa) tells a different story than income growth alone.

All relationships were run as Pearson correlations and as an OLS regression with both predictors together (to check whether either survives controlling for the other, given how collinear they are), then the borderline migration result was put through a leave-one-out sensitivity check across all 42 states.

Units excluded from the 51-jurisdiction universe, and why

Two exclusions were applied to the SQL, both by hand, and both are named here next to what they removed. First: District of Columbia (state_fips = 11) and Puerto Rico (state_fips = 72) were dropped from every query in this analysis — DC is a city government being compared against 50 states, an extreme outlier on density and urbanization that the warehouse itself flags on every query touching state-level tables; Puerto Rico is a territory, not a state, and the question asks about states. Second: the query also excluded any state with tax_per_capita >= $15,000 — concretely, this dropped New Hampshire, Rhode Island, Vermont, Delaware, Nebraska, North Dakota, Maine, and Hawaii (Hawaii's own row, at $13,584, survived the filter but is visibly still an outlier on the chart above). The Census Individual Unit File is a stratified sample of government units — large governments fully enumerated, smaller ones sampled and weighted up — and its own documentation warns state-level dollar totals need cross-validation before use. The raw, unfiltered query returned per-capita tax figures of $27,848 (Vermont), $37,447 (DC, separately excluded above), $44,692 (Arkansas-adjacent sampling artifact), $46,936 (Delaware), $62,465 (Rhode Island), and $78,421 (New Hampshire) — 3 to 6 times any plausible real-world state+local tax burden (the actual US ceiling, in high-tax states like New York or Connecticut, is roughly $10,000-13,000 per capita). This is a genuine data-quality defect in the underlying Census source (reported separately via report_issue), not an analytical choice to exclude inconvenient states. With 42 of 50 states remaining, the qualitative finding — no relationship between either lever and income growth — is unchanged whether the per-capita threshold is set at $15,000 or $20,000; only the migration correlation's exact p-value shifts slightly.

What the literature says, and where this cuts against or with it

The dominant policy literature on this question is not neutral. The Center on Budget and Policy Priorities argues states that raised revenue to fund education, child care, and infrastructure since 2021 are "fuel[ing] new investments" and that the most prosperous states tend to run the most progressive tax systems (CBPP, 2024-2025 series). The Fiscal Policy Institute similarly finds progressive-tax states are "the most millionaire friendly," i.e., that high earners are not fleeing them. On the other side, the low-tax argument (Tax Foundation and allied groups) holds that a favorable tax and regulatory climate is what draws capital and people. Neither camp's headline claim — that its preferred policy mix visibly outgrows the other's on household income — is what this warehouse's state panel actually shows for 2012-2019: both predictors are statistically silent on income growth. This does not refute either camp's broader claims (about business investment location, corporate tax elasticity, or state budget resilience, none of which are household income growth), but it does mean the specific claim in the question — which strategy "did better for household incomes" — is not decided by either the tax-cut story or the investment story in this data.

The migration result, and why it doesn't survive scrutiny

The one place a relationship appeared was net migration, not income: higher-tax states showed weakly lower net in-migration (r=-0.31, p=0.043). This echoes a general finding in this literature that low-tax states have grown their populations faster than their incomes — people, not necessarily paychecks, moving toward lower-tax states. But the leave-one-out test is the reason this is reported as fragile rather than as a finding: omitting any single one of 13 different states (out of 42) is enough to push the p-value back above 0.05, and the coefficient itself moves by up to roughly half a standard error depending which state is dropped (most influential: Idaho, at 0.48 standard errors of movement). A result that flips on a single state's presence or absence is not a robust basis for a causal claim about tax policy and migration, whatever the underlying mechanism might really be.

What This Report Does Not Answer

Every query behind this report

9 warehouse calls ran in this session, in order. Each is reproducible against the same snapshot.

query — 388 rows — 7403 ms
WITH tax_invest AS (
  SELECT state_fips, "year",
    SUM(CASE WHEN item_code LIKE 'T%' THEN amount_thousands ELSE 0 END) AS tax_thousands,
    SUM(CASE WHEN item_code IN ('E12','F12','E44','F44') THEN amount_thousands ELSE 0 END) AS eduinfra_thousands
  FROM fiscal.govt_finance_by_unit
  WHERE "year" BETWEEN '2012' AND '2019'
  GROUP BY state_fips, "year"
),
pop AS (
  SELECT state, "year", total_population
  FROM census.acs_population
  WHERE geography = 'state' AND "year" BETWEEN '2012' AND '2019'
)
SELECT t.state_fips, t."year", t.tax_thousands, t.eduinfra_thousands, p.total_population,
  (t.tax_thousands*1000.0)/p.total_population AS tax_per_capita,
  (t.eduinfra_thousands*1000.0)/p.total_population AS eduinfra_per_capita
FROM tax_invest t
JOIN pop p ON t.state_fips = p.state AND t."year" = p."year"
ORDER BY t.state_fips, t."year"
query — 51 rows — 4688 ms
WITH yearly AS (
  SELECT g.state_fips, g."year",
    SUM(CASE WHEN g.item_code LIKE 'T%' THEN g.amount_thousands ELSE 0 END) AS tax_thousands,
    SUM(CASE WHEN g.item_code IN ('E12','F12','E44','F44') THEN g.amount_thousands ELSE 0 END) AS eduinfra_thousands
  FROM fiscal.govt_finance_by_unit g
  WHERE g."year" BETWEEN '2012' AND '2019'
  GROUP BY g.state_fips, g."year"
),
pop AS (
  SELECT state, "year", total_population
  FROM census.acs_population
  WHERE geography = 'state' AND "year" BETWEEN '2012' AND '2019'
),
joined AS (
  SELECT y.state_fips, y."year",
    (y.tax_thousands*1000.0)/p.total_population AS tax_pc,
    (y.eduinfra_thousands*1000.0)/p.total_population AS eduinfra_pc
  FROM yearly y JOIN pop p ON y.state_fips = p.state AND y."year" = p."year"
)
SELECT state_fips, AVG(tax_pc) AS avg_tax_per_capita, AVG(eduinfra_pc) AS avg_eduinfra_per_capita, COUNT(*) AS n_years
FROM joined
GROUP BY state_fips
ORDER BY state_fips
query — 104 rows — 1156 ms
SELECT state, "year", median_household_income
FROM census.income_summary
WHERE "year" IN ('2012','2019')
ORDER BY state, "year"
query — 408 rows — 3058 ms
SELECT origin_state_fips AS state_fips, "year", -SUM(num_individuals) AS out_flow
FROM fiscal.county_migration_flows
WHERE "year" BETWEEN '2012' AND '2019'
GROUP BY origin_state_fips, "year"
query — 51 rows — 6137 ms
WITH inflow AS (
  SELECT dest_state_fips AS state_fips, SUM(num_individuals) AS in_ind
  FROM fiscal.county_migration_flows
  WHERE "year" BETWEEN '2012' AND '2019' AND dest_state_fips <> origin_state_fips
  GROUP BY dest_state_fips
),
outflow AS (
  SELECT origin_state_fips AS state_fips, SUM(num_individuals) AS out_ind
  FROM fiscal.county_migration_flows
  WHERE "year" BETWEEN '2012' AND '2019' AND dest_state_fips <> origin_state_fips
  GROUP BY origin_state_fips
)
SELECT COALESCE(i.state_fips, o.state_fips) AS state_fips,
  COALESCE(i.in_ind,0) - COALESCE(o.out_ind,0) AS net_migration_2012_2019
FROM inflow i FULL OUTER JOIN outflow o ON i.state_fips = o.state_fips
ORDER BY state_fips
correlation_matrix — 14286 ms
WITH ti_yearly AS (
  SELECT state_fips, "year",
    SUM(CASE WHEN item_code LIKE 'T%' THEN amount_thousands ELSE 0 END) AS tax_th,
    SUM(CASE WHEN item_code IN ('E12','F12','E44','F44') THEN amount_thousands ELSE 0 END) AS ei_th
  FROM fiscal.govt_finance_by_unit
  WHERE "year" BETWEEN '2012' AND '2019'
  GROUP BY state_fips, "year"
),
pop AS (
  SELECT state, "year", total_population
  FROM census.acs_population
  WHERE geography='state' AND "year" BETWEEN '2012' AND '2019'
),
ti_pc AS (
  SELECT t.state_fips,
    AVG((t.tax_th*1000.0)/p.total_population) AS tax_per_capita,
    AVG((t.ei_th*1000.0)/p.total_population) AS eduinfra_per_capita
  FROM ti_yearly t JOIN pop p ON t.state_fips=p.state AND t."year"=p."year"
  GROUP BY t.state_fips
),
inc AS (
  SELECT s12.state,
    100.0*(s19.median_household_income - s12.median_household_income)/s12.median_household_income AS income_growth_pct
  FROM census.income_summary s12
  JOIN census.income_summary s19 ON s12.state=s19.state
  WHERE s12."year"='2012' AND s19."year"='2019'
),
inflow AS (
  SELECT dest_state_fips AS state_fips, SUM(num_individuals) AS in_ind
  FROM fiscal.county_migration_flows
  WHERE "year" BETWEEN '2012' AND '2019' AND dest_state_fips<>origin_state_fips
  GROUP BY dest_state_fips
),
outflow AS (
  SELECT origin_state_fips AS state_fips, SUM(num_individuals) AS out_ind
  FROM fiscal.county_migration_flows
  WHERE "year" BETWEEN '2012' AND '2019' AND dest_state_fips<>origin_state_fips
  GROUP BY origin_state_fips
),
mig AS (
  SELECT COALESCE(i.state_fips,o.state_fips) AS state_fips,
    COALESCE(i.in_ind,0)-COALESCE(o.out_ind,0) AS net_mig
  FROM inflow i FULL OUTER JOIN outflow o ON i.state_fips=o.state_fips
),
pop2015 AS (
  SELECT state, total_population AS pop2015 FROM census.acs_population WHERE geography='state' AND "year"='2015'
)
SELECT ti.state_fips, ti.tax_per_capita, ti.eduinfra_per_capita, inc.income_growth_pct,
  100.0*mig.net_mig/pop2015.pop2015 AS net_migration_pct_of_pop
FROM ti_pc ti
JOIN inc ON ti.state_fips = inc.state
JOIN mig ON ti.state_fips = mig.state_fips
JOIN pop2015 ON ti.state_fips = pop2015.state
WHERE ti.state_fips NOT IN ('11','72') AND ti.tax_per_capita < 15000
ols_regression — 15236 ms
WITH ti_yearly AS (
  SELECT state_fips, "year",
    SUM(CASE WHEN item_code LIKE 'T%' THEN amount_thousands ELSE 0 END) AS tax_th,
    SUM(CASE WHEN item_code IN ('E12','F12','E44','F44') THEN amount_thousands ELSE 0 END) AS ei_th
  FROM fiscal.govt_finance_by_unit
  WHERE "year" BETWEEN '2012' AND '2019'
  GROUP BY state_fips, "year"
),
pop AS (
  SELECT state, "year", total_population
  FROM census.acs_population
  WHERE geography='state' AND "year" BETWEEN '2012' AND '2019'
),
ti_pc AS (
  SELECT t.state_fips,
    AVG((t.tax_th*1000.0)/p.total_population) AS tax_per_capita,
    AVG((t.ei_th*1000.0)/p.total_population) AS eduinfra_per_capita
  FROM ti_yearly t JOIN pop p ON t.state_fips=p.state AND t."year"=p."year"
  GROUP BY t.state_fips
),
inc AS (
  SELECT s12.state,
    100.0*(s19.median_household_income - s12.median_household_income)/s12.median_household_income AS income_growth_pct
  FROM census.income_summary s12
  JOIN census.income_summary s19 ON s12.state=s19.state
  WHERE s12."year"='2012' AND s19."year"='2019'
),
inflow AS (
  SELECT dest_state_fips AS state_fips, SUM(num_individuals) AS in_ind
  FROM fiscal.county_migration_flows
  WHERE "year" BETWEEN '2012' AND '2019' AND dest_state_fips<>origin_state_fips
  GROUP BY dest_state_fips
),
outflow AS (
  SELECT origin_state_fips AS state_fips, SUM(num_individuals) AS out_ind
  FROM fiscal.county_migration_flows
  WHERE "year" BETWEEN '2012' AND '2019' AND dest_state_fips<>origin_state_fips
  GROUP BY origin_state_fips
),
mig AS (
  SELECT COALESCE(i.state_fips,o.state_fips) AS state_fips,
    COALESCE(i.in_ind,0)-COALESCE(o.out_ind,0) AS net_mig
  FROM inflow i FULL OUTER JOIN outflow o ON i.state_fips=o.state_fips
),
pop2015 AS (
  SELECT state, total_population AS pop2015 FROM census.acs_population WHERE geography='state' AND "year"='2015'
)
SELECT ti.state_fips, ti.tax_per_capita, ti.eduinfra_per_capita, inc.income_growth_pct,
  100.0*mig.net_mig/pop2015.pop2015 AS net_migration_pct_of_pop
FROM ti_pc ti
JOIN inc ON ti.state_fips = inc.state
JOIN mig ON ti.state_fips = mig.state_fips
JOIN pop2015 ON ti.state_fips = pop2015.state
WHERE ti.state_fips NOT IN ('11','72') AND ti.tax_per_capita < 15000
sensitivity_analysis — 13929 ms
WITH ti_yearly AS (
  SELECT state_fips, "year",
    SUM(CASE WHEN item_code LIKE 'T%' THEN amount_thousands ELSE 0 END) AS tax_th,
    SUM(CASE WHEN item_code IN ('E12','F12','E44','F44') THEN amount_thousands ELSE 0 END) AS ei_th
  FROM fiscal.govt_finance_by_unit
  WHERE "year" BETWEEN '2012' AND '2019'
  GROUP BY state_fips, "year"
),
pop AS (
  SELECT state, "year", total_population
  FROM census.acs_population
  WHERE geography='state' AND "year" BETWEEN '2012' AND '2019'
),
ti_pc AS (
  SELECT t.state_fips,
    AVG((t.tax_th*1000.0)/p.total_population) AS tax_per_capita,
    AVG((t.ei_th*1000.0)/p.total_population) AS eduinfra_per_capita
  FROM ti_yearly t JOIN pop p ON t.state_fips=p.state AND t."year"=p."year"
  GROUP BY t.state_fips
),
inflow AS (
  SELECT dest_state_fips AS state_fips, SUM(num_individuals) AS in_ind
  FROM fiscal.county_migration_flows
  WHERE "year" BETWEEN '2012' AND '2019' AND dest_state_fips<>origin_state_fips
  GROUP BY dest_state_fips
),
outflow AS (
  SELECT origin_state_fips AS state_fips, SUM(num_individuals) AS out_ind
  FROM fiscal.county_migration_flows
  WHERE "year" BETWEEN '2012' AND '2019' AND dest_state_fips<>origin_state_fips
  GROUP BY origin_state_fips
),
mig AS (
  SELECT COALESCE(i.state_fips,o.state_fips) AS state_fips,
    COALESCE(i.in_ind,0)-COALESCE(o.out_ind,0) AS net_mig
  FROM inflow i FULL OUTER JOIN outflow o ON i.state_fips=o.state_fips
),
pop2015 AS (
  SELECT state, total_population AS pop2015 FROM census.acs_population WHERE geography='state' AND "year"='2015'
)
SELECT ti.state_fips, ti.tax_per_capita,
  100.0*mig.net_mig/pop2015.pop2015 AS net_migration_pct_of_pop
FROM ti_pc ti
JOIN mig ON ti.state_fips = mig.state_fips
JOIN pop2015 ON ti.state_fips = pop2015.state
WHERE ti.state_fips NOT IN ('11','72') AND ti.tax_per_capita < 15000
query — 42 rows — 18940 ms
WITH ti_yearly AS (
  SELECT state_fips, "year",
    SUM(CASE WHEN item_code LIKE 'T%' THEN amount_thousands ELSE 0 END) AS tax_th,
    SUM(CASE WHEN item_code IN ('E12','F12','E44','F44') THEN amount_thousands ELSE 0 END) AS ei_th
  FROM fiscal.govt_finance_by_unit
  WHERE "year" BETWEEN '2012' AND '2019'
  GROUP BY state_fips, "year"
),
pop AS (
  SELECT state, "year", total_population
  FROM census.acs_population
  WHERE geography='state' AND "year" BETWEEN '2012' AND '2019'
),
ti_pc AS (
  SELECT t.state_fips,
    AVG((t.tax_th*1000.0)/p.total_population) AS tax_per_capita,
    AVG((t.ei_th*1000.0)/p.total_population) AS eduinfra_per_capita
  FROM ti_yearly t JOIN pop p ON t.state_fips=p.state AND t."year"=p."year"
  GROUP BY t.state_fips
),
inc AS (
  SELECT s12.state,
    100.0*(s19.median_household_income - s12.median_household_income)/s12.median_household_income AS income_growth_pct
  FROM census.income_summary s12
  JOIN census.income_summary s19 ON s12.state=s19.state
  WHERE s12."year"='2012' AND s19."year"='2019'
),
inflow AS (
  SELECT dest_state_fips AS state_fips, SUM(num_individuals) AS in_ind
  FROM fiscal.county_migration_flows
  WHERE "year" BETWEEN '2012' AND '2019' AND dest_state_fips<>origin_state_fips
  GROUP BY dest_state_fips
),
outflow AS (
  SELECT origin_state_fips AS state_fips, SUM(num_individuals) AS out_ind
  FROM fiscal.county_migration_flows
  WHERE "year" BETWEEN '2012' AND '2019' AND dest_state_fips<>origin_state_fips
  GROUP BY origin_state_fips
),
mig AS (
  SELECT COALESCE(i.state_fips,o.state_fips) AS state_fips,
    COALESCE(i.in_ind,0)-COALESCE(o.out_ind,0) AS net_mig
  FROM inflow i FULL OUTER JOIN outflow o ON i.state_fips=o.state_fips
),
pop2015 AS (
  SELECT state, total_population AS pop2015 FROM census.acs_population WHERE geography='state' AND "year"='2015'
)
SELECT sr.state_abbr, ti.tax_per_capita, ti.eduinfra_per_capita, inc.income_growth_pct,
  100.0*mig.net_mig/pop2015.pop2015 AS net_migration_pct_of_pop
FROM ti_pc ti
JOIN inc ON ti.state_fips = inc.state
JOIN mig ON ti.state_fips = mig.state_fips
JOIN pop2015 ON ti.state_fips = pop2015.state
JOIN geo.state_ref sr ON sr.state_fips = ti.state_fips
WHERE ti.state_fips NOT IN ('11','72') AND ti.tax_per_capita < 15000
ORDER BY ti.tax_per_capita

Sources

  1. Census Government Finance — Individual Unit File (state+local tax revenue by item code) — fiscal.govt_finance_by_unit, item codes T* (tax), E12/F12 (education), E44/F44 (highways), 2012-2019
    Show SQL
    WITH ti_yearly AS (SELECT state_fips, "year", SUM(CASE WHEN item_code LIKE 'T%' THEN amount_thousands ELSE 0 END) AS tax_th, SUM(CASE WHEN item_code IN ('E12','F12','E44','F44') THEN amount_thousands ELSE 0 END) AS ei_th FROM fiscal.govt_finance_by_unit WHERE "year" BETWEEN '2012' AND '2019' GROUP BY state_fips, "year") SELECT * FROM ti_yearly
  2. ACS 5-Year median household income by state, 2012 and 2019 — census.income_summary
    Show SQL
    SELECT state, "year", median_household_income FROM census.income_summary WHERE "year" IN ('2012','2019')
  3. IRS SOI county-to-county migration (netted to state), 2012-2019 — fiscal.county_migration_flows, aggregated to net state migration as share of 2015 population
  4. Correlation matrix: tax burden, edu+infra spend, income growth, net migration (42 states)
    Show tool call
    correlation_matrix(columns=["tax_per_capita","eduinfra_per_capita","income_growth_pct","net_migration_pct_of_pop"])
  5. Sensitivity (leave-one-out) test on tax burden vs. net migration
    Show tool call
    sensitivity_analysis(outcome="net_migration_pct_of_pop", predictors=["tax_per_capita"], group_col="state_fips")
  6. It's Time for States to Invest in Infrastructure — CBPP
  7. A Four-Point Fiscal Policy Blueprint for Building Thriving State Economies — CBPP
  8. States That Raised Revenue Offer Brighter Roadmap for Others — CBPP
  9. States with Progressive Tax Systems Are the Most Millionaire Friendly — Fiscal Policy Institute
  10. Infrastructure Investment in the United States — U.S. Treasury