Skip to content

Seed the services and transportation block from SAS/AIES expenses (#564) - #718

Draft
WesIngwersen wants to merge 18 commits into
step3_service_expense_resourcefrom
step3_service_seed
Draft

Seed the services and transportation block from SAS/AIES expenses (#564)#718
WesIngwersen wants to merge 18 commits into
step3_service_expense_resourcefrom
step3_service_seed

Conversation

@WesIngwersen

@WesIngwersen WesIngwersen commented Aug 25, 2026

Copy link
Copy Markdown
Member

Stacked on #717.

#717 measured the resource — 97 BEA detail industries, 51.4% impact-weighted
reach on N
. This builds the extractor and the seed that reach it, and then
corrects three things in it against what BEA says it used (Table C2, added
to the repo as bea_2017_benchmark_sources.md).

⚠️ Naming: SAS is the Service Annual Survey but it also publishes
utilities and transportation. The block is named for the sectors it covers, not
the survey — services_transport_* throughout. sector_coverage() states the
scope on BEA's own sector taxonomy.

What is new

Census_AIES_Service_Expenses — a new source on timeseries/aies/exp02,
carrying the 13 sectors that Census_AIES_Expenses' basic endpoint returns as
well-formed zeros. ⚠️ Deliberately a separate source, not a repoint: basic
uniquely publishes EXPS_CSTMTOT_DVAL and RCPT_TOT_VAL, the controls the
materials additivity check uses. Verified exp02 ⊇ basic on manufacturing —
648 rows, 0 differing cells.

⚠️ Census_AIES.py's parse filters on a hardcoded AIES_EXPENSE_FLOWS
whitelist
, so seven sector-specific cells were being silently dropped.

services_transport_seed(year)100 BEA detail columns, $6,779B,
across seven BEA sectors: FIRE $2,422B, PROF $1,196B, 6 $1,027B, 51
$753B, 7 $622B, 48TW $483B, 81 $276B.

The scores

Against a frozen 2017, on BEA's published summary:

year dollar impact (N) (on D)
2020 +0.5% +10.2% +22.6%
2021 +1.4% +10.8% +25.1%
2022 +2.3% +9.2% +22.6%
2023 −5.0% −4.1% −2.9%

⚠️ Impact means N — total kg CO2e per dollar, direct plus indirect
(Wes, 2026-08-25). A Use cell is an entry in A, so an error propagates through
the whole Leontief inverse; what a row is worth is its total embodied emissions.
D overstated this seed by more than double, because it over-weights
electricity — which is nearly all direct, and which the survey names well.

⚠️ Read the impact column as the result, the dollar column as a sign check.
+0.5–2.3% is still inside the noise §S4 rejected other columns on.

⚠️ The gain is carried by weight, not by count — 22, 21 and 17 of 33 columns
individually win, and ORE alone carries 59–83%.

✅ Two columns §S4 rejected on dollars win consistently on N:
622 (+11.4/+12.3/+27.5%) and 722 (+7.5/+11.2/+10.6%).
⚠️ 5412OP's rejection stands (−9.0/−7.7/−2.0%) — and so does 81's
(−0.8/+2.9/+0.6%). ⚠️ 81 looked like a win on D and is flat on N,
which is the kind of verdict the weighting change decides.

⚠️ The row ranking #717 re-sorted the plan on is a D ranking. On N:
manufacturing 28.0% → 44.7%, utilities 26.1% → 13.7%, agriculture
23.7% → 16.4%, and all other (services) 7.2% → 14.3%.

⚠️ Three corrections, all from checking against BEA's C2

1. The index denominator was wrong

relative_index divides each item's ratio by the industry's own growth — and
that growth was computed over the mapped items only. BEA names fourteen
SAS items; SAS_ITEM_TO_BEA covers thirteen, and the two it cannot map —
Expensed purchases of other materials, parts, and supplies ($327B) and
All other operating expenses ($2,005B) — are $2.33T of the panel's $13.1T.

Using the mapped subset silently asserted those two grew at the mapped average.
industry_growth() now runs over the whole input bill. ✅ Worth +0.8pp on
impact — and it costs the 2020 dollar figure. The denominator is right now,
not tuned.

2. ❌ A scope guard was built and is withdrawn

It refused industries whose own input bill contradicted their published column,
named six (including 22), and looked worth on impact.

⚠️ On the full bill instead of the mapped subset, only 486 fails — and
486 is one of the seed's better columns.
5122 swings 0.57 → 1.36. The
six-industry list was measuring the item map's incompleteness and reporting it
as a fact about the survey. A level disagreement does not predict a bad
shape.
published_agreement() and contradicting_industries() stay as
diagnostics; nothing is excluded on them.

3. ❌ Utilities is held at the benchmark — on the mechanism, not the score

BEA did use SAS for utilities, so "different universe" is unavailable. What is
left: utilities' column moves on a price-driven reweighting211 goes
7.7% → 23.3% as the column rises $160.4B → $258.4B, while the survey's
purchased-fuels line falls $102.5B → $79.6B. A relative share index divides
out common movement, which is the signal. It was the largest single drag:
−119% of the aggregate gain at 2020.

⚠️ Its drift is now unaddressed, not fixed — θ is 0.0 across this surge. The
EIA route (861/861M, form 176) is #719.

⚠️ Scope: this is utilities' own input column — 3 columns, $160B.
Purchased electricity and fuels as rows in the other 100 columns are still
survey-seeded, and that is the high-impact part (221100 alone is 13.0% of
total impact on N, the single largest row).

⚠️ Part of the rise from +4.5–5.0% is a smaller denominator, since holding
22 back removes a column carrying half the block's frozen dissimilarity. ✅ The
seeded error falls too (0.0318 → 0.0128), so it is not only rescaling — but do
not quote this as "5× better".

❌ 2023 is refused

SURVEY_CHANGE_YEARS. AIES against a SAS 2017 base crosses the survey change
and the sas-17/sas-22 rebenchmark — splice step median |log| 0.203 against
0.118 within-instrument. ✅ When AIES publishes a second year, 2024-on-2023
is within-instrument and this becomes usable.

⚠️ 2018 and 2019 get no seed and are not interpolated — they sit on the seam.

Also fixed: a units error

⚠️ The SAS FBA is USD, the AIES FBA is Thousand USD. Invisible in
relative_index (it divides out), wrong for anything reading a level.
⚠️ The 2023 failure is not this bug — unchanged after the fix.

Still open

Gates

black, ruff, mypy clean; targeted Step 3 tests pass (28). Includes a merge
of #715's black fix (ec27a634), which was the format gate failing on #715
and #717 as well as here.

WesIngwersen and others added 3 commits August 25, 2026 13:23
Builds the source re-evaluation in the parent commit into an extractor and a
seed over 103 BEA detail columns, against the one column ORE covered.

New source Census_AIES_Service_Expenses reads timeseries/aies/exp02, which
carries the 13 service sectors that Census_AIES_Expenses' basic endpoint
returns as well-formed zeros. It is deliberately a separate source rather
than a repoint: basic uniquely publishes EXPS_CSTMTOT_DVAL and RCPT_TOT_VAL,
the controls the materials additivity check uses, and exp02 does not have
them. Verified exp02 is a strict superset of basic on manufacturing -- 648
rows, 0 differing cells.

Census_AIES.py's parse filters on a hardcoded AIES_EXPENSE_FLOWS whitelist,
so seven sector-specific cells were silently dropped until added there:
transport fuel, transport repair, freight, medical supplies, insurance,
professional liability and printing.

service_seed(year) moves 103 columns holding $6,939B. relative_index now
accepts an injected panel so a year can be read from a different survey than
its base, and SEEDED_ELSEWHERE keeps the seed off the sectors inputs_structure
already covers.

Scored against a frozen 2017 on BEA's published summary, the seed wins on the
SAS years and wins three to four times bigger on impact than on dollars:

  2020  +1.1% dollar  +3.6% impact
  2021  +1.2% dollar  +3.8% impact
  2022  +0.4% dollar  +4.2% impact
  2023  -5.5% dollar  -5.8% impact

That is the re-evaluation's thesis holding up -- the survey names purchased
electricity and fuels for every industry, and those are the rows the model
weights. The dollar gains are inside the noise S4 rejected other columns on,
so the impact column is the case for this seed and should be quoted as such.

2023 is refused. It is AIES read against a SAS 2017 base, so it crosses the
survey change on top of the sas-17/sas-22 rebenchmark; the splice step is a
median |log| of 0.203 against 0.118 within-instrument. When AIES publishes a
second year, 2024 against 2023 is within-instrument and this becomes usable.
2018 and 2019 get no seed and are not interpolated -- they sit on the seam.

Also fixes a units error the work surfaced: the SAS FBA is USD and the AIES
FBA is Thousand USD, so the two panels need different divisors to meet in $M.
It is invisible in relative_index, which divides it out, and wrong for anything
reading a level. The 2023 score failure is not caused by it and is unchanged
after the fix.

Gates: ruff check and mypy clean; pytest NOT run on this commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… sectors

Three corrections to the seed the parent commit built, all from checking our
sourcing against what BEA says it used (Table C2, now in the repo as
bea_2017_benchmark_sources.md).

1. The denominator was wrong. relative_index divides each item's ratio by the
industry's own growth, and that growth was computed over the MAPPED items only.
BEA names fourteen SAS items for these industries; SAS_ITEM_TO_BEA covers
thirteen, and the two it cannot -- expensed purchases of other materials, parts
and supplies ($327B) and all other operating expenses ($2,005B) -- are $2.33T
of the panel's $13.1T. Using the mapped subset silently asserted those two grew
at the mapped average. industry_growth() now runs over intermediate_items(),
the whole input bill. Worth about +0.8pp on impact, and it costs the 2020
dollar figure: the denominator is right now, not tuned.

2. A scope guard was built and is withdrawn. It refused industries whose own
input bill contradicted their published column, named six of them, and appeared
to be worth 5x on impact. Scored on the full bill instead of the mapped subset,
only 486 fails -- and 486 is one of the seed's better columns (+54.7% at 2020),
so a level disagreement does not predict a bad shape. The six-industry list was
measuring the item map's incompleteness and reporting it as a fact about the
survey. published_agreement() and contradicting_industries() stay as
diagnostics so the negative result stays reproducible; nothing is excluded.

3. Utilities is held at the benchmark instead (NOT_SEEDED), on the mechanism
rather than on the score. BEA did use SAS for utilities, so "different universe"
is not available. What is left is that utilities' column moves on a price-driven
reweighting -- 211 goes 7.7% -> 23.3% of it as the column rises 160.4B -> 258.4B
-- while the survey's purchased-fuels line falls 102.5B -> 79.6B. A relative
share index divides out common movement, which is the signal here. It was the
largest single drag: -119% of the aggregate gain at 2020. Its drift is now
unaddressed, not fixed; the EIA route (861/861M, form 176) is filed separately.

This is only utilities' OWN input column, 3 detail columns and $160B. The
purchased-electricity and purchased-fuels ROWS in the other 100 columns are
still survey-seeded, which is the high-impact part of the seed.

Scored against a frozen 2017 on BEA's published summary:

  2020  +0.5% dollar  +22.6% impact
  2021  +1.4% dollar  +25.1% impact
  2022  +2.3% dollar  +22.6% impact

Part of the rise from +4.5-5.0% is a smaller denominator: holding 22 back
removes a column carrying half the block's frozen dissimilarity. The seeded
error falls too, 0.0318 -> 0.0128, so it is not only rescaling -- but this
should not be quoted as "5x better" than the figures with 22 in.

Renamed service_* to services_transport_* throughout, and the module with it.
SAS is the Service Annual Survey but it also publishes utilities and
transportation, and naming the block after the survey blurred exactly the
distinction that decided NOT_SEEDED. sector_coverage() states the scope on
BEA's own sector taxonomy: seven sectors, FIRE PROF 6 51 7 48TW 81, all
services or transportation. New CLI flags --sectors, --by-column, --agreement.

Also adds services_transport_score_by_column, which is what re-scores S4's
per-column no-goes on impact: 622, 722 and 486 win consistently, 5412OP does
not. The aggregate is carried by weight, not count -- ORE alone is 71-78%.

Gates: black, ruff, mypy clean; targeted Step 3 tests pass (28).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@WesIngwersen WesIngwersen changed the title Extract AIES service expenses, and seed the whole service block (#564) Seed the services and transportation block from SAS/AIES expenses (#564) Aug 25, 2026
WesIngwersen and others added 15 commits August 25, 2026 15:30
The previous commit renamed the seed module and left service_expense_resource.py
importing the old name, which broke it at import time. My blast-radius grep had
excluded the resource file from its own results, so the breakage did not show up
until something imported it.

Renames the resource module to match and repoints every reference, in
Census_AIES.py's source note as well.

Gates: black, ruff, mypy clean; both modules import.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wes, 2026-08-25: rank by total kg CO2e per dollar -- direct plus indirect --
not by the direct slice. A Use cell is an entry in A, so an error in it
propagates through the whole Leontief inverse; what a row is worth getting
right is its total embodied emissions. Some sectors have much higher N than D
because their indirect is large, and their inputs are still very important.

total_impact_intensity() computes N = C B L with L = (I - (Adom + Aimp))^-1,
matching the inverse cornerstone_disagg_pipeline builds N from. impact_intensity
now takes kind= and defaults to 'total'; direct_impact_intensity keeps D so the
contrast stays legible.

The ranking moves a long way. On the 2017 detail block, by commodity row:

                        dollars      D        N
  manufacturing 31-33     31.6%    28.0%    44.7%
  agriculture 111/112      2.8%    23.7%    16.4%
  all other (services)    55.6%     7.2%    14.3%
  utilities 22             2.4%    26.1%    13.7%
  mining 21                3.4%    11.5%     8.0%

Manufacturing nearly doubles, because a manufactured good carries a long
upstream chain. Utilities halves, because electricity's emissions are almost
entirely direct and D therefore flatters it. Services double. Individual rows
move harder still: 31161A meat processing goes 0.10% -> 2.61%, a 26x change
that is all upstream livestock, and 531ORE goes 1.48% -> 3.97%. The block is
less concentrated on N -- top 10 rows are 48.5% against 65.3% on D.

The seed's score, re-run on N:

  2020  +0.5% dollar  +10.2% impact   (22/33 columns win)
  2021  +1.4% dollar  +10.8% impact   (21/33)
  2022  +2.3% dollar   +9.2% impact   (17/33)

On D these read +22.6/+25.1/+22.6%, so D was overstating the seed by more than
double -- it over-weights electricity, which the survey names well.

It also flips a verdict. Re-scoring S4's per-column no-goes on N: 622
(+11.4/+12.3/+27.5%) and 722 (+7.5/+11.2/+10.6%) overturn, 5412OP still loses
(-9.0/-7.7/-2.0%), and 81 does NOT overturn (-0.8/+2.9/+0.6%) although it
looked like a consistent win on D. More columns win on N than on D, because N
spreads weight off the few electricity-heavy rows.

Every ranking recorded before this is a D ranking, including the table PR #717
re-sorted the plan on; the resource module's docstring is restated accordingly.

Gates: black, ruff, mypy clean; targeted Step 3 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…577)

C2 says BEA estimates agriculture's inputs from "BEA NIPA estimates based on
USDA ERS statistics for farm income, EIA data, and 2017 Economic Census data",
so ERS Farm Income and Wealth Statistics is the source BEA itself used. The
extractor was already in bedrock. Its parse filtered to Cash receipts alone and
dropped intermediate expenses entirely -- the same class of bug as Census_AIES's
AIES_EXPENSE_FLOWS whitelist, and it made a concept ERS has published since 1910
look absent.

Three separate silent filters, all fixed and all now tested:

1. KEPT_CONCEPTS replaces the hardcoded Cash receipts test, as prefixes. Adding
a concept there is the only way to surface it, which is now said in one place.

2. The concept prefix is 'Cash receipt', singular. ERS writes the concept as
'Cash receipts value' everywhere except bell peppers, which are 'Cash receipt
value' -- 129 rows over 2008-2023. The old contains('Cash receipts') test
dropped them: $626M of $1,470,607M in 2017, 0.04%. A source typo, not a real
distinction, so this changes the cash receipts total by that amount.

3. Operator-dwelling variants are dropped. Several expense categories publish
both an 'excl.' and an 'incl. operator dwellings' series, identical in
VariableDescriptionPart2, so they collide in ActivityProducedBy and double any
groupby. Farm dwellings are not an intermediate input to farming, and the
'excl.' series is the one that reconciles.

4. Expense activities take the full published description. Four series share
Part2 = 'Miscellaneous' -- the group total, insurance premiums, federal
insurance premiums and irrigation -- so the Part1+Part2 name is not unique for
expenses and a groupby would add a group to its own members. Cash receipts keep
the Part1+Part2 name, which is what Sector_Crosswalk_USDA_ERS_FIWS.csv is keyed
on.

Verified against the real February 2025 file. 2017 intermediate product
expenses, excl. operator dwellings, is $226,611M, and farm origin $104,467M +
manufactured inputs $56,316M + other intermediate $65,828M reconciles to it
exactly. 2025 is $298,474M, matching #577's table.

The declared years ran to 2023 while the file already carried 2024 and 2025;
extended to 2025. 2025 is an ERS forecast, not a realized estimate, and is
flagged as such. Irrigation is discontinued after 2023 -- 18 categories in
2024-25 against 19 before -- so a consumer must not read its absence as zero.

Widened rather than forked: no FBS method or Python module consumed this FBA,
and the inventories plan needs a variable the same filter was dropping.

Gates: black, ruff, mypy clean; 7 new tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The plan still described a state three findings out of date: services rejected,
agriculture a small prize, ORE marginal, every ranking on dollars.

New section: What a column is worth. Sets out N against D against dollars, and
why N is the ordering to source on -- a Use cell is an entry in A, so an error
propagates through the whole Leontief inverse. Carries the row-group table and
the warning that any ranking dated before 2026-08-25 is a D ranking at best.
Also states the rows-versus-columns trap explicitly, since agriculture rows are
16.4% of impact and agriculture columns are 6.9%, and they are different
questions.

New section: What is still held at the 2017 benchmark. The backlog as an
inventory rather than a feeling -- 50 columns, $3,577B, 24.1% of the block,
broken out by BEA sector with the reason and issue for each. It also answers
the question of how to get them back, and the answer is not uniform: the
geometric-interpolation form is settled, but the Economic Census expense
breakout covers sectors 21, 23 and 31-33 only, so "rebenchmark at 2022 and
interpolate" is the right shape for trade alone. Agriculture and utilities have
annual sources and should be seeded directly. For trade the work is a
suppression recovery, not an extractor.

S4's verdict table is no longer the answer. Re-scored on N, 622 (+11.4/+12.3/
+27.5%) and 722 (+7.5/+11.2/+10.6%) overturn; 5412OP still loses and 81 is flat
-- and 81 is the cautionary case, because it wins on D. What stands from S4 is
42, 4A0 and 23, whose problem was suppression and reach rather than weighting.

S5 rewritten for the ERS extractor, which is built. It reconciles exactly at
2017 and agrees with BEA's published 111CA within 0.988-1.047 over seven years.
But #577's rationale -- "the value is in the levels" -- does not survive: the
level is already observed through GO - VAPRO, so the value has to be the mix,
which is what #577 says not to expect. Recorded with the mix actually measured.

New S6 for utilities from EIA (#719), which exists because the services seed
had to refuse that column, and ORE re-titled: on N it gains +25.1/+32.6/+37.2%
and carries 59-83% of the block's gain, so "marginal" was a dollar verdict.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The extractor in the parent commit made the seed buildable. Built, measured on
BEA's published 111CA for 2018-2024, and it does not earn its place.

  year  dollar gain   impact gain
  2018      -6.7%        +19.1%
  2019     -17.6%        -42.9%
  2020     +10.5%         +4.6%
  2021      +0.4%        -18.8%
  2022      -5.8%        -10.5%
  2023      +3.0%        +10.7%
  2024      +7.3%        +33.5%

Four of seven years positive on each weighting, swinging from -42.9% to +33.5%.
That is noise around zero, not a seed that tracks. leave_one_out finds no
culprit: feed contributes +8.0pp at 2022 and -5.9pp at 2024, livestock
purchases -5.1pp then +46.9pp. A category that helps one year and hurts the
next is not a mapping problem.

The mechanism is absent, and that is the finding. Comparing each category's
share movement against its BEA commodity row's share movement, six cleanest
pairs over seven years: electricity +0.61, pesticide +0.38, petroleum +0.27,
livestock +0.16, feed +0.13, fertilizer -0.38, pooled +0.18 over 42
observations. ERS category shares do not track BEA's commodity shares.

The explanation is consistent with everything else measured here. BEA uses ERS
for the farm income LEVELS -- which is why published_agreement runs 0.988 to
1.047 across seven years, the closest agreement of any source in this step --
and distributes across commodities by its own means. A category-share index
cannot reproduce a commodity mix that is not built from category shares.

#577's instinct was right for a reason it did not give. It says not to expect a
shifting mix because the mix is stable; the stronger reason is that where it
does move, it does not move with BEA's.

Also records what this does not establish: the test is at 111CA summary because
that is the only place a later year is published, so detail-level mix is
unobservable; fertilizer and pesticide both collapse to 325 at summary; and the
span contains the 2022 rebenchmark.

The module is kept as the record of a tested no-go rather than deleted, in the
same way the government function bridge is. What is worth keeping outright is
the extractor: it surfaces a concept it was filtering away, three silent bugs
are fixed, and it reaches 2025 for Step 2 and inventories.

Gates: black, ruff, mypy clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
S5 said "source extracted, seed not built". It is now built and rejected: 4 of
7 years positive, swinging -42.9% to +33.5%, and ERS category shares correlate
0.18 with BEA commodity shares over 42 observations. BEA uses ERS for farm
income levels and distributes across commodities by its own means.

The extractor stands and is the section's real yield.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…son (#564)

S4 rejected the trade Business Expenses Supplement on suppression and left the
door open: "no-go now, reopen only behind a suppression recovery". Retested with
three of that verdict's objections removed. The answer is a firmer no, and the
suppression recovery it pointed at should not be built.

What was wrong with the first test:

1. It was run at BEA summary and Step 3 estimates BEA detail. "4A0 loses every
item because it spans nine three-digit NAICS" is a summary artefact -- at detail
4A0 is six columns and five of the six are a single three-digit NAICS, needing
no aggregation at all.

2. It scored items BEA did not use. Its account of 452 rests on contract labour
x4.45, and contract labour is not one of BEA's thirteen trade items (Table C2).
BEA_ITEMS is BEA's own list.

3. It was dollar-weighted.

Re-scored at BEA detail, on BEA's thirteen items, at 2022, both weightings:

  42    -3.3% dollar    -2.5% N
  441  -24.4%          -43.6%
  445  -24.0%           +5.4%
  452 -132.7%         -183.6%
  4A0   -3.3%          -31.8%

Every column loses on dollars and four of five on impact. 441 is the decisive
one: 13 of 13 items published in both years, no aggregation, 61.5% dollar and
75.7% impact reach -- and -43.6%. Neither suppression nor reach is the binding
constraint, so recovering suppressed cells buys nothing. That is a build saved.

The mechanism is absent, and it is the same one that sank ERS agriculture. Each
BES item's movement against its BEA commodities' published share movement
correlates +0.06 pooled over 54 pairs, against agriculture's +0.18. A survey can
measure an industry's expenses well and still not describe how BEA moved the
commodity mix, because BEA does not build the mix from item shares.

Some movements are not credible anyway: 452 reports building rent falling
9,037M -> 5,577M, a 38% nominal fall in five years, with professional services
x2.83 and communication x0.38.

What stands from the original verdict is recorded rather than dropped:
suppression is real (retail 1 of 9 columns with all 13 items, 3 with none;
wholesale 0 of 9 and 4 with none, and 2022 is far more suppressed than 2017);
the percent-of-total column is suppressed on exactly the same cells, so 0 of 471
are recoverable by arithmetic; 425000 has no AWTS coverage at all because AWTS
surveys merchant wholesalers and 425 is agents and brokers.

No extractor is built. Nothing continues the BES after 2022 -- AIES publishes no
expense cell for 42 or 44-45 -- so a Census_BES source would carry two
observations and no future. The module downloads the four workbooks directly so
the finding stays reproducible, and uses the REVISED 2017 wholesale file: the
original is benchmarked to the 2012 Economic Census.

Caveat recorded: 2017 to 2022 is the only span the BES offers, so the
correlations rest on cross-sectional spread inside one interval rather than on
repeated observation.

Gates: black, ruff, mypy clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…no-go

Wes, 2026-08-25: the answer key every score in this step used is wrong, for two
reasons. BEA has not incorporated the 2022 Economic Census, so its published
2018-2024 tables are the 2017 benchmark carried forward on BEA's own annual
methods -- grading a seed against them measures agreement with an extrapolation,
and a seed that caught real structural change would lose. And they are scored at
summary, which sums the ten farm columns into 111CA and gives several survey
items the same target, so most of what a seed moves cancels before it is graded.

benchmark_holdout.py is the key that has neither problem. BEA publishes three
detail benchmarks, 2007/2012/2017, and io_2017 reads all of them onto the same
2017 code axis. So: seed the observed 2012 block with a source's 2012->2017
movement and score against the observed 2017 block, at detail. Out of sample,
against an observation rather than an extrapolation, and nothing in the test is
derived from what is being tested. Only the mix is on trial -- every column is
renormalised to the observed 2017 total, because Step 3 observes the level.

Economy-wide 2012->2017 mix drift is 0.0606 impact-weighted, which is the bar.

Regraded on it, the agriculture seed reverses from rejected to validated:

  dollar   frozen 0.2269 -> seeded 0.2074   +8.6%    8 of 10 columns
  impact   frozen 0.3286 -> seeded 0.2698  +17.9%    9 of 10 columns

against the 4-of-7-years swinging -42.9% to +33.5% the old key reported. No
leakage: the only 2017 information entering the seed is the ERS index itself,
and the renormalisation divides out because frozen and seeded are compared as
shares.

The prize is also much larger than the plan said. Farm columns are among the
worst-drifting in the whole table at detail on N -- 1111A0 0.474, 1121A0 0.424,
1111B0 0.311 -- so "agriculture is not in this list at all" was a dollar
judgement taken at summary and is wrong twice over. 111400 greenhouse and
nursery is the one column the seed worsens.

The +0.18 correlation that carried the rejection is withdrawn: several ERS
categories map to the same summary parent, which gives different category
movements identical targets and pushes the correlation toward zero by
construction. agriculture_score is kept, reframed as evidence about the key.

Two other verdicts are flagged rather than changed, because they rest on the
same key and I have not regraded them: the trade BES retest (it has 2012 and
2017 vintages, so it CAN be regraded, and the no-go must not be acted on until
it is), and the services and transportation block, which cannot be regraded here
at all because Census_SAS_Expenses starts at 2013.

Gates: black, ruff, mypy clean; targeted tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ason (#564)

The trade rejection rested on the same wrong answer key that reversed the
agriculture one, so it had to be regraded before it could be acted on. The
blocker was the 2012 vintage: it exists, under names the 2017 and 2022 files
do not predict -- arts/tables/2012/bes.xls (final census, not the preliminary
2012_arts_detailed_operating_expenses.xls) and the 2012r wholesale revision,
which is the file benchmarked to the 2012 Economic Census.

On benchmark_holdout the seed is a wash: +0.3% on dollars and -5.6% on impact,
9 and 8 of 18 columns winning. The no-go stands, but the -43.6% / -183.6% and
the "+0.06, movements do not track" finding are both withdrawn -- at detail
against the observed 2017 block the same comparison is +0.62 over 164 pairs.

What decides it is the weighting: the BES tracks BEA on items that carry no
impact (rent of machinery +0.89, water and sewer +0.81) and fails on the three
that carry 60% of it (electricity -0.19 at 38% of N, building rent +0.21 at
13%, transport +0.01 at 9%). Mean correlation 0.469 unweighted, 0.281 on
dollars, 0.028 on N. Shrinking the index toward 1 does not rescue it, so it is
not a calibration problem either.

The one conclusion that survives intact is the actionable one: do not build the
suppression recovery. 441000 has 13 of 13 items in both holdout years, 75.7%
impact reach, and still loses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…719)

The utilities columns were held at the 2017 benchmark because the services
survey had to refuse them: what the column does is a price-driven reweighting
of its fuel bill, and a relative share index divides common movement out by
construction. EIA measures receipts times delivered price, which is exactly a
reweighting.

On benchmark_holdout the seed gains +18.9% on dollars and +16.0% on impact,
with all three electric columns winning on both weightings -- 221100 +17.9%,
S00101 +10.0%, S00202 +13.5%. These are among the worst-drifting columns in
the table (S00101 0.340, 221100 0.194 against an economy-wide 0.0606), so this
is the first live route into the held block.

The fuel mix is doing the work, not a general "fuel got cheaper" signal: one
uniform index across the three fuels scores +7.1% against the per-fuel +16.0%.
The gain survives the index raised to any power from 0.25 to 2.0, and the
index is deliberately not divided by the industry's expense growth, which
leaves it independent of everything observed at the target year.

Three things to hold against it, all in the module docstring: coal carries the
gain and coal is the leg with the least future (51.7% of the fuel bill in 2012,
16.6% in 2022); only gas matches on level, and BEA's 324110 row for the
electric columns is not oil-fired generation, so the petroleum leg works
through prices rather than through the concept; and it is three columns on one
span. 221200 gas distribution and 221300 stay held, because BEA books
gas-for-resale net and there is no cell for what EIA measures.

Nothing is wired into the pipeline. The source is PUDL's public S3 mirror of
EIA 923 and 861 -- no credentials, and only complete years are used, because
PUDL publishes the current year month by month.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…719)

The first pass left 221200 held on a concept argument alone -- BEA books
gas-for-resale net, so ecnpurgas's $51.4B faces a $1M own-row cell. That was
the same argument I had just overruled for the petroleum leg, which works
through prices despite covering 3% of its cell, so 176 deserved measuring
rather than reasoning about.

Measured, it fails, and the way it fails is worth keeping. The only index that
wins is citygate receipts for sales customers, +29.0% on N -- and its 45% fall
across the span is EIA splitting that line in 2014, when the
transportation-customers line first appears. Receipts over merchant sales
volume runs 1.2-1.6 through 2013 and 0.8 in every one of the eleven years
since: a step, not a trend. The merchant function itself is flat, 6,959 ->
7,237 bcf and $57.0B -> $62.0B, and every concept continuous across the break
loses by 2 to 5%. Respondent counts are smooth across 2014, which rules out a
universe change.

Second, and independent of the break: form 176 is not a survey of the utilities
sector. Its ~2,000 respondents include interstate and intrastate pipelines,
storage and LNG operators and direct-delivery producers, each filing one report
per state. 78-81% of the volume is pipeline movement (486000) against 17%
delivered to consumers, ~30 respondents a year report producer lease use
(211000), and total disposition is about 5.7x US consumption because the same
molecule is counted at each step of the chain.

The general lesson is the mirror of the trade regrade: there a wrong answer key
made a sound source look useless, here a vintage break in the source makes a
useless index look excellent. A holdout gain is only as good as the source's
continuity across the span.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#497)

Grading the S3 materials seed on benchmark_holdout needs a 2012 vintage of
ecnmatfuel. It exists -- api.census.gov/data/2012/ecnmatfuel, under a group
named on the older pattern (EC1231SM1, not EC__31MATFUEL) -- and covers 393
industries and 1,272 materials across NAICS 21 and 31-33.

Three things it needed. The parse assumed columns 2012 does not publish: no
label columns and no MATFUELCOST_F, so a withheld cell arrives as a bare 0
(11.9% of rows against 9.1% flagged in 2017) and Suppressed is left empty for
that vintage rather than guessed at. The total codes differ -- 2012 uses
00000001 materials and 00000002 fuels where 2017 and 2022 use 00772000 and
00772002 -- and missing them leaves a $6.4T total row in as if it were a
material. All four codes are disjoint across vintages, so one tuple serves.

The bug: census_EC_URL_helper has a 2012 branch that appends both &for=us:* and
&for=state:* so Census_EC.yaml, which deliberately leaves the geography blank,
gets its national and state calls. Census_EC_MatFuel.yaml sets for: us:* itself
because 2022 returns HTTP 400 without one, so the branch appended a second for
clause the API ignores: two URLs, identical answers, every row twice. Shares
survive that; levels and coverage do not. The branch now fires only when the
config has not already set a geography, which leaves Census_EC unchanged.

The grade itself is not settled and is not claimed here: 2012 publishes 1,272
material codes against 2017's 289 and only 132 are shared, so differencing the
two vintages unrestricted measures a code-scheme change (-134.7% on N, index
cells up to 16,000x). Restricted to the shared codes the index is sane -- median
0.723, p95 1.9 -- and scores -2.8% on N over 390 live cells, which is neither a
pass nor enough coverage to be a verdict. Grading the materials seed needs a
2012 -> 2017 MATFUEL concordance first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two decisions from Wes and one measurement.

The materials seed ships ungraded: the census mix is the best observation of
manufacturing's structure that exists, and the alternative is not a better
source but the frozen 2017 mix, which asserts nothing changed in eight years --
a stronger claim than the data makes. The gate decides where a decision hangs
on it and there is an alternative to switch to; it cannot decide anything where
the source is the only observation of the movement. Two checks that need no
second benchmark year stand in when it cannot run: continuity inside the source,
and level/coverage sanity against the BEA cell. Both caught a real error today.

And the durable output of a holdout run is which source-to-Use-table
correspondences it confirms, not the gain, because a confirmed correspondence
replicates to 2022 and a gain number does not. The per-row tables are recorded
as such: EIA gas to 211000 confirmed, coal partial at 2.1x the cell, oil not
confirmed at 3% and carried as a price proxy, and the 132 shared MatFuel codes
confirmed with the rest unverified rather than wrong.

The measurement: services and transportation, regraded on the holdout with a
2013 base -- the earliest SAS publishes -- passes at +11.5% on N with 58 of 99
scored columns winning, against +2.0% on dollars. 531ORE, the heaviest column
at $747.6B, gains +15.5%. The base is 2013 against a 2012 frozen comparator, so
the seed is not credited with the 2012->2013 movement, which biases the test
against it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…sal (#497)

Two claims in this repo about Census_SAS_Expenses were wrong, both found by Wes
pushing back on "2018/2019 are missing".

The years are published. The extractor docstring said both vintages carry the
sheet for 2018 and 2019 with only a handful of items populated. Neither half is
true: sas-17 carries 2013-2017 and sas-22 carries 2020-2022, so those years are
in neither, and they live in sas-18.xlsx and sas-19.xlsx, which this source does
not fetch. There they are fully populated -- 8 of 8 cells for every NAICS -- on
a cut item list, 8-12 items per NAICS against 24-28 in sas-17. What Census cut
for those two collection years is exactly the seed's input list: electricity,
communication, fuels, professional and technical services, advertising, repairs,
water and sewer, rent of buildings and machinery, data processing. The total
Expenses line survives and is a denominator rather than a mix, usable for
industry_growth and not fetched today. So the mix gap is real and the stated
reason for it was not.

The 2023 refusal rested half on a -5.0%/-4.1% score against BEA's carry-forward
summary, which is the key this branch discredited this morning. AIES does
continue the collection and AIES_TO_SAS_ITEM aligns the names, so the
correspondence is not what fails. What fails is the dispersion the index reads:
a relative index divides out whatever is common to the industry, so only each
item's deviation from its industry's own step survives. That residual is 0.035
in a quiet year, 0.043-0.046 across the energy surge, and 0.164 across the
survey change -- an eventful year is worth about +0.01 and the instrument change
about +0.12. The refusal stands on evidence that does not depend on the answer
key, and the score figure is withdrawn.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wes: 2023 data reset from the 2022 benchmark, so do not discard it for jumping.
That is right, and the refusal I re-grounded an hour ago used the wrong control.

The seed's index divides out whatever is common to an industry, so only each
item's deviation from its industry's own step reaches it. I compared that
residual against within-vintage pairs -- 0.035 quiet, 0.043 across the energy
surge -- and read 0.164 as instrument noise. The right control is a pair that
crosses a benchmark in the same instrument: SAS 2017 (2012 EC) to SAS 2020
(2017 EC) scores 0.146. So 0.164 is what crossing a benchmark looks like, not
what a broken instrument looks like; the survey change itself adds about 0.018.
A four-year within-vintage span scores 0.108, so span length does not explain a
one-year step either.

And the rebenchmark is information rather than contamination. AIES replaced SAS
outright at data year 2023, and a survey launched off the 2022 Economic Census
resets levels and composition toward what that census revealed -- which is the
observation BEA has not incorporated. Refusing 2023 holds the mix on a
2017-benchmarked structure, which is the frozen-benchmark problem this step
exists to fix. Census documentation has not been read to confirm the 2022-EC
benchmark explicitly; the timing and the dispersion signature are consistent.

services_transport_seed now builds AIES years by default. What is genuinely lost
is the ~0.018 that is instrument rather than economics, and coverage: AIES
carries 466 item-industry pairs against 974 in the last SAS year. A 2023
movement is weaker evidence than a 2020-2022 one, not unusable evidence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant