fix: distinguish 497K fee tables from footnotes - #1160
Conversation
dgunning
left a comment
There was a problem hiding this comment.
Automated review of the classifier change. Three findings, all reproduced by running this branch side by side against main on identical HTML.
The narrowing correctly stops the #912 footnote from landing in the operating_expenses bucket, but a footnote that is no longer operating_expenses is not thereby unclassified — it falls through to the later branches, where "first table wins" makes it displace real data. Two of the three findings are that fall-through and the over-tight row predicate that now gates the whole filing.
Non-issues checked: no consumers of the removed _FEE_TABLE_LABELS outside this module; tests/test_497k_tables_characterization.py, tests/issues/regression/test_issue_912_497k_fee_waiver.py and tests/test_prospectus497k.py all pass on this branch (104 passed, 28 deselected); the baseline JSON edits are correct — the only non-phantom change (2016q4-3794) drops three keys whose values were already null.
Stop prose-only tables from falling through into data classifiers, validate fee value shapes without rejecting SEC footnote formats, and preserve column alignment for indented operating-expense and shareholder-fee tables. Add end-to-end regressions for the maintainer-reported loss cases and review-discovered edge shapes.
|
@dgunning Thanks for the detailed repros. I addressed all three review points:
I added end-to-end regressions for the reported first-table-wins losses, marked missing values, single- and multi-class spacer layouts, flattened rows, and indented shareholder fees. Local verification on the corrected diff:
|
Problem
A 497K fee-waiver footnote can be rendered as its own one-row table. The Ocean Park filing in #912 includes the phrase "management fees" in that prose, so
_classify_tabletreated the footnote as a second operating-expenses table.That false positive changed
extract_fee_tablesto the repeated-section path. The single-column expense parser then read3 Yearsas$3, returned no three-year value, and emitted the footnote as a phantom share class. The reduced filing excerpt returned two records withexpense_1yr == 3instead of one record with expenses of $109 and $381.Approach
I rejected filtering malformed classes after extraction because the false table count has already selected the wrong expense-example parser by then. I also rejected a literal prefix check because valid labels can be prefixed or footnote-marked, while prose can itself begin with "management fees."
A corpus-wide classifier comparison changed exactly three tables, all one-row prose footnotes. Existing management-fee label rows remained classified as operating expenses.
Review follow-up
The maintainer-reported fall-through, marked-value, spacer-column, and flattened-row cases are covered directly. Follow-up review also added end-to-end protection for percent-leading narrative, multi-class value alignment, and shareholder-fee extraction after indentation normalization.
Compatibility
This does not change the public API. It narrows an internal classifier false positive and preserves existing SEC value forms, including leading-decimal percentages, split-text percentages, parenthesized values, numeric or symbol markers, and single-letter marker lists.
This intentionally leaves the cover-page ticker fallback from #912 out of scope because it is a separate extraction path with different fallback rules.
Testing
uvx hatch run pytest tests/issues/regression/test_issue_912_497k_fee_waiver.py tests/test_prospectus497k.py -q --tb=short: 98 passed.Related to #912