Skip to content

feat(nowcast): grade final-demand column totals against BEA by year - #1159

Merged
WesIngwersen merged 2 commits into
mainfrom
final_demand_vs_published_check
Nov 2, 2026
Merged

WesIngwersen merged 2 commits into
mainfrom
final_demand_vs_published_check

Conversation

@WesIngwersen

Copy link
Copy Markdown
Member

What this adds

bedrock/analysis/nowcasting/final_demand_vs_published.py: a check that grades each final-demand column total of a balanced nowcast build against BEA's published summary Use table, for every year 2017–2024.

No unit tests, per the convention for analysis scripts; --check is the runnable verification.

What it finds on v0.5.0_a166975 (the v1.0 build)

column result
F040 exports fails every year, 2.6–10.3% short ($58–269bn). The rest-of-world adjustment S00900 is zero in every nowcast year (#1158).
F010 PCE +0.6–1.5% high for the same reason; under the 2% bar
F030 inventories fails every year, $2–28bn (−22% to +64%)
F07S federal nondefense software investment fails in 2023 only, −27% ($5.3bn)
every other column within 0.6% every year

So --check fails on main today, by design: it records three open defects, not a regression.

How to run

uv run python -m bedrock.analysis.nowcasting.final_demand_vs_published
uv run python -m bedrock.analysis.nowcasting.final_demand_vs_published --check
uv run python -m bedrock.analysis.nowcasting.final_demand_vs_published --reference latest

Refs #1158, #1157, #528, #658

🤖 Generated with Claude Code

Adds bedrock/analysis/nowcasting/final_demand_vs_published.py. For each year
of a named balanced build (default: the canonical config's
nowcast_mut_vintage, fetched from GCS if not local), it sums each
final-demand column of the balanced detail Use SUT and compares it with the
same column of BEA's published summary Use SUT. --check exits non-zero when a
column-year is outside both 2% and $1bn; --reference latest compares against
the 1997-2024 workbook for every year (#1157).

On v0.5.0_a166975 it fails on exports (F040, 2.6-10.3% short every year:
the S00900 rest-of-world adjustment is zero, #1158), on inventories (F030,
$2-28bn), and on F07S in 2023 (-27%).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@WesIngwersen
WesIngwersen requested a review from bl-young October 30, 2026 20:26

@bl-young bl-young left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve -- the check is apples-to-apples (same $M units, commodity set, and code[:4] FD rollup as use_summary_sut column totals), and I reproduced the failures locally on v0.5.0_a166975.

Inline notes below are docstring / tolerance clarity, not blockers.

(default: the canonical config's ``nowcast_mut_vintage``) and fetches it from
GCS when it is not local, so the answer is about the tables the model ships.

⚠️ **Most final-demand columns cannot fail this check by construction.** The

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit on "cannot fail by construction": conditioning only scales reachable same-sign groups against the as_read workbook (nowcast_fd_conditioning empty-group / sign-mismatch guards). F07S 2023 and --reference latest (e.g. F02S 2022 in the docstring below) are the escapes. Worth softening to that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Softened in 06f2417: the docstring now says the conditioned columns rarely fail, and names the two ways out: the empty-group and sign-mismatch guards that hold a factor at 1.0 (F07S 2023), and --reference latest (F02S 2022).

larger column, so the same dollars stay under the 2% bar there: ``F040``
fails, ``F010`` does not.

❌ **``F07S`` federal nondefense software investment is 27% short in 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

F07S is federal nondefense structures, not software (that is F07N / IPP).

Also worth naming the cause: on v0.5.0_a166975 the 2023 gap is almost entirely the Used summary group (ours about -$5.5bn vs published +$0.16bn) -- the FD conditioning sign-mismatch guard holding the factor at 1.0 -- not a broad structures miss.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 06f2417: F07S is federal nondefense structures. The docstring also names the cause: the Used group (ours about -$5.5bn vs published +$0.16bn), with the sign-mismatch guard holding the factor at 1.0.

table['relative_gap'] = table['gap'] / table['published'].abs().where(
table['published'] != 0
)
table['fails'] = (table['gap'].abs() > ABSOLUTE_TOLERANCE_USD_M) & (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AND rule lets F010 pass with a ~$200bn gap every year (+1.5% < 2%). Same S00900 dollars as the F040 fail -- so --check encodes "F040 fails, F010 does not" by the relative bar, not because PCE is clean.

Fine if intentional for the Phase 3 review bar; worth one sentence in the Tolerance section so a reader does not treat a green F010 as "matched".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional for the Phase 3 bar. The Tolerance section now says so: F010 passes with about $200bn every year on v0.5.0_a166975, the same S00900 dollars that fail F040, so a pass on a large column is not a match. #1171 moves those dollars in Step 6.

codes = [
c for c in USA_2017_SUMMARY_FINAL_DEMAND_CODES if c in published.columns
]
theirs = published.reindex(index=list(USA_2017_SUMMARY_COMMODITY_CODES))[

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional harden: reindex silently drops missing rows. Asserting Used and Other are present would keep a BEA label change from zeroing mass without error.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 06f2417: the check raises a KeyError naming the year if the published Use has no Used or Other row, before the reindex. It runs clean on both references for v1.0.0_5514c3b.

@WesIngwersen WesIngwersen self-assigned this Oct 31, 2026
- Say when conditioned columns can still fail (empty-group and
  sign-mismatch guards; --reference latest) instead of "by construction".
- F07S is federal nondefense structures; its 2023 miss is the Used group.
- Note that a pass on a large column is not a match (F010 passes ~200bn).
- Raise if the published Use loses its Used or Other row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@WesIngwersen
WesIngwersen merged commit 8b41ef0 into main Nov 2, 2026
5 checks passed
@WesIngwersen
WesIngwersen deleted the final_demand_vs_published_check branch November 2, 2026 13:10
WesIngwersen added a commit that referenced this pull request Nov 2, 2026
Co-Authored-By: Claude Opus 5.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.

2 participants