Raw model output on the native 0.25° grid — no MOS, no bias correction, no post-processing. Scores understate operational forecast quality. Read the fairness statement →

CastCheck methodology v0.2 · data through 2026-08-30

Data

Everything published here is recomputable from the archived station values, the truth table and this repository at a given commit. Nothing is hand-edited. Errors are stored in °C and displayed in °F on the site; the downloads below are in °C.

Downloads

FileContents RowsSize
scores_latest.csvevery published aggregate: station × model × init × lead × variable × method × window, with n, MAE, bias, RMSE, hit rates, skill and bootstrap intervals (values in °C). 4755210.6 MB
pairwise_latest.csv.gzpaired model-vs-model MAE differences on common days, with the bootstrap interval and the significance flag (°C). 1603842.0 MB
daily_errors.csv.gzthe complete per-day record behind every score: one row per station, model, initialization, lead day, method, variable and climatological day, with the forecast, the observation and the signed error (°C). This is the file to download if you want to recompute anything. 1771802.0 MB
stations.csvstation metadata: identifier, name, truth product, fixed standard UTC offset, coordinates and elevation. 231.8 kB
models.csvmodel registry, including the persistence baseline. 12792 B

The parquet shards behind these CSVs are mirrored as a Hugging Face dataset: https://huggingface.co/datasets/castcheck/temperature-verification. The code that produces them is at https://github.com/zifanzhang/castcheck (MIT).

JSON API (v1)

Static files, CORS-open, cached one hour. See the API page for worked curl and Python examples and openapi.json.

EndpointContentsRows/files
scores/latest.jsonevery published score (all stations, models, leads, windows)47552
scores/leaderboard.jsonthe station_id=ALL slice used by the front page
leaderboard/{window}-{init}z-{method}-{variable}.jsonone pre-built file per leaderboard view (32 of them)
scores/{station}/{model}/{lead}.jsonone permanent-link card plus the last 90 days of daily errors
pairwise/latest.jsonmodel-vs-model paired bootstrap (station_id=ALL)160384
stations.jsonstation metadata
models.jsonmodel metadata
status.jsonpipeline completeness report
openapi.jsonOpenAPI 3.1 description of the endpoints above

Table schemas

forecast_values

one extracted station value per model run, valid time, variable and interpolation method (DESIGN §3.1).

ColumnTypeUnitMeaning
model_idstringstable model identifier from config/models.yaml
model_versionstringupstream cycle/version string as advertised by the producer
init_timetimestampUTCmodel initialization time
valid_timetimestampUTCforecast valid time
lead_hint16hoursvalid_time − init_time
station_idstringICAO identifier, or ALL for the cross-station aggregate
variablestringtmax or tmin (daily extreme of the four common samples)
bucket_hint8hoursaccumulation window of a native extreme field, 0 if instantaneous
methodstringgrid-to-station interpolation: bilinear (headline) or nearest
value_cfloat32°Cextracted station value, NaN when missing
missing_reasonstringwhy a value is absent; empty when present
source_urlstringexact object or URL the value was read from
fetched_attimestampUTCwhen the value was fetched

truth_daily

one row per station-day-source with the first-final policy and QC flags (DESIGN §3.2).

ColumnTypeUnitMeaning
station_idstringICAO identifier, or ALL for the cross-station aggregate
climo_datedateLST dayclimatological day, midnight to midnight local standard time
sourcestringtruth product: CLI, CF6 or OBS
tmax_fint16°Fdaily maximum as reported by the NWS (whole degrees)
tmin_fint16°Fdaily minimum as reported by the NWS (whole degrees)
tmax_cfloat32°Cdaily maximum converted to °C
tmin_cfloat32°Cdaily minimum converted to °C
issuance_timetimestampUTCissuance time of the truth product
is_finalboolfirst CLI issued after local midnight (the first-final policy)
revisedboola later corrected report exists (never used in scores)
revised_tmax_fint16°Flatest corrected maximum, published but not scored
revised_tmin_fint16°Flatest corrected minimum, published but not scored
qc_flagstringquality note, empty when clean
product_idstringapi.weather.gov product id or IEM archive key
schema_versionstringdata-model version (DESIGN §3)
methodology_versionstringMETHODOLOGY version that produced the numbers

daily_forecasts

sampled and native daily extremes per model run, station and climatological day (DESIGN §3.3).

ColumnTypeUnitMeaning
model_idstringstable model identifier from config/models.yaml
model_versionstringupstream cycle/version string as advertised by the producer
init_timetimestampUTCmodel initialization time
station_idstringICAO identifier, or ALL for the cross-station aggregate
climo_datedateLST dayclimatological day, midnight to midnight local standard time
lead_dayint8daystarget climatological date − UTC date of the initialization
methodstringgrid-to-station interpolation: bilinear (headline) or nearest
tmax_sampled_cfloat32°Cmax of the four common samples in the climatological day
tmin_sampled_cfloat32°Cmin of the four common samples in the climatological day
n_samplesint8counthow many of the four common samples were present (0–4)
tmax_native_cfloat32°Cdaily max from the model's native extreme field (diagnostic)
tmin_native_cfloat32°Cdaily min from the model's native extreme field (diagnostic)
missing_reasonstringwhy a value is absent; empty when present
schema_versionstringdata-model version (DESIGN §3)
methodology_versionstringMETHODOLOGY version that produced the numbers

scores

published aggregates with bootstrap intervals (DESIGN §3.4).

