The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

xaiHydro: Explainable AI Tools for Hydro-Climate Modelling

A Unified R Workflow — Book Chapter Companion Vignette

Sadikul Islam

Department of Hydrology, [Your Institution], India
sadikul.islam@institution.ac.in

2026-09-13


Citation: Islam, S. (2026). Explainable AI for Hydro-Climate Models: A Unified R Workflow Using SHAP, LIME and Partial Dependence. In: Hydro-Climate Analytics: Remote Sensing, AI and Geospatial Modelling (Springer). DOI: 10.XXXX/XXXXXXX


1 Introduction

1.1 Background

Machine learning models are increasingly used in hydro-climate science to predict streamflow, estimate drought indices, map flood risk, and project evapotranspiration under changing climate scenarios. These models — random forests, gradient boosting machines, deep neural networks — often outperform traditional physically-based models in predictive accuracy (Breiman 2001). However, they operate as black boxes: their internal decision logic is opaque to practitioners, regulators, and policy makers who must trust model outputs for operational water management and climate adaptation decisions.

Explainable Artificial Intelligence (XAI) addresses this gap by providing mathematically rigorous tools to attribute model predictions to individual input features. XAI transforms a black-box model into an interpretable system without sacrificing predictive performance.

1.2 The xaiHydro package

xaiHydro provides a unified, hydrology-aware R workflow for applying three complementary XAI families to any trained hydro-climate model:

Method Scope Key advantage
SHAP (Lundberg and Lee 2017) Global + local Theoretically grounded; satisfies efficiency, symmetry, and dummy axioms
LIME (Ribeiro et al. 2016) Local Interpretable surrogate; works with any model type
PDP / ALE (Friedman 2001; Apley and Zhu 2020) Global marginal Visualises feature response curves across the predictor range

All three methods require only a trained model and its training data — no additional field data collection, no satellite downloads, no pre-processing pipelines. This makes xaiHydro uniquely accessible for researchers who already have a fitted model and want to explain it.

1.3 Scope of this vignette

This vignette serves as both:

  1. A package tutorial — covering every function in xaiHydro with reproducible code.
  2. The book chapter case study — the outputs produced here are the figures and analyses referenced in the accompanying Springer chapter.

2 Installation

# CRAN release (recommended)
install.packages("xaiHydro")

# Development version from GitHub
# install.packages("remotes")
remotes::install_github("sadikul-islam/xaiHydro")

Load the package:

library(xaiHydro)

3 Simulated datasets

xaiHydro ships with two built-in data simulators so every example in this vignette is fully reproducible without external data.

3.1 Daily streamflow dataset

df_flow <- sim_streamflow_data(n = 365, seed = 2026)
head(df_flow, 6)
precipitation temperature evapotranspiration soil_moisture snow_water_equiv ndvi lag1_precip lag3_precip streamflow
9.22 23.75 5.489 0.3396 7.17 0.4962 0.00 0.00 11.112
0.00 16.97 4.846 0.3073 3.83 0.5052 9.22 0.00 12.939
6.27 20.70 5.562 0.2638 2.07 0.4675 0.00 5.16 10.519
4.53 23.18 6.311 0.2270 11.25 0.5279 6.27 3.60 15.716
0.00 22.80 5.722 0.2828 5.94 0.4594 4.53 3.60 11.772
0.00 23.16 4.893 0.3021 4.76 0.5431 0.00 1.51 4.187
str(df_flow)
#> 'data.frame':    365 obs. of  9 variables:
#>  $ precipitation     : num  9.22 0 6.27 4.53 0 0 0 0 6.37 1.72 ...
#>  $ temperature       : num  23.8 17 20.7 23.2 22.8 ...
#>  $ evapotranspiration: num  5.49 4.85 5.56 6.31 5.72 ...
#>  $ soil_moisture     : num  0.34 0.307 0.264 0.227 0.283 ...
#>  $ snow_water_equiv  : num  7.17 3.83 2.07 11.25 5.94 ...
#>  $ ndvi              : num  0.496 0.505 0.468 0.528 0.459 ...
#>  $ lag1_precip       : num  0 9.22 0 6.27 4.53 0 0 0 0 6.37 ...
#>  $ lag3_precip       : num  0 0 5.16 3.6 3.6 1.51 0 0 2.12 2.7 ...
#>  $ streamflow        : num  11.1 12.9 10.5 15.7 11.8 ...

The function generates a synthetic daily hydro-meteorological dataset mimicking realistic seasonal dynamics:

