File size: 2,859 Bytes
186a48a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #!/usr/bin/env python3
import argparse
import json
from pathlib import Path
import sys
import matplotlib.pyplot as plt
import numpy as np
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from model.streamflow_lstm import metrics, write_json
parser = argparse.ArgumentParser(description="Evaluate streamflow forecasts and baselines")
parser.add_argument("--config", default="conf/config.yaml")
args = parser.parse_args()
with open(args.config, encoding="utf-8") as handle:
config = json.load(handle)
with np.load(config["paths"]["predictions"]) as data:
methods = {name: data[name] for name in ("prediction", "persistence", "glofas")}
target, gauges, lead_hours = data["target"], data["gauges"].astype(str), data["lead_hours"]
lead_days = {"2_day": 7, "5_day": 19, "8_day": 31}
report = {"lead_definition": "zero-based indices 7/19/31 equal 48/120/192 hours", "gauges": {}}
for gauge_index, gauge in enumerate(gauges):
report["gauges"][gauge] = {}
for method, values in methods.items():
report["gauges"][gauge][method] = {
label: metrics(values[gauge_index, :, index], target[gauge_index, :, index])
for label, index in lead_days.items()
}
numbers = [value for gauge in report["gauges"].values() for method in gauge.values()
for lead in method.values() for value in lead.values()]
if not np.all(np.isfinite(numbers)):
raise FloatingPointError("evaluation metrics are not finite")
write_json(config["paths"]["evaluation_metrics"], report)
colors = {"prediction": "#146c94", "persistence": "#d17a22", "glofas": "#6a8e3a"}
figure, axes = plt.subplots(2, 1, figsize=(11, 8), constrained_layout=True)
for method, values in methods.items():
rmse = [np.mean([metrics(values[g, :, lead], target[g, :, lead])["rmse_m3_s"] for g in range(len(gauges))])
for lead in range(len(lead_hours))]
axes[0].plot(lead_hours / 24, rmse, label=method, color=colors[method], linewidth=2)
axes[0].set(title="Mean gauge RMSE across forecast lead", xlabel="Lead (days)", ylabel="RMSE (m3 s-1)")
axes[0].grid(alpha=0.25)
axes[0].legend()
x = np.arange(len(gauges))
width = 0.25
for offset, (method, values) in enumerate(methods.items()):
kge = [metrics(values[g, :, 19], target[g, :, 19])["kge"] for g in range(len(gauges))]
axes[1].bar(x + (offset - 1) * width, kge, width, label=method, color=colors[method])
axes[1].axhline(1 - np.sqrt(2), color="black", linestyle="--", linewidth=1, label="KGE skill threshold")
axes[1].set(title="Gauge KGE at 5-day lead", xlabel="Gauge", ylabel="KGE", xticks=x, xticklabels=gauges)
axes[1].legend(ncol=4, fontsize=8)
comparison = Path(config["paths"]["comparison"])
comparison.parent.mkdir(parents=True, exist_ok=True)
figure.savefig(comparison, dpi=150)
plt.close(figure)
print(config["paths"]["evaluation_metrics"], comparison)
|