ColumnTypeUnitMeaning
station_idstringICAO identifier, or ALL for the cross-station aggregate
model_idstringstable model identifier from config/models.yaml
init_hourint8UTC hourmodel initialization hour, 0 or 12
lead_dayint8daystarget climatological date − UTC date of the initialization
variablestringtmax or tmin (daily extreme of the four common samples)
methodstringgrid-to-station interpolation: bilinear (headline) or nearest
windowstringscoring window: 30d, 90d, 365d or all
nint32daysnumber of scored climatological days in the window
n_stationsfloat32stationsmean number of stations behind each day of an ALL row (1 for a single-station row)
n_flaggedint32dayshow many of those days carry a QC flag on the observation
maefloat32°Cmean absolute error, forecast − observed
biasfloat32°Cmean signed error; positive = model too warm
rmsefloat32°Croot mean squared error
hit1ffloat32fractionshare of days with |error| ≤ 1 °F
hit2ffloat32fractionshare of days with |error| ≤ 2 °F
hit3ffloat32fractionshare of days with |error| ≤ 3 °F
mae_debiasedfloat32°CMAE after removing the per-station constant bias of the window; the part of the error that is not a fixed offset
skill_persistencefloat32fraction1 − MAE/MAE(persistence); positive is better
skill_persistence_debiasedfloat32fractionthe same skill score computed on the debiased errors, so a station with a large constant offset is not scored as skill-less
mae_ci_lowfloat32°C2.5th percentile, moving-block bootstrap
mae_ci_highfloat32°C97.5th percentile, moving-block bootstrap
bias_ci_lowfloat32°C2.5th percentile of the bias bootstrap
bias_ci_highfloat32°C97.5th percentile of the bias bootstrap
rmse_ci_lowfloat32°C2.5th percentile of the RMSE bootstrap
rmse_ci_highfloat32°C97.5th percentile of the RMSE bootstrap
hit1f_ci_lowfloat32fraction2.5th percentile of the ±1 °F hit-rate bootstrap
hit1f_ci_highfloat32fraction97.5th percentile of the ±1 °F hit-rate bootstrap
model_versionstringupstream cycle/version string as advertised by the producer
segment_startdateLST dayfirst day of the current model-version segment; scores cover only this segment
period_startdateLST dayfirst climatological day contributing to the window
period_enddateLST daylast climatological day contributing to the window
computed_attimestampUTCwhen this table was computed
methodology_versionstringMETHODOLOGY version that produced the numbers
schema_versionstringdata-model version (DESIGN §3)

pairwise

paired model-vs-model MAE differences (DESIGN §3.5).

ColumnTypeUnitMeaning
station_idstringICAO identifier, or ALL for the cross-station aggregate
init_hourint8UTC hourmodel initialization hour, 0 or 12
lead_dayint8daystarget climatological date − UTC date of the initialization
variablestringtmax or tmin (daily extreme of the four common samples)
windowstringscoring window: 30d, 90d, 365d or all
model_astringfirst model of the pair
model_bstringsecond model of the pair
n_commonint32daysdays on which both models of the pair have a forecast
mae_difffloat32°CMAE(model_a) − MAE(model_b) on their common days
ci_lowfloat32°C2.5th percentile of the paired bootstrap difference
ci_highfloat32°C97.5th percentile of the paired bootstrap difference
significantbooltrue when the 95 % interval of the difference excludes zero
methodstringgrid-to-station interpolation: bilinear (headline) or nearest
computed_attimestampUTCwhen this table was computed
methodology_versionstringMETHODOLOGY version that produced the numbers
schema_versionstringdata-model version (DESIGN §3)

Licences and attribution

Raw model output, no post-processing. These are values interpolated from each model's native grid, without MOS, bias correction, downscaling or any other post-processing, and they are not the products that ECMWF, NOAA, the NWS or any app vendor issue to end users.

Cite the dataset

CastCheck (2026). Station-level verification of raw weather-model 2 m temperature forecasts [data set]. Methodology version 0.2, schema version 0.1, data through 2026-08-30. https://castcheck.zifanzhang.com

For a specific number, cite its permanent link instead — every score card carries a short and a long citation with the access date.

Changelog

MethodologyDateChange
v0.12026-08-30First public build: 2 m temperature, 23 stations, ECMWF IFS HRES, NCEP GFS and the NOAA/CIRA AIWP models, persistence baseline, moving-block bootstrap intervals.

Data events (a source outage, a corrected observation, a model version change) are recorded in status and in the gaps list of status.json.

Known limitations

  1. Daily extremes are the max/min of four 6-hourly samples, so they under-state the true afternoon peak and over-state the pre-dawn trough. The bias is identical for every model and is therefore fair for comparison, but the absolute errors are not what a user of a post-processed forecast experiences (METHODOLOGY §2.3).
  2. No elevation or lapse-rate correction is applied; stations whose elevation differs sharply from the 0.25° grid cell carry a representativeness error that is charged to the model.
  3. Truth is the first final NWS CLI report. Later corrections are stored but never change a published score, so a corrected observation leaves a permanent, documented discrepancy.
  4. Models enter the record on different dates, so their windows are not identical. Pairwise comparisons are computed on common days only; the leaderboard columns are not.
  5. Groups with fewer than 30 scored days are published but greyed out and excluded from every ranking; early in a model's record most windows are in that state.
  6. The 0.25° AIWP archive is a research product; outages there appear here as gaps, not as bad forecasts (see /status/).