| |
| 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) |
|
|