Skip to content

Guides

Extending dataset()

Two ways to grow the composed frame without touching the parity-critical core: station panels (dataset(stations=[…]) stacks per-station calendars into one long-format frame) and the experimental contributor registry (register your own labels-only feature block, composed in via features=[…]).

Pass stations=[...] (instead of a single station=) to compose a long-format panel — one call fans out to per-station single-station dataset() calls and concatenates them. The station column comes first, caller order is preserved, and each station gets its own settlement calendar and its own label-recipe resolution (mixed-venue panels are fine).

import mostlyright
df = mostlyright.dataset(
stations=["KNYC", "KLAX", "KORD"],
from_date="2025-01-06",
to_date="2025-01-12",
label="cli",
)
# Long-format: the `station` column is first, then the usual daily columns.
# Rows/columns are exactly the concat of the three single-station calls.
print(df.head())
# Nested per-station provenance hangs off attrs["stations"].
print(df.attrs["stations"]["NYC"]) # that station's flat single-call attrs

Rules worth knowing:

  • stations= is mutually exclusive with station= / city= / contract= / contracts=. The station list must be non-empty and unique (a duplicate would double-count rows).
  • Panels require the DataFrame return path in Python (return_type="list" raises).
  • Mixed-venue is OK — one station on label="cli", another sliced from a BYO frame, etc. — as long as each resolves to a valid recipe.
  • A BYO label frame for a panel must carry a station column so each label maps to the right station calendar (the aligner raises LabelAlignmentError(reason="missing_station_panel") otherwise).
  • In Python, the nested provenance lives at df.attrs["stations"] keyed by the resolved (normalized) station code. In TypeScript, datasetPanel() returns { rows, provenance } — the same map hangs off provenance.stations (TS has no .attrs seam).

A contributor is one function — contribute(entity, window, grain) -> {key: {prefixed_col: value}} — plus declared metadata (a column prefix, an archive_class, an injection_point, and the columns it emits). Registered contributors compose into dataset() through the features=[...] selector with no core change. The built-in "forecasts", "satellite", "cwop", and "trades" features are themselves built on this same registry.

from mostlyright.experimental import contributor
@contributor(
"era5_ssrd", # unique registry name + features=[...] token
"ssrd_", # column prefix (must end with "_")
"refetchable", # archive_class: "refetchable" | "ledger"
injection_point="post_join", # "pre_aggregation" | "post_join"
columns=("ssrd_mean",), # REQUIRED for third-party: the prefixed columns
)
def era5_ssrd(entity, window, grain, *, tz_override=None):
from_date, to_date = window
# ... fetch + aggregate to one row per LST settlement day ...
# keyed by the documented (station, settlement_date) tuple
# (a plain settlement-date string is also accepted):
return {(entity, "2025-01-06"): {"ssrd_mean": 12.3}, ...}

Once registered, add the contributor’s name to features=[...]:

df = mostlyright.dataset(
"KNYC", "2025-01-06", "2025-01-08",
label="cli",
features=["era5_ssrd"],
)
# df now carries an additive ssrd_mean column; label columns untouched.

A contributor can never emit or override a label column. The firewall forbids the reserved obs_ / cli_ / wu_ / noaa_wrh_ prefixes and the exact label / date / station names — categorically, with no flag that could ever disable it. A contributor that declares or emits a firewalled column raises a loud typed error and refuses to compose. This is the inverse of the BYO-label collision guard: labels are produced in exactly one place, and features can never shadow them.

The registry distinguishes three failure modes deliberately:

What happensResult
contribute() raises at compose time (or covers zero days)its declared columns join as an all-NaN block, a RuntimeWarning fires, the record lands in df.attrs["contributor_errors"], and the dataset() call still succeeds
a missing optional extra (SourceUnavailableError)re-raised loudly — a missing [satellite] / [cwop] install is a hard, actionable error, not a silent NaN block
a firewall / prefix violationraises loudly at registration/compose — it is a programming error in the contributor

Because a raising contribute() still materializes its declared columns as all-NaN, columns never silently vanish — the horizontal concat stays a pure LEFT join (label columns and row order untouched). Inspect df.attrs["contributor_errors"] after a run to see which contributors degraded.