Column Description Unit
precipitation Daily rainfall mm
temperature Mean daily air temperature °C
evapotranspiration Reference ET (Penman–Monteith style) mm/day
soil_moisture Volumetric soil moisture m³/m³
snow_water_equiv Snow water equivalent mm
ndvi NDVI vegetation greenness index dimensionless
lag1_precip Previous-day precipitation mm
lag3_precip 3-day moving mean precipitation mm
streamflow Response variable — simulated discharge m³/s

The response is generated as a non-linear function of predictors with added noise, including a precipitation × NDVI interaction term to capture vegetation-modulated runoff.

3.2 Monthly drought index dataset

df_drought <- sim_drought_data(n = 240, seed = 2026)
head(df_drought, 6)
precip_anom temp_anom pdsi ndvi_anom spi_3 spi_12 et_deficit spei
26.03 0.39 0.723 0.2974 -1.553 0 0.086 -1.027
-53.98 0.17 -0.551 -0.5852 -0.680 0 0.483 -0.529
6.96 1.79 -0.486 0.0418 -0.423 0 0.682 -0.329
-4.24 1.10 -0.897 -0.0679 0.054 0 0.372 -0.355
-33.33 -0.01 0.262 -0.2826 -0.502 0 0.367 -0.484
-125.80 -0.03 -1.664 -1.3441 0.132 0 0.358 -0.324

This dataset simulates 20 years of monthly hydro-climatic anomalies with SPEI (Standardised Precipitation-Evapotranspiration Index) as the response, driven by precipitation anomalies, temperature anomalies, SPI at 3- and 12-month scales, PDSI, NDVI anomaly, and ET deficit.


4 Model training

4.1 Random Forest — streamflow prediction

We train a Random Forest model on the simulated streamflow dataset. The same workflow applies to any regression model supported by predict().

library(randomForest)

# Separate predictors and response
y_flow <- df_flow$streamflow
X_flow <- df_flow[, setdiff(names(df_flow), "streamflow")]

set.seed(42)
rf_model <- randomForest(
  x         = X_flow,
  y         = y_flow,
  ntree     = 300,
  mtry      = 3,
  importance = TRUE
)

print(rf_model)
# Mean of squared residuals: ~X.XX
# % Var explained:            ~XX.X

4.2 XGBoost — drought index prediction

library(xgboost)

y_spei   <- df_drought$spei
X_drought <- df_drought[, setdiff(names(df_drought), "spei")]

xgb_mat   <- xgboost::xgb.DMatrix(as.matrix(X_drought), label = y_spei)
xgb_model <- xgboost::xgboost(
  data      = xgb_mat,
  nrounds   = 150,
  max_depth = 4,
  eta       = 0.1,
  objective = "reg:squarederror",
  verbose   = 0
)

5 The hydro explainer

5.1 Creating an explainer

hydro_explainer() is the single entry point for all XAI analyses. It wraps [DALEX::explain()] (Biecek and Burzykowski 2021) and attaches hydrology-specific metadata used for axis labels, titles, and interpretation messages throughout the package.

exp_flow <- hydro_explainer(
  model    = rf_model,
  data     = X_flow,
  y        = y_flow,
  variable = "streamflow",
  units    = "m3/s",
  label    = "RandomForest"
)

print(exp_flow)
# == xaiHydro Explainer ==
#   Target variable : streamflow [m3/s]
#   Model label     : RandomForest
#   Observations    : 365
#   Predictors      : 8
#   Model class     : randomForest

The explainer object is compatible with all xaiHydro functions and can also be passed directly to any DALEX function for additional analyses.

5.2 Explainer for XGBoost (custom predict wrapper)

For models that require a non-standard prediction call (XGBoost, Keras, PyTorch via torch), supply a predict_function:

pfun_xgb <- function(model, newdata) {
  predict(model, xgboost::xgb.DMatrix(as.matrix(newdata)))
}

exp_drought <- hydro_explainer(
  model            = xgb_model,
  data             = X_drought,
  y                = y_spei,
  variable         = "drought_index",
  units            = "SPEI",
  label            = "XGBoost",
  predict_function = pfun_xgb
)

6 SHAP Analysis

SHAP (SHapley Additive exPlanations) values decompose each model prediction into additive contributions from each feature, satisfying three desirable axioms: efficiency (contributions sum to the prediction gap from baseline), symmetry (equal features receive equal credit), and dummy (non-contributing features get zero) (Lundberg and Lee 2017).

6.1 Computing SHAP values

shap_vals <- hydro_shap(
  explainer = exp_flow,
  nsim      = 50,     # Monte Carlo replicates — increase for stability
  seed      = 42
)

# Returns a tidy data frame:
# id | feature | shap_value | feature_value | mean_abs_shap
head(shap_vals, 8)

Tip: nsim = 50 is adequate for publication figures. Use nsim = 100–200 for final manuscript outputs.

6.2 Global SHAP summary plot

