Regressor Panel API

Arms' coordinates as regressors for QCEW employment, in two sealed regimes (roadmap Stage 3). The outcome setting picks next-year log employment (level, the default) or its log change from year t (growth, Req 21), on the same rows, partition and folds. One opening of a regime's outer set counts for both outcomes (Req 22).

QCEW rows

QCEW national rows for the regressor panel (roadmap Stage 3; Req 2; Req 21; D1; D7).

Stage 1's finding (specs/findings/employment-statistics-coverage.md) fixes the source: QCEW annual averages for 2022–2025, national grain, private ownership (own_code 5). The panel reads the Open Data Access national slices ({year}_US000_annual.csv) under the sha256 values the finding records; they agree with the annual single files on every national row.

  • Cells. One cell per code and year. Sectors 31-33, 44-45 and 48-49 are keyed 31, 44 and 48, as in the codebook. Each of the 19 six-digit NAICS 238 codes that BLS publishes only as residential (…1) and nonresidential (…2) codes is its five-digit parent's only child, so its cell is read from the parent's row.
  • Usable. A cell is usable when it is disclosed and its employment, establishments and wages are all positive. A suppressed cell is never read as zero.
  • Population. The codes with a usable cell in every window year.
  • Rows (D7). One row per population code and feature year t in 2022–2024: the covariates are log establishments and log total annual wages from year t (D1), and the outcome is log annual-average employment in year t + 1.
  • Growth (Req 21). In growth mode the outcome is log E(t + 1) − log E(t) instead, from the same cells, and nothing else changes. Every population code has a usable cell in every window year, so the difference is always defined.

RegressorOutcome

Bases: str, Enum

A panel row's outcome (Req 21): level is log employment in t + 1 (D7), today's panel and the default; growth is its log change from t, the economic artifact's first panel (Req 20).

find_split_codes(published, codebook_six)

Codebook six-digit codes that QCEW publishes only as BLS residential and nonresidential codes.

Raises:

Type Description
ValueError

If such a code is not its five-digit parent's only child, so the parent's row would not carry the code's own value.

level_cells(cells, codebook_codes, level)

One cell per codebook code of the given level and year.

Returns:

Type Description
DataFrame

code, year, disclosure_code, estabs, emp, wages and source

DataFrame

(published, or five_digit_parent for a split six-digit code).

load_national_cells(qcew_dir, expected_sha256)

Read every window year's national slice after checking its sha256.

Raises:

Type Description
ValueError

If a slice has no pinned sha256 or its sha256 differs from the pinned one.

panel_rows(cells, codes, outcome=RegressorOutcome.LEVEL)

One row per code and feature year (D7): year-t covariates and the outcome outcome names.

The level outcome is log employment in t + 1. The growth outcome subtracts log employment in t, read from the same cells by the same expression, so a growth row differs from its level row in outcome alone (Req 21).

Parameters:

Name Type Description Default
cells DataFrame

One level's cells (level_cells).

required
codes Collection[str]

The codes to build rows for (population).

required
outcome RegressorOutcome

level (the default) or growth.

LEVEL

Raises:

Type Description
ValueError

If outcome is neither, or a code lacks a usable cell in a window year.

population(cells)

Codes with a usable cell in every window year, sorted.

read_national_slice(path)

The private, all-size annual rows of one national slice.

Returns:

Type Description
DataFrame

industry_code, agglvl_code, year (Int32), disclosure_code (blank reads as

DataFrame

''), and estabs, emp, wages as Int64.

slice_name(year)

File name of one year's national slice.

usable_cell()

Disclosed, with positive employment, establishments and wages.

Partition and held-out draw

The regressor panel's sealed outer sets (Req 2; Req 4; roadmap Stage 3; D7).

Every panel row is a code in a feature year. One partition serves both regimes, so that no validation read touches either regime's sealed set:

  • Held-out outer set (H). Every row of a code whose employment includes a held-out four-digit group: at levels 4–6 the code's own four-digit ancestor (or the code itself) is held out; at levels 2 and 3 a held-out group rolls into the code.
  • Seen outer set (S). The remaining rows with feature year 2024, whose outcome is 2025 (D7).
  • Remainder (R). The remaining rows, feature years 2022 and 2023. Validation reads only these.

The held-out groups are drawn once, stratified by sector, and committed (conf/data/regressor_heldout_groups.csv). The split's fingerprint is the sha256 of that table's canonical CSV, which equals the committed file's hash: a redraw gets a new fingerprint, so it would not count as a reopening, and the table is therefore never redrawn.

RegressorSplit

Bases: str, Enum

