Shared libraries
Two pure, DOM-free, dependency-free ES modules carry everything the apps compute.
lib/met-stats.mjs — the verification math (708 lines, 129/129 self-test)
Section titled “lib/met-stats.mjs — the verification math (708 lines, 129/129 self-test)”The single source of truth for MET-style statistics, reconciled from the seven original
apps. Conventions: a 2×2 cell is {a,b,c,d} = {FY_OY, FY_ON, FN_OY, FN_ON}; an SL1L2 line
carries the means {fbar,obar,ffbar,oobar,fobar} + n exactly as MET writes them; every
metric returns NaN (never throws, never Infinity) on degenerate denominators.
| Group | Exports |
|---|---|
| Categorical (2×2) | baseRate fcstRate podRate far pofd sr csi fbias gss hss pss orss categoricalFromCTC |
| Continuous from SL1L2 | me mse rmse bcrmseFromSL1L2 pearsonFromSL1L2 continuousFromSL1L2 sl1l2FromPairs |
| Continuous from pairs | mePairs maePairs msePairs rmsePairs bcrmsePairs pearsonPairs continuousFromPairs maeFromSums |
| Vector wind (VL1L2) | vectorContinuousFromVL1L2 (FBAR, OBAR, FS_RMS, OS_RMS, MSVE, RMSVE) |
| Aggregation | sumCTC sumSL1L2 aggregateCategorical aggregateContinuous weightedMean — and the teaching demonstrator wrongMeanOfRatios |
| Ensemble | rankOne rankHistogram classifyRankHist crpsOne crpsAggregate ensembleSpread ensembleEventProb perCaseSpreadSkill spreadSkill contingencyFromEnsemble |
| Probabilistic | brierDecompFromBins (Murphy decomposition) reliabilityDiagram |
| Formatting | fmt (NaN → em-dash, ±Infinity → ∞) |
The aggregation contract is the lab’s core fidelity rule: sum raw counts / partial sums
first, derive the metric second (ratio-of-sums). wrongMeanOfRatios exists only so apps
can show the incorrect value next to the correct one.
lib/met-stat-parse.mjs — the .stat parser (490 lines, 97/97 self-test)
Section titled “lib/met-stat-parse.mjs — the .stat parser (490 lines, 97/97 self-test)”Header-driven and version-tolerant: it reads the 24-column common header from the file (or
recognizes headerless .stat), then applies per-line-type column schemas confirmed against
the MET v12.2 source (not just docs).
| Group | Exports |
|---|---|
| Core | parseStat parseMode columnNamesFor COMMON_HEADER |
| Line-type adapters | ctcCell sl1l2Line vl1l2Line vcntStats pctBins pstdStats prcPoints rhistBins ecntStats orankRecord modeCtsCell modeObject |
Contract: common-header fields come back as strings (they are untrusted display values);
statistics are numbers or null (MET’s NA).
Validation on the real archive
Section titled “Validation on the real archive”- 6,329
.statfiles, 88,456 records, 0 parse errors (full 62 GiB archive). - Independent oracles: 32,389/32,390 SL1L2→CNT and 7,038/7,038 VL1L2→VCNT recomputations equal MET’s own paired output lines.
- The parser’s fixtures are real MET lines — a lesson from the
N_THRESHincident, where hand-written fixtures shared the code’s wrong assumption and 76/76 tests passed over a bug.
lib/met-data-source.mjs — the data funnel (built)
Section titled “lib/met-data-source.mjs — the data funnel (built)”The blueprint-§2 normalization + selection layer, now real: .stat text (or the compact
public bundle) → normalized per-case records keyed by
(model, var@lev, lead, obtype, desc, interp, mask, thresh, cycle) carrying raw
aggregables, with select(), aggregateCTC/SL1L2/VL1L2() (ratio-of-sums),
series() (optionally with per-point bootstrap CIs), and toBundle()/fromBundle() — the
de-identified columnar wire format apps 02/04/12 stream from the site root (3.6 MB raw,
~0.8 MB gzipped, 44,228 cases).
Selftest: 65/65 including a full-archive pass where every sampled single-case
aggregate reproduces MET’s own paired CNT line (471/471). That oracle pass caught two
real data-model traps during development: MET emits lines distinguishable only by DESC
(same OBTYPE, different obs source — pooling them is wrong, so desc is in the key), and
one model’s upper-air lines carry FCST_LEAD=000000 with the true lead only in the
filename (fromStatTexts(texts, {names}) repairs exactly that case).
De-identification note: the real-name→alias map is not in this public module —
publishing the mapping would reveal what it hides. tools/build-real-bundle.mjs takes it
as build-time CLI input and hard-fails if any source token survives in the output; cycle
timestamps are always replaced by ordinal ids.
lib/met-mode.mjs — object-based verification (built)
Section titled “lib/met-mode.mjs — object-based verification (built)”A faithful-core MODE engine: missing-aware circular-disc convolution, 8-connected object identification, single-object attributes (moments, convex hull, intensity percentiles), pair attributes, MET’s fuzzy-logic total-interest engine (MODEConfig_default weights, configurable piecewise maps), matching and union-find cluster merging. Selftest 42/42 (hand-worked geometry + invariants on the real case; no MODE oracle exists in this archive — stated honestly). Powers the MODE Lab.
Uncertainty in met-stats
Section titled “Uncertainty in met-stats”bootstrapCI(items, statFn, {B, alpha, seed, method: 'percentile' | 'bca'}) — true
case-level bootstrap with deterministic seeding and a jackknife-accelerated BCa option;
the lib selftest grew to 140/140 (coverage against a known truth, reproducibility,
degenerate inputs, BCa behavior on skewed statistics). Measured cost ~0.4 ms per CI at
B=1000 — cheap enough that apps 02/04 recompute bands on every interaction.