plot_shap_summary(shap_vals, top_n = 8)

The beeswarm summary plot shows:

Features where red points cluster on the right indicate that high values of that predictor increase predicted streamflow — physically consistent with rainfall–runoff theory.

6.3 Single-observation waterfall

# Explain a high-flow day (e.g., observation 180 after a storm event)
plot_shap_waterfall(shap_vals, obs_id = 180, top_n = 7)

The waterfall plot shows exactly how prediction for day 180 was built up from the global baseline (mean predicted streamflow) through additive feature contributions. Red bars push the prediction above baseline; blue bars pull it below.


7 LIME Explanation

LIME (Local Interpretable Model-agnostic Explanations) fits a locally weighted linear surrogate model in the neighbourhood of a specific observation (Ribeiro et al. 2016). It provides a simple, interpretable approximation of the black-box model’s local behaviour.

lime_result <- hydro_lime(
  explainer    = exp_flow,
  new_obs      = X_flow[180, , drop = FALSE],
  n_features   = 6,      # top-6 features in the local model
  kernel_width = 0.75
)

# Local R² indicates surrogate model quality (closer to 1 = better)
plot_lime_hydro(lime_result)

The local R² is printed in the subtitle. Values above 0.7 indicate the surrogate captures the model’s local structure well.


8 Partial Dependence Profiles

Partial Dependence Plots (PDP) show the marginal effect of a single predictor on the response, averaging over all other predictors (Friedman 2001). Accumulated Local Effects (ALE) are preferred when predictors are correlated, as they avoid averaging over unrealistic covariate combinations (Apley and Zhu 2020).

8.1 PDP — marginal effects

pdp_result <- hydro_pdp(
  explainer   = exp_flow,
  variable    = c("precipitation", "soil_moisture",
                   "lag1_precip",  "temperature"),
  type        = "partial",
  grid_points = 60
)

plot_pdp_hydro(pdp_result, ncol = 2)

8.2 ALE — preferred for correlated predictors

ale_result <- hydro_pdp(
  explainer = exp_flow,
  variable  = c("precipitation", "soil_moisture"),
  type      = "accumulated"
)

plot_pdp_hydro(ale_result, ncol = 2)

In hydrology, precipitation and soil moisture are typically positively correlated (wet soils follow wet periods). ALE correctly handles this by computing effects within observed data slices rather than averaging over impossible combinations.


9 Variable Importance

Permutation importance measures how much model error increases when each feature’s values are randomly shuffled, breaking the feature– response relationship (Breiman 2001). Features causing large error increases are the most important.

imp <- hydro_importance(
  explainer     = exp_flow,
  loss_function = "rmse",
  B             = 20       # repetitions — reduces variance of estimate
)

plot_importance_hydro(imp, top_n = 8)

10 Prediction Breakdown

The breakdown profile decomposes a single prediction into ordered additive contributions from each feature (Biecek and Burzykowski 2021). Unlike SHAP (which averages over all orderings), the sequential Break Down algorithm shows contributions in a specific order; the SHAP-type breakdown averages over B random orderings for a more stable result.

bd <- hydro_breakdown(
  explainer = exp_flow,
  new_obs   = X_flow[180, , drop = FALSE],
  type      = "shap",   # or "break_down"
  B         = 25
)

plot_breakdown_hydro(bd, max_features = 8)

11 Residual Diagnostics

Before trusting XAI explanations, model residuals should be examined. Systematic patterns in residuals (heteroscedasticity, temporal autocorrelation) can bias SHAP and PDP estimates.

hydro_residuals(exp_flow)

The four-panel figure checks:

  1. Residual histogram — departure from normality (skewness, heavy tails).
  2. Residuals vs predicted — heteroscedasticity; systematic bias at high flows.
  3. Observed vs predicted — overall fit; 1:1 line is perfect prediction.
  4. Residuals over index — temporal autocorrelation in daily data (important for hydrological models with lag effects).

12 Full automated XAI report

hydro_xai_report() runs all key analyses in one call and assembles them into a publication-ready multi-panel figure. This is the figure used in the book chapter case study.

hydro_xai_report(
  explainer = exp_flow,
  nsim      = 50,
  top_n     = 6,
  new_obs   = X_flow[180, , drop = FALSE],
  seed      = 42,
  save_path = "xaiHydro_streamflow_report.png",
  width     = 16,
  height    = 11,
  dpi       = 300
)

13 Complete drought index case study

This section demonstrates the full xaiHydro workflow on an XGBoost drought model, reproducing the second case study in the book chapter.

library(xgboost)

# Data
df_d  <- sim_drought_data(n = 240, seed = 2026)
y_d   <- df_d$spei
X_d   <- df_d[, setdiff(names(df_d), "spei")]

# Model
xgb_m <- xgboost::xgboost(
  data      = xgboost::xgb.DMatrix(as.matrix(X_d), label = y_d),
  nrounds   = 150, max_depth = 4, eta = 0.1,
  objective = "reg:squarederror", verbose = 0
)

# Explainer
pfun  <- function(m, nd) predict(m, xgboost::xgb.DMatrix(as.matrix(nd)))
exp_d <- hydro_explainer(xgb_m, X_d, y_d,
                          variable         = "drought_index",
                          units            = "SPEI",
                          label            = "XGBoost",
                          predict_function = pfun)

# XAI suite
shap_d <- hydro_shap(exp_d, nsim = 50)
imp_d  <- hydro_importance(exp_d, B = 20)
pdp_d  <- hydro_pdp(exp_d, variable = c("spi_3", "spi_12", "et_deficit"))

plot_shap_summary(shap_d, top_n = 6,
                  title = "SHAP Summary — SPEI drought model (XGBoost)")
plot_importance_hydro(imp_d)
plot_pdp_hydro(pdp_d, ncol = 3)
hydro_residuals(exp_d)

14 Interpreting XAI results in a hydro-climate context

14.1 Physical interpretation of SHAP

When precipitation and lag1_precip appear in the top-ranked SHAP features with positive effects (red points on the right), this confirms the model has correctly learnt that higher antecedent and current rainfall drives higher streamflow — consistent with rainfall–runoff theory. Conversely, high evapotranspiration having negative SHAP (blue points on left) indicates that high ET demand reduces net water available for runoff.

14.2 ALE vs PDP selection guide

Use ALE when: - Predictors are correlated (precipitation–soil moisture, temperature–ET). - The training data does not uniformly cover the predictor space. - You suspect extrapolation artefacts in PDP.

Use PDP when: - Predictors are approximately independent. - You want a simpler figure for a non-technical audience.

14.3 LIME local R² threshold

An R² < 0.5 for the LIME surrogate means the local linear approximation is unreliable for that observation. Increase kernel_width (to use a broader neighbourhood) or reduce n_features (for a sparser model).


15 Session information

sessionInfo()
#> R version 4.6.0 (2026-04-24 ucrt)
#> Platform: x86_64-w64-mingw32/x64
#> Running under: Windows 11 x64 (build 26200)
#> 
#> Matrix products: default
#>   LAPACK version 3.12.1
#> 
#> locale:
#> [1] LC_COLLATE=C                   LC_CTYPE=English_India.utf8   
#> [3] LC_MONETARY=English_India.utf8 LC_NUMERIC=C                  
#> [5] LC_TIME=English_India.utf8    
#> 
#> time zone: Asia/Calcutta
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] xaiHydro_0.1.0
#> 
#> loaded via a namespace (and not attached):
#>  [1] digest_0.6.39     R6_2.6.1          fastmap_1.2.0     xfun_0.60        
#>  [5] cachem_1.1.0      knitr_1.52        htmltools_0.5.9   rmarkdown_2.32   
#>  [9] lifecycle_1.0.5   cli_3.6.6         sass_0.4.10       jquerylib_0.1.4  
#> [13] compiler_4.6.0    rstudioapi_0.19.0 tools_4.6.0       evaluate_1.0.5   
#> [17] bslib_0.12.0      yaml_2.3.12       otel_0.2.0        jsonlite_2.0.0   
#> [21] rlang_1.3.0

References

Apley, Daniel W, and Jingyu Zhu. 2020. “Visualizing the Effects of Predictor Variables in Black Box Supervised Learning Models.” Journal of the Royal Statistical Society: Series B 82 (4): 1059–86. https://doi.org/10.1111/rssb.12377.
Biecek, Przemyslaw, and Tomasz Burzykowski. 2021. Explanatory Model Analysis. Chapman; Hall/CRC. https://ema.drwhy.ai/.
Breiman, Leo. 2001. “Random Forests.” Machine Learning 45 (1): 5–32. https://doi.org/10.1023/A:1010933404324.
Friedman, Jerome H. 2001. “Greedy Function Approximation: A Gradient Boosting Machine.” Annals of Statistics, 1189–232. https://doi.org/10.1214/aos/1013203451.
Lundberg, Scott M, and Su-In Lee. 2017. “A Unified Approach to Interpreting Model Predictions.” Advances in Neural Information Processing Systems 30.
Ribeiro, Marco Tulio, Sameer Singh, and Carlos Guestrin. 2016. “"Why Should I Trust You?": Explaining the Predictions of Any Classifier.” Proceedings of the 22nd ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, 1135–44. https://doi.org/10.1145/2939672.2939778.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.