Which part of the partition a panel row belongs to.

ancestor_at(code, level)

The code's ancestor at a level (the code itself at its own level); 31-33 is one sector.

assign_splits(rows, heldout_groups)

Add each row's group and split (RegressorSplit) to panel rows.

Parameters:

Name Type Description Default
rows DataFrame

Panel rows with code and feature_year.

required
heldout_groups Collection[str]

The committed held-out four-digit groups.

required

check_partition(rows, codes)

Require exactly one row per code and feature year, each in exactly one split.

Raises:

Type Description
ValueError

If a code-year is missing, repeated, or has no split.

codes_fingerprint(codes)

SHA-256 of the codes, sorted, one per line: the codebook's content, whatever its file.

draw_heldout_groups(six_digit_codes, fraction, seed)

Draw the held-out four-digit groups, stratified by sector.

Parameters:

Name Type Description Default
six_digit_codes Collection[str]

The six-digit population (Stage 1's 980 codes).

required
fraction Fraction

Share of each sector's groups to hold out (largest-remainder quotas).

required
seed int

Base seed; sector s draws its groups with np.random.default_rng([seed, int(s)]).

required

Returns:

Type Description
Tuple[str, ...]

The held-out groups, sorted.

group_of(code)

The row's group: its four-digit ancestor at levels 4–6, the code itself at levels 2 and 3.

Folds and Stage 4's resampling run over groups (Req 5: four-digit-parent groups).

group_table_fingerprint(groups)

SHA-256 of the held-out groups' canonical CSV, which equals the committed file's hash.

heldout_tainted(code, heldout_groups)

Whether the code's employment includes a held-out group.

read_codebook_codes(path, expected_sha256)

A supervision bundle codebook's codes, sorted, after checking their fingerprint.

Raises:

Type Description
ValueError

If a code repeats or the codes' fingerprint differs from expected_sha256.

read_group_table(path)

Read the committed held-out groups.

Raises:

Type Description
ValueError

If a group is not a distinct four-digit code.

sector_quotas(counts, fraction, seed)

Largest-remainder quotas of fraction of each sector's groups.

The total is fraction of all groups, rounded half up; remainder ties go to the smaller of per-sector draws from np.random.default_rng([seed]), taken in sorted sector order.

split_counts(rows)

Rows per split, every split named (zero when empty).

write_group_table(groups, path)

Write the canonical CSV and return its sha256 (group_table_fingerprint).

Draw the regressor panel's held-out four-digit groups (roadmap Stage 3; Req 2; Req 4).

naics-embedder data regressor-groups runs this once. The table it writes (conf/data/regressor_heldout_groups.csv) is committed, and the panel reads it from then on, so the held-out regime's outer set never moves. The seen regime's outer set leaves out the held-out groups too, so a redraw moves both outer sets, and its new fingerprint would not count as a reopening: an existing table is replaced only with force.

generate_regressor_group_table(cfg, codebook_path, *, force=False)

Draw the held-out four-digit groups and write the table and its provenance.

Returns:

Type Description
Path

The table's path (cfg.heldout_groups_csv).

Raises:

Type Description
FileExistsError

If the table exists and force is False.

ValueError

If a pinned hash differs or the data are not the branch record's population.

Fitting

Ridge regression on standardized features along a penalty grid (Req 2 "Fitting"; D1).

standardized_ridge_path matches scikit-learn's make_pipeline(StandardScaler(), Ridge(alpha)) at every penalty on the grid, from one singular value decomposition of the standardized fit set. The mean and scale come from the fit rows only, and a constant column keeps scale 1, as StandardScaler does.

best_alpha_index(errors)

The penalty with the least error; a tie goes to the larger penalty (more shrinkage).

squared_errors(y_true, predictions)

Summed squared error per penalty: y_true (m,) against predictions (m, A).

standardized_ridge_path(x_fit, y_fit, x_score, alphas)

Predictions for the score rows at every penalty.

Parameters:

Name Type Description Default
x_fit ndarray

Fit features (n, p).

required
y_fit ndarray

Fit outcomes (n,).

required
x_score ndarray

Features to predict (m, p).

required
alphas Sequence[float]

Penalties (A,), each positive.

required

Returns:

Type Description
ndarray

Predictions (m, A), column a at alphas[a].

Text-only comparator

The text-only comparator of the regressor panel (Req 2; roadmap D9).

D9: "the arm's own backbone, frozen, embedding each code's text, reduced by PCA to the arm's dimension". The table holds one vector per code at the backbone's hidden size; the panel reduces it to the arm's dimension when it scores the arm.

  • Text. The four channels the arm reads (title, description, excluded, examples) from the arm's own descriptions file, without the arm's field markers (spec 4.2).
  • Pooling. Each channel is mean-pooled over its tokens under the attention mask, as the arm's encoder pools (text_model/shared_encoder.py). A code's vector is the mean over its present channels: an absent (null or blank) channel is masked out, never encoded as a placeholder.
  • Frozen. The backbone runs in evaluation mode under torch.no_grad, on the CPU in float32, with no adapter.

build_text_only_table(descriptions_path, output_path, *, backbone, max_length, batch_size=32, model=None, tokenizer=None, revision=None)

Embed every code's text with the frozen backbone and write the table and its provenance.

A channel text whose marked form is over the window is read as its pinned window-fitting summary, as the arm reads it (panels/window_summaries.py); texts are embedded unmarked. model and tokenizer default to load_backbone(backbone); tests pass small ones.

Returns:

Type Description
Path

The table's path.

Raises:

Type Description
ValueError

If max_length exceeds the backbone's trained input window (Req 9), or as resolve_channel_texts: a channel text is over the window and the backbone has no pinned summaries, the pin fits a window other than max_length, or the artifact fails one of the resolver's checks.

encode_code_texts(descriptions, model, tokenizer, *, max_length, batch_size=32)

Embed every code's text with a frozen backbone.

Parameters:

Name Type Description Default
descriptions DataFrame

One row per code with the four channel columns.

required
model Module

A Hugging Face encoder returning last_hidden_state.

required
tokenizer Any

Its tokenizer.

required
max_length int

Tokens kept per channel text.

required
batch_size int

Texts per forward pass.

32

Returns:

Type Description
ndarray

One row per code, in descriptions order: the mean of its present channels'

ndarray

mean-pooled vectors (float64).

Raises:

Type Description
ValueError

If a code has no present channel.

load_backbone(name, *, local_files_only=True)

The backbone's model and tokenizer, from the local Hugging Face cache by default.

Returns:

Type Description
Tuple[Any, Any, Optional[str]]

(model, tokenizer, revision); revision is the resolved snapshot's commit hash.

matrix_fingerprint(codes, matrix)

SHA-256 of the codes and their float64 values, in code order.

pca_reduce(vectors, dimension)

Reduce the vectors to dimension principal components (D9: the arm's dimension).

Raises:

Type Description
ValueError

If dimension exceeds the number of vectors or their width.

provenance_path(table_path)

The provenance JSON written beside a text-only table.

text_only_fingerprint(table)

The table's matrix_fingerprint: the name a regressor read logs it by.

The provenance records it beside the file's own hash (table_sha256), so a logged read matches its table file without re-reading the table.

text_only_frame(codes, vectors)

code plus t0 … t{h-1} (float64).

text_only_matrix(table)

The table's codes and its t columns (float64).

Panel

The regressor panel (Req 2; Req 1, regressor estimand; Req 4; roadmap Stage 3; D1, D7, D8, D9).

An arm's coordinates enter a ridge regression as regressors, and the panel returns out-of-sample predictions per row, keyed by code, year and group, so that Stage 4 can compute whichever statistic it settles on and resample by four-digit group.

  • Rows (D7; Req 21). A code in a feature year t (2022–2024): covariates from year t, and the panel's outcome (qcew_rows): log employment in t + 1 (level, the default) or its log change from t (growth). The outcome names the reads: regressor_seen and regressor_heldout for the levels, growth_seen and growth_heldout for growth.
  • Regimes (D8: each is a panel). seen: every scored code has earlier rows in the fit set; one-hot is a real competitor. heldout: every row of a held-out four-digit group leaves the fit set; it runs at levels 4–6, where four-digit parents exist.
  • Partition. One partition serves both regimes (regressor_splits): validation reads only the remainder, feature years 2022 and 2023 of codes outside the held-out groups.
  • Comparators (Req 2). Covariates alone (log establishments and log wages, D1), and each representation alone and with the covariates: the arm's coordinates, six-digit one-hot (level-L one-hot in the multi-level variant; seen regime only), ancestor indicators at levels 2 to L − 1, and the text-only table reduced by PCA to the arm's dimension (D9). The margin read (Req 24) scores only the comparators that read no arm's tables, and reads no arm.
  • Fitting. Ridge on standardized features, the penalty tuned by nested cross-validation inside the remainder only (FitTask). Fold assignments depend on the regime, level, repeat and group ids alone, never on the arm, so every arm is scored on the same folds (Req 5).
  • Sealing (Req 22). Each regime's outer set is read only after a logged opening by the same panel object. An opening is recorded against the split, not the outcome: one under either outcome's read name counts for both, so a second opening of the same split (the same regime and held-out draw, by fingerprint) needs a stated reason. Every read goes to the selection log, whose n_queries field counts rows here.

ArmTables dataclass

One arm's coordinates and the text-only table reduced to the arm's dimension (D9).

from_tables(coordinates, text_only) classmethod

Pair an arm's coordinate table with the text-only table of the same codes.

Raises:

Type Description
ValueError

If the two tables cover different codes.

lookup(codes)

Row numbers of the codes in this arm's tables.

FitSettings dataclass

How the panel tunes and resamples.

Parameters:

Name Type Description Default
alphas Tuple[float, ...]

Ridge penalties, ascending.

required
folds int

Validation folds per repeat (grouped).

5
repeats int

Validation repeats, each with its own fold assignment.

5
inner_folds int

Tuning folds inside a held-out fit set (grouped).

5
fold_seed int

Base seed of every fold assignment.

20260924
min_groups int

Fewest remainder groups a regime needs at a level; fewer is undefined.

10

FitTask dataclass

One out-of-sample prediction: fit on fit and predict score at the penalty with the least summed squared error over the tuning pairs (fit rows, scored rows).

Row numbers index the frame the plan was built for; fold is -1 for the outer set.

Regime

Bases: str, Enum

The two regressor regimes, each its own panel under Req 5 (D8).

RegressorPanel

The panel rows at each level, the committed held-out draw, and the log every read goes to.

Parameters:

Name Type Description Default
rows_by_level Mapping[int, DataFrame]

Panel rows per level (qcew_rows.panel_rows).

required
heldout_groups Collection[str]

The committed held-out four-digit groups.

required
log SelectionLog

The selection log.

required
settings FitSettings

Tuning and resampling settings.

required
outcome RegressorOutcome

The outcome rows_by_level were built for, which names every read (Req 21).

LEVEL

cell_status(regime, level)

None if the regime is defined at the level, otherwise the reason it is not.

data_fingerprint(level)

SHA-256 of the rows a read at the level scores from: every row there, its split included, sorted by code and feature year, with the columns in name order.

fingerprint names the held-out draw alone, which the log counts openings by, so two panels can share it and still fit other outcomes. This returns no rows, so it is not a read and logs nothing. The values are hashed exactly: rows derived on another platform can differ in a last bit, and then read as other data.

from_sources(*, qcew_dir, qcew_sha256, codebook_codes, heldout_groups_csv, heldout_groups_sha256, log_path, settings, branch_record, levels=LEVELS, outcome=RegressorOutcome.LEVEL) classmethod

The panel from the pinned QCEW slices, a codebook and the committed held-out groups, its rows built for outcome (Req 21).

The groups must be the pinned draw: the log counts openings under the draw's fingerprint, so a table edited or redrawn since would read as never opened.

Raises:

Type Description
ValueError

If the groups' fingerprint is not heldout_groups_sha256, or the data are not the population branch_record names (verify_branch_record).

open_outer(regime, purpose, *, reopen_reason=None)

Open one regime's sealed outer set for this panel object, logging the opening.

Raises:

Type Description
SplitAlreadyOpenedError

If the log already records an opening of this split, under either outcome's read name, and no reopen_reason is given (require_openable).

reference_validation(regime, level, purpose, detail=None)

Out-of-sample predictions for the remainder rows from reference_comparators alone, logging the read: Req 24's margin read, which reads no arm and computes neither embedding nor text_only.

It fits the rows, folds and penalties validation fits, so each of its comparators predicts exactly as in any arm's read. detail joins the read's logged detail, as in validation.

require_arm(arm)

Require the arm to cover every code of every loaded level; logs nothing.

Check before opening an outer set: a test read that failed after the opening would use the opening up. Codes are not sealed; only rows are.

Raises:

Type Description
ValueError

If the arm has no coordinates for a panel code.

require_openable(regime, reopen_reason=None)

Require that open_outer would open the regime's outer set; logs nothing.

Check every regime before opening any: an opening followed by a refused one would be used up for nothing.

Raises:

Type Description
SplitAlreadyOpenedError

If the log already records an opening of this split, under either outcome's read name (seal_names), and no reopen_reason is given.

split_counts(level)

Rows per split at a level (counts only; reading rows goes through the log).

test(regime, level, arm, purpose, detail=None)

Predictions for the regime's outer set from a fit on the whole remainder, logging the read.

detail joins the read's logged detail, as in validation.

Raises:

Type Description
SealedSplitError

If this panel object has not opened the regime's outer set.

validation(regime, level, arm, purpose, detail=None)

Out-of-sample predictions for the remainder rows, logging the read.

detail joins the read's logged detail, so a caller can name the run it scores; it cannot replace a key the panel logs itself.

comparators(regime, level)

Every comparator the regime scores at a level, covariates alone first.

One-hot runs only in the seen regime, where it can predict more than the intercept; ancestor indicators need a level above 2.

coordinate_matrix(table)

The codes and coordinates of an arm's table in the export form Req 2 names.

Every column other than code and the export's index and level metadata is a coordinate.

Raises:

Type Description
ValueError

If codes repeat, a coordinate is not finite, or the rows lie on a hyperboloid: Lorentz points (the train prompt's hyp_e* export) are not the export form, which is tangent coordinates at the origin for a hyperbolic arm (Stage 6's export). Also if a column is constant: it carries nothing and would count toward the arm's dimension, which the text-only comparator is reduced to (D9). A log map at the origin keeps such a column, its zero time coordinate.

feature_matrix(comparator, frame, level, arm)

The comparator's features for every row of frame.

Indicator columns come from the codes in frame, never from outcomes.

Raises:

Type Description
ValueError

If the comparator reads an arm's table and arm is None.

fit_settings(cfg)

The config's tuning and resampling settings.

group_folds(groups, n_folds, seed)

A fold per row: the distinct groups, sorted, permuted by np.random.default_rng(seed) and dealt into n_folds folds in turn, so a group's rows share a fold.

Raises:

Type Description
ValueError

If there are fewer groups than folds.

heldout_validation_plan(frame, level, settings)

Repeated grouped folds over the remainder; each fold's penalty is tuned by grouped folds inside the rest of the remainder (nested), so a scored group never tunes its own penalty.

indicators(values)

One column per distinct value (sorted), one 1 per row.

load_regressor_panel(cfg, codebook_path, *, log_path=None, levels=LEVELS)

The panel the config describes, its outcome included, over a codebook, logging to log_path or the config's.

looks_lorentz(matrix, rtol=0.001)

Whether every row lies on one hyperboloid x0^2 - |x|^2 = 1/c with x0 > 0.

A float32 export's rounding error in x0^2 - |x|^2 grows with x0^2, so each row's tolerance scales with its x0^2, and the level 1/c is read from the row nearest the origin, whose error is least; a level set by far rows (their median, say) would fail the near ones. The level's sign is not checked: when even the nearest row is far out, rounding can push its value below zero.

outer_plan(frame, regime, level, settings)

Fit on the whole remainder and predict the regime's outer set once.

The penalty is tuned inside the remainder only: in the seen regime on the forward split (2022 rows fit, 2023 rows scored), in the held-out regime by grouped folds.

reference_comparators(regime, level)

The regime's comparators at a level that read no arm's tables: covariates, one-hot (seen regime only) and ancestor indicators, each alone and with the covariates (Req 24's margin read).

require_branch_record(cfg)

The configured branch record.

Raises:

Type Description
ValueError

If the config has none.

run_plan(tasks, x, y, alphas)

Each task's chosen penalty and its predictions for the scored rows.

seal_names(regime)

Every read name an opening of the regime's outer set is logged under, one per outcome.

The outer sets are rows, and 2025 employment underlies both outcomes' outer rows, so one opening of a regime's split counts for both (Req 22).

seen_validation_plan(frame, level, settings)

Fit on the remainder's 2022 rows and predict its 2023 rows, forward in time (D7).

The 2023 rows fall into grouped folds; each fold's penalty is chosen on the other folds' 2023 rows, so no scored row tunes its own penalty. Repeats redraw the folds.

summarize(predictions)

Descriptive fit per panel, split, level and comparator: rows, RMSE and R² pooled over repeats.

A report for reading, not the decision statistic (Stage 4 settles that).

table_fingerprint(table)

An arm's coordinate table's matrix_fingerprint: the name a regressor read logs it by.

verify_branch_record(record, codebook_codes, six_digit_population)

Require the panel's data to be what Stage 1's finding dictates.

Parameters:

Name Type Description Default
record Mapping[str, Any]

The branch record (RegressorBranchRecord fields).

required
codebook_codes Collection[str]

The codebook's codes.

required
six_digit_population Collection[str]

The six-digit codes with a usable cell in every window year.

required

Raises:

Type Description
ValueError

If the record names another branch, years, ownership or grain than this panel reads, or the population is not the codebook's six-digit codes without the record's excluded codes.