update for 4 hour rain forecast
This commit is contained in:
@@ -112,6 +112,34 @@ CREATE INDEX IF NOT EXISTS idx_predictions_rain_1h_site_ts
|
||||
CREATE INDEX IF NOT EXISTS idx_predictions_rain_1h_pending_eval
|
||||
ON predictions_rain_1h(site, evaluated_at, ts DESC);
|
||||
|
||||
-- Rain model predictions (next 4h)
|
||||
CREATE TABLE IF NOT EXISTS predictions_rain_4h (
|
||||
ts TIMESTAMPTZ NOT NULL,
|
||||
generated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
site TEXT NOT NULL,
|
||||
model_name TEXT NOT NULL,
|
||||
model_version TEXT NOT NULL,
|
||||
threshold DOUBLE PRECISION NOT NULL,
|
||||
probability DOUBLE PRECISION NOT NULL,
|
||||
predict_rain BOOLEAN NOT NULL,
|
||||
|
||||
rain_next_4h_mm_actual DOUBLE PRECISION,
|
||||
rain_next_4h_actual BOOLEAN,
|
||||
evaluated_at TIMESTAMPTZ,
|
||||
|
||||
metadata JSONB,
|
||||
|
||||
PRIMARY KEY (site, model_name, model_version, ts)
|
||||
);
|
||||
|
||||
SELECT create_hypertable('predictions_rain_4h', 'ts', if_not_exists => TRUE);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_predictions_rain_4h_site_ts
|
||||
ON predictions_rain_4h(site, ts DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_predictions_rain_4h_pending_eval
|
||||
ON predictions_rain_4h(site, evaluated_at, ts DESC);
|
||||
|
||||
-- Raw retention: 90 days
|
||||
DO $$
|
||||
BEGIN
|
||||
|
||||
@@ -100,44 +100,104 @@ FROM baseline;
|
||||
|
||||
|
||||
CREATE OR REPLACE VIEW rain_prediction_drift_daily AS
|
||||
WITH all_predictions AS (
|
||||
SELECT
|
||||
1::INT AS horizon_hours,
|
||||
ts,
|
||||
site,
|
||||
model_name,
|
||||
model_version,
|
||||
threshold,
|
||||
probability,
|
||||
predict_rain
|
||||
FROM predictions_rain_1h
|
||||
UNION ALL
|
||||
SELECT
|
||||
4::INT AS horizon_hours,
|
||||
ts,
|
||||
site,
|
||||
model_name,
|
||||
model_version,
|
||||
threshold,
|
||||
probability,
|
||||
predict_rain
|
||||
FROM predictions_rain_4h
|
||||
)
|
||||
SELECT
|
||||
time_bucket(INTERVAL '1 day', ts) AS day,
|
||||
site,
|
||||
model_name,
|
||||
model_version,
|
||||
horizon_hours,
|
||||
count(*) AS prediction_rows,
|
||||
avg(probability) AS probability_mean,
|
||||
stddev_samp(probability) AS probability_stddev,
|
||||
avg(CASE WHEN predict_rain THEN 1.0 ELSE 0.0 END) AS predicted_positive_rate,
|
||||
avg(threshold) AS threshold_mean
|
||||
FROM predictions_rain_1h
|
||||
GROUP BY 1,2,3,4;
|
||||
FROM all_predictions
|
||||
GROUP BY 1,2,3,4,5;
|
||||
|
||||
|
||||
CREATE OR REPLACE VIEW rain_calibration_drift_daily AS
|
||||
WITH all_predictions AS (
|
||||
SELECT
|
||||
1::INT AS horizon_hours,
|
||||
ts,
|
||||
site,
|
||||
model_name,
|
||||
model_version,
|
||||
probability,
|
||||
predict_rain,
|
||||
rain_next_1h_actual AS rain_actual
|
||||
FROM predictions_rain_1h
|
||||
UNION ALL
|
||||
SELECT
|
||||
4::INT AS horizon_hours,
|
||||
ts,
|
||||
site,
|
||||
model_name,
|
||||
model_version,
|
||||
probability,
|
||||
predict_rain,
|
||||
rain_next_4h_actual AS rain_actual
|
||||
FROM predictions_rain_4h
|
||||
)
|
||||
SELECT
|
||||
time_bucket(INTERVAL '1 day', ts) AS day,
|
||||
site,
|
||||
model_name,
|
||||
model_version,
|
||||
count(*) FILTER (WHERE rain_next_1h_actual IS NOT NULL) AS evaluated_rows,
|
||||
avg(probability) FILTER (WHERE rain_next_1h_actual IS NOT NULL) AS mean_probability,
|
||||
avg(CASE WHEN rain_next_1h_actual THEN 1.0 ELSE 0.0 END) FILTER (WHERE rain_next_1h_actual IS NOT NULL) AS observed_positive_rate,
|
||||
avg(power(probability - CASE WHEN rain_next_1h_actual THEN 1.0 ELSE 0.0 END, 2.0))
|
||||
FILTER (WHERE rain_next_1h_actual IS NOT NULL) AS brier_score,
|
||||
horizon_hours,
|
||||
count(*) FILTER (WHERE rain_actual IS NOT NULL) AS evaluated_rows,
|
||||
avg(probability) FILTER (WHERE rain_actual IS NOT NULL) AS mean_probability,
|
||||
avg(CASE WHEN rain_actual THEN 1.0 ELSE 0.0 END) FILTER (WHERE rain_actual IS NOT NULL) AS observed_positive_rate,
|
||||
avg(power(probability - CASE WHEN rain_actual THEN 1.0 ELSE 0.0 END, 2.0))
|
||||
FILTER (WHERE rain_actual IS NOT NULL) AS brier_score,
|
||||
avg(
|
||||
CASE
|
||||
WHEN rain_next_1h_actual IS NULL THEN NULL
|
||||
WHEN predict_rain = rain_next_1h_actual THEN 1.0
|
||||
WHEN rain_actual IS NULL THEN NULL
|
||||
WHEN predict_rain = rain_actual THEN 1.0
|
||||
ELSE 0.0
|
||||
END
|
||||
) AS decision_accuracy
|
||||
FROM predictions_rain_1h
|
||||
GROUP BY 1,2,3,4;
|
||||
FROM all_predictions
|
||||
GROUP BY 1,2,3,4,5;
|
||||
|
||||
|
||||
CREATE OR REPLACE VIEW rain_pipeline_health AS
|
||||
WITH sites AS (
|
||||
WITH prediction_latest AS (
|
||||
SELECT
|
||||
site,
|
||||
max(generated_at) AS prediction_generated_latest_ts,
|
||||
max(evaluated_at) AS prediction_evaluated_latest_ts
|
||||
FROM (
|
||||
SELECT site, generated_at, evaluated_at FROM predictions_rain_1h
|
||||
UNION ALL
|
||||
SELECT site, generated_at, evaluated_at FROM predictions_rain_4h
|
||||
) p
|
||||
GROUP BY site
|
||||
),
|
||||
sites AS (
|
||||
SELECT DISTINCT site FROM observations_ws90
|
||||
UNION
|
||||
SELECT DISTINCT site FROM observations_baro
|
||||
@@ -145,12 +205,16 @@ WITH sites AS (
|
||||
SELECT DISTINCT site FROM forecast_openmeteo_hourly
|
||||
UNION
|
||||
SELECT DISTINCT site FROM predictions_rain_1h
|
||||
UNION
|
||||
SELECT DISTINCT site FROM predictions_rain_4h
|
||||
)
|
||||
SELECT
|
||||
s.site,
|
||||
(SELECT max(ts) FROM observations_ws90 w WHERE w.site = s.site) AS ws90_latest_ts,
|
||||
(SELECT max(ts) FROM observations_baro b WHERE b.site = s.site) AS baro_latest_ts,
|
||||
(SELECT max(ts) FROM forecast_openmeteo_hourly f WHERE f.site = s.site) AS forecast_latest_ts,
|
||||
(SELECT max(generated_at) FROM predictions_rain_1h p WHERE p.site = s.site) AS prediction_generated_latest_ts,
|
||||
(SELECT max(evaluated_at) FROM predictions_rain_1h p WHERE p.site = s.site) AS prediction_evaluated_latest_ts
|
||||
FROM sites s;
|
||||
p.prediction_generated_latest_ts,
|
||||
p.prediction_evaluated_latest_ts
|
||||
FROM sites s
|
||||
LEFT JOIN prediction_latest p
|
||||
ON p.site = s.site;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
-- Add 4-hour rain prediction storage table.
|
||||
-- Safe to re-run.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS predictions_rain_4h (
|
||||
ts TIMESTAMPTZ NOT NULL,
|
||||
generated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
site TEXT NOT NULL,
|
||||
model_name TEXT NOT NULL,
|
||||
model_version TEXT NOT NULL,
|
||||
threshold DOUBLE PRECISION NOT NULL,
|
||||
probability DOUBLE PRECISION NOT NULL,
|
||||
predict_rain BOOLEAN NOT NULL,
|
||||
|
||||
rain_next_4h_mm_actual DOUBLE PRECISION,
|
||||
rain_next_4h_actual BOOLEAN,
|
||||
evaluated_at TIMESTAMPTZ,
|
||||
|
||||
metadata JSONB,
|
||||
|
||||
PRIMARY KEY (site, model_name, model_version, ts)
|
||||
);
|
||||
|
||||
SELECT create_hypertable('predictions_rain_4h', 'ts', if_not_exists => TRUE);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_predictions_rain_4h_site_ts
|
||||
ON predictions_rain_4h(site, ts DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_predictions_rain_4h_pending_eval
|
||||
ON predictions_rain_4h(site, evaluated_at, ts DESC);
|
||||
Reference in New Issue
Block a user