Outcome Panel API¶
Sealed text-to-code decoding splits over the Census index entries (roadmap Stage 2).
Index-entry roles¶
Index-entry roles for the outcome panel (Req 3; roadmap D4).
Every entry of the Census NAICS index file holds exactly one role: examples-channel text, or a
training, validation or test query. The assignment is made once, frozen in a committed table
(conf/data/index_roles.csv), and consumed by hash afterwards; regeneration is never how the
sealed splits are preserved.
- Eligibility. Validation and test queries are drawn only from entries that no training text
can leak: an entry is withheld from both held-out splits when it matches, exactly or as a
near-duplicate (
leakage), any title, description, exclusion text, fallback examples text or any other index entry. Checking against every other entry, not only those that end up as training text, makes the held-out splits leak-free whatever the assignment. - Quotas. Per code, role counts are largest-remainder quotas of the configured fractions,
with remainder ties broken by a seeded draw. Every code keeps at least
examples_floorexamples-channel entries, and held-out quotas beyond the code's eligible entries move to training, one at a time from the larger of the two held-out quotas (test on a tie). - Seeds. Each code draws from its own generator, seeded by (seed, code), so a code's roles do not depend on any other code.
EligibilityReport
dataclass
¶
Which entries may be held out, and why the others are withheld.
The *_static and *_entry counts overlap; withheld_exact (any exact match) and
withheld_near_duplicate (a near-duplicate and no exact match) partition withheld.
RoleFractions
dataclass
¶
Exact role fractions; non-negative and summing to one.
from_mapping(mapping)
classmethod
¶
Exact fractions from decimal config values (0.35 becomes 7/20, not a binary float).
allocate_role_counts(n_entries, n_eligible, fractions, tie_break, examples_floor=1)
¶
Role counts for one code with n_entries entries, n_eligible of them held-out-eligible.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_entries
|
int
|
The code's index entries. |
required |
n_eligible
|
int
|
Entries that may become validation or test queries. |
required |
fractions
|
RoleFractions
|
Target role fractions. |
required |
tie_break
|
Sequence[float]
|
One draw per role, in |
required |
examples_floor
|
int
|
Minimum examples-role entries (capped at |
1
|
Returns:
| Type | Description |
|---|---|
Dict[IndexRole, int]
|
Counts per role, summing to |
assign_index_roles(entries, eligible, fractions, seed, examples_floor=1)
¶
Give every entry exactly one role, per code and stratified.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entries
|
DataFrame
|
Index entries ( |
required |
eligible
|
ndarray
|
Held-out eligibility, aligned with |
required |
fractions
|
RoleFractions
|
Target role fractions. |
required |
seed
|
int
|
Base seed; each code draws from |
required |
examples_floor
|
int
|
Minimum examples-role entries per code. |
1
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
|
attach_role_text(roles, entries)
¶
Join the frozen role table to the index entries it was built from.
Returns:
| Type | Description |
|---|---|
DataFrame
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the two list different entries, or give an entry different codes. |
examples_channel_by_code(role_rows)
¶
Each code's examples channel: its examples-role entries in index-file order, joined.
held_out_eligibility(entries, static_segments, min_jaccard=NEAR_DUPLICATE_MIN_JACCARD)
¶
Flag the entries that no training text can leak.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entries
|
DataFrame
|
Index entries ( |
required |
static_segments
|
Sequence[str]
|
Every code's title, description, exclusion and fallback examples
segments ( |
required |
min_jaccard
|
Fraction
|
Near-duplicate threshold. |
NEAR_DUPLICATE_MIN_JACCARD
|
read_role_table(path)
¶
Read the frozen role table, keeping codes as strings.
role_table_fingerprint(roles)
¶
SHA-256 of the role assignment's canonical CSV (entry_id, code, role).
It equals the committed table's file hash, so a split is identified by its assignment whichever file it was read from.
verify_examples_channel(descriptions, role_rows)
¶
Require every code with index entries to carry exactly its examples-role entries.
Raises:
| Type | Description |
|---|---|
ValueError
|
If a code's examples channel differs from its examples-role entries. |
verify_role_leakage(descriptions, role_rows, min_jaccard=NEAR_DUPLICATE_MIN_JACCARD, *, extra_texts=())
¶
Match every validation and test query against all training text; fail on any match.
Training text is every code's title, description, examples channel and exclusion text in
descriptions, every training-role query, and extra_texts: the redirection table's
activity phrases, which Stage 7 trains on as queries (Req 8).
Returns:
| Type | Description |
|---|---|
Dict[str, Dict[str, int]]
|
Exact and near-duplicate match counts per held-out split (all zero on success). |
write_role_table(roles, path)
¶
Write the canonical CSV and return its sha256 (role_table_fingerprint).
Draw the frozen index-entry role table (roadmap Stage 2; Req 3; D4).
naics-embedder data roles runs this once. The table it writes (conf/data/index_roles.csv)
is committed and data preprocess applies it from then on, so the sealed validation and test
splits never move. Redrawing reassigns every entry and unseals both splits, so an existing table
is replaced only with force.
generate_index_role_table(download_cfg, panel_cfg, *, force=False)
¶
Draw every index entry's role, check the result, and write the table and its provenance.
Held-out (validation and test) queries are drawn only from entries that match no title, description, exclusion text, fallback examples text or other index entry, exactly or as a near-duplicate; the provenance counts the entries withheld for each reason.
Returns:
| Type | Description |
|---|---|
Path
|
The role table's path ( |
Raises:
| Type | Description |
|---|---|
FileExistsError
|
If the table exists and |
ValueError
|
If the drawn roles fail a Req 3 check. |
Leakage¶
Leakage between held-out index-entry queries and training text (Req 3, "Leakage").
Texts are compared after normalize_text: lowercase ASCII letters and digits, with every other
run of characters collapsed to one space. A query leaks into a training segment in two ways:
- exact: the query occurs in the segment as whole words (equality included);
- near-duplicate: the character-trigram Jaccard similarity of query and segment reaches the
threshold, 9/10 by default. Trigrams are taken inside word boundaries, as scikit-learn's
char_wbanalyzer builds them, so reordered words score as the same text.
Jaccard is compared in integers (denominator * shared >= numerator * union), so no pair sits
on a floating-point boundary. The check compares whole queries with whole segments, so a query
whose words appear reordered inside a longer segment is not caught.
LeakageMatches
dataclass
¶
Per-query leakage flags, one boolean per query in input order.
find_leakage(queries, corpus, *, min_jaccard=NEAR_DUPLICATE_MIN_JACCARD)
¶
Flag each query that occurs in, or near-duplicates, any corpus text.
Raises:
| Type | Description |
|---|---|
ValueError
|
If a query has no letters or digits, or the threshold is outside (0, 1]. |
find_leakage_within(texts, *, min_jaccard=NEAR_DUPLICATE_MIN_JACCARD)
¶
Flag each text that occurs in, or near-duplicates, another text of the same list.
A text is never matched against itself, but two identical texts flag each other.
leaking_texts(segments, queries, *, min_jaccard=NEAR_DUPLICATE_MIN_JACCARD)
¶
Flag each text that some query leaks into, under find_leakage's two rules.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
Sequence[Sequence[str]]
|
Each text's normalized segments, as |
required |
queries
|
Sequence[str]
|
The queries, normalized here. |
required |
min_jaccard
|
Fraction
|
The near-duplicate threshold. |
NEAR_DUPLICATE_MIN_JACCARD
|
Returns:
| Type | Description |
|---|---|
ndarray
|
One boolean per text: some query occurs in, or near-duplicates, one of its segments. |
normalize_text(text)
¶
Lowercase ASCII letters and digits, every other run of characters one space.
text_segments(text)
¶
Normalized sentences of a description or exclusion text.
A cross-reference sentence ("Growing soybeans--are classified in Industry 111110") also
yields its activity phrase, the part before --, which later stages train on as a query.
training_text_segments(descriptions, extra_texts=())
¶
Sorted, de-duplicated normalized segments of every code's training text, plus extra texts.
Titles are one segment each, descriptions and exclusion texts are split into sentences, and
the examples channel is split into its '; '-joined entries.
Decoding¶
Text-to-code decoding scores for the outcome panel (Req 3).
Each query decodes to the nearest candidate code under the arm's own distance. Ties are broken against the truth: the true code's rank counts every candidate at a distance no greater than its own, so an encoder that places every point alike ranks the truth last, never first. Distances are computed in float64 on the CPU whatever the device or dtype of the points.
Metrics per query: exact top-1 accuracy, reciprocal rank, Hit@k for k in HIT_KS, and the
level of the lowest common ancestor of the top-1 code and the truth (6 for the same six-digit
code, 2 for a shared sector, 1 for the virtual root above the sectors).
DecodingResult
dataclass
¶
Decoding scores at three grains.
Attributes:
| Name | Type | Description |
|---|---|---|
per_query |
DataFrame
|
One row per query: |
per_code |
DataFrame
|
One row per true code: |
summary |
Dict[str, Union[int, float, str]]
|
Query-weighted means of every metric, with |
cosine_distances(queries, candidates)
¶
One minus cosine similarity, (Q, C).
euclidean_distances(queries, candidates)
¶
Euclidean distances, (Q, C), without the matrix-product shortcut that loses precision.
lca_level(code_a, code_b)
¶
Level of the lowest common ancestor of two codes, counting the virtual root as level 1.
A shared sector is level 2 and the same six-digit code level 6; combined sectors (31-33, 44-45, 48-49) count as one sector.
lorentz_distances(queries, candidates)
¶
Geodesic distances on the curvature -1 hyperboloid, (Q, C), for (time, space) rows.
The time coordinate is re-derived from the spatial ones, as metrics.core's
lorentz_distance_matrix does, so a point rounded off the hyperboloid is read at its
spatial position.
resolve_distance(distance)
¶
A registered distance by name, or a callable with its __name__.
score_decoding(query_points, query_codes, candidate_points, candidate_codes, distance='cosine', query_ids=None)
¶
Decode every query to its nearest candidate and score it against the query's true code.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query_points
|
Tensor
|
Query embeddings, (Q, D). |
required |
query_codes
|
Sequence[str]
|
The true code of each query. |
required |
candidate_points
|
Tensor
|
Candidate embeddings, (C, D), in the same space. |
required |
candidate_codes
|
Sequence[str]
|
Distinct candidate codes, one per row of |
required |
distance
|
Union[str, DistanceFn]
|
A name in |
'cosine'
|
query_ids
|
Optional[Sequence[int]]
|
Identifiers carried into |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
On mismatched shapes, repeated candidates, a query code that is not a candidate, or a distance that is not finite. |
Panel and selection log¶
The outcome panel (Req 3; Req 4): text-to-code decoding over sealed query splits.
Validation queries may be read at any time, and every read is logged. The test split is sealed: reading it needs a logged opening by the same panel object, and opening the same split (the same role assignment, by fingerprint) a second time needs a stated reason. Training queries are training data, not a selection, so reading them is not logged.
OutcomePanel
¶
The index-entry query splits, the six-digit candidates, and the log every read goes to.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
role_rows
|
DataFrame
|
Every index entry with its text and role ( |
required |
candidates
|
Sequence[str]
|
The six-digit codes queries decode to, entry-less codes included. |
required |
log
|
SelectionLog
|
The selection log. |
required |
entryless_candidates
property
¶
Candidates without index entries: decoded to, never queried (112130 and 541120).
data_fingerprint(split)
¶
SHA-256 of what a read of the split scores: its queries (entry_id, code and
text, in entry order) and the candidates.
fingerprint names the role assignment alone, which the log counts openings by, so two
panels can share it and still score other text or decode to other candidates. This
returns no rows, so it is not a read and logs nothing, even for the sealed test split.
from_bundle(bundle, log_path)
classmethod
¶
The panel from a validated supervision bundle's index_roles member and codebook.
Every loadable bundle carries the member, whose roles its build checked against its descriptions.
from_files(index_roles_parquet, descriptions_parquet, log_path)
classmethod
¶
The panel from data preprocess outputs: the index roles and the descriptions.
Raises:
| Type | Description |
|---|---|
ValueError
|
If the descriptions' examples channel is not built from the examples-role entries only. A descriptions file from before the roles existed holds every entry, held-out queries included, in its examples channel. |
open_test(purpose, *, reopen_reason=None)
¶
Open the sealed test split for this panel object, logging the opening.
Raises:
| Type | Description |
|---|---|
SplitAlreadyOpenedError
|
If the log already records an opening of this split and
no |
score(encoder, split, purpose, distance='cosine', detail=None)
¶
Decode one split's queries over every candidate with the encoder, logging the read.
detail joins the read's logged detail, so a caller can name the run it scores; it
cannot replace the encoder or the distance the panel logs. score_logged also returns
the record the read logged.
score_logged(encoder, split, purpose, distance='cosine', detail=None)
¶
score, also returning the record the read appended to the selection log.
The record is appended before any query is encoded, so a read that fails while it scores
is logged all the same. A caller that keeps its own copy of its reads, as the training
monitor keeps monitor_reads.jsonl, keeps this record as it was logged.
Returns:
| Type | Description |
|---|---|
Tuple[DecodingResult, Dict[str, Any]]
|
The decoding scores, and the read's record as the log appended it. |
test_queries(purpose)
¶
Test queries, logging the read.
Raises:
| Type | Description |
|---|---|
SealedSplitError
|
If this panel object has not opened the test split. |
training_queries()
¶
Training queries (entry_id, code, text); training data, so not logged.
validation_queries(purpose)
¶
Validation queries, logging the read.
QueryCodeEncoder
¶
Bases: Protocol
What the panel needs from an arm: queries and codes embedded in one space.
SealedSplitError
¶
Bases: RuntimeError
The test split was read before a logged opening.
SplitAlreadyOpenedError
¶
Bases: RuntimeError
The test split was opened again without a stated reason.
The selection log (Req 4; Verification "Selection hygiene").
An append-only JSON-lines file with one record per event: every read of a panel's validation or
test split, and every opening of a test split. It is the evidence that selections read validation
splits only and that each test split was opened once, for the final configuration. A second
opening needs a stated reason and is recorded as reopen.
SelectionEvent
¶
Bases: str, Enum
What a selection-log record reports.
SelectionLog
¶
Append-only JSON-lines log of panel reads and test-split openings.
append(event, *, panel, split, purpose, fingerprint, n_queries, detail=None)
¶
Append one record and return it.
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
openings(panel, fingerprint)
¶
The recorded openings (open and reopen) of one test split, oldest first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
panel
|
Union[str, Collection[str]]
|
The name the split is opened under, or every such name: the regressor panel's two outcomes share each regime's outer set (Req 22). |
required |
fingerprint
|
str
|
The split's fingerprint. |
required |
records()
¶
Every record, oldest first; a log that does not exist yet has none.
merge_read_detail(logged, extra)
¶
A read's logged detail joined with a caller's extra, which can name the run it scores.
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Lexical baseline¶
A training-free lexical encoder for the outcome panel.
Texts are hashed character trigrams of their normalized form (leakage.normalize_text),
L2-normalized, so cosine distance decodes a query to the code whose text shares most of its
trigrams. It is the stub arm that exercises the panel end to end on the real validation split
until a trained encoder can embed a query (roadmap Stage 6), and a floor for trained arms. It is
never a candidate configuration.
LexicalTrigramEncoder
¶
Hashed character-trigram vectors for codes (from their texts) and queries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code_texts
|
Mapping[str, str]
|
The text each code is embedded from. |
required |
n_features
|
int
|
Hash buckets per vector. |
4096
|
code_texts_from_descriptions(descriptions)
¶
Each code's title, description and examples channel, joined by spaces.
Exclusion text is left out: it names activities the code does not cover.