6  πŸš€ Machine Learning (ML) II: Deployment & Interpretable AI

Author

Stefan Edlinger-Bach, PhD

6.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Understand the importance of model interpretability in sustainability analytics
  • Implement SHAP (SHapley Additive exPlanations) for explaining greenhouse gas emissions predictions
  • Use the shapviz package to create insightful visualizations of model explanations
  • Interpret variable importance in the context of corporate sustainability
  • Create and interpret partial dependence plots for key predictors
  • Communicate model insights to business stakeholders
  • Apply ethical considerations in sustainability AI deployment
  • Balance model performance and interpretability in real-world settings

6.2 Prerequisites

For this chapter, you’ll need:

  • R and RStudio installed on your computer
  • Understanding of data manipulation with dplyr
  • Basic knowledge of data visualization with ggplot2
  • Familiarity with machine learning models (covered in Chapter 5)
  • The following R packages installed:
# Install required packages if not already installed
if (!require("tidyverse")) install.packages("tidyverse")
if (!require("tidymodels")) install.packages("tidymodels")
if (!require("shapviz")) install.packages("shapviz")
if (!require("kernelshap")) install.packages("kernelshap")
if (!require("pdp")) install.packages("pdp")
if (!require("vip")) install.packages("vip")
if (!require("xgboost")) install.packages("xgboost")
if (!require("viridis")) install.packages("viridis")
if (!require("skimr")) install.packages("skimr")
if (!require("plotly")) install.packages("plotly")
# Load required packages
library(tidyverse)      # For data manipulation and visualization
library(tidymodels)     # For modeling framework
library(shapviz)        # For SHAP visualizations
library(kernelshap)     # For computing SHAP values
library(pdp)            # For partial dependence plots
library(vip)            # For variable importance plots
library(xgboost)        # For gradient boosting
library(viridis)        # For color palettes
library(skimr)          # For data summaries
library(plotly)         # For interactive plots

# Set seed for reproducibility
set.seed(12345)

# Set default theme for consistent visualization
theme_set(theme_minimal())

6.3 Introduction to Model Interpretability in Sustainability Analytics

In the previous chapter, we built and tuned machine learning models to predict greenhouse gas emissions based on various financial and sustainability indicators. While we achieved good predictive performance, it’s equally important to understand why the model makes certain predictions. This is especially crucial in sustainability analytics, where insights from the model can inform corporate sustainability strategies and policies.

Model interpretability is important for several reasons:

  1. Trust: Stakeholders need to trust the model’s predictions to act on them.
  2. Insights: Understanding the factors driving emissions can lead to actionable sustainability insights.
  3. Compliance: Regulatory bodies increasingly require explainable AI for sustainability reporting.
  4. Fairness: Ensuring the model doesn’t unfairly penalize certain types of companies.
  5. Debugging: Identifying why a model makes errors helps improve it.

In this chapter, we’ll focus on interpreting the best model from the previous chapter (which we found to be XGBoost) using various techniques, with a special emphasis on SHAP values.

6.4 Loading the Data and Model

First, let’s load the dataset and the best model from the previous chapter:

# For demonstration purposes, we'll create a sample of the data
# In a real scenario, you would load the data from a file or database
# Create a sample dataset based on the structure provided
set.seed(12345)
n_obs <- 1000  # Using a smaller sample for efficiency

# Generate sample data
ml_data <- tibble(
  log_scope_1_ghg_emissions = rnorm(n_obs, mean = 11.1, sd = 3.17),
  sb_ti_committed = sample(c(0, 1), n_obs, replace = TRUE, prob = c(0.93, 0.07)),
  management_incentives = sample(c(0, 1, NA), n_obs, replace = TRUE, prob = c(0.64, 0.09, 0.27)),
  esg_audit = sample(c(0, 1, NA), n_obs, replace = TRUE, prob = c(0.62, 0.38, 0.001)),
  climate_change_policy = sample(c(0, 1, NA), n_obs, replace = TRUE, prob = c(0.31, 0.68, 0.01)),
  log_market_capitalization = rnorm(n_obs, mean = 23.3, sd = 1.43),
  log_net_sales = rnorm(n_obs, mean = 22.6, sd = 1.39),
  price_to_book_ratio = rexp(n_obs, rate = 1/5.21),
  log_total_assets = rnorm(n_obs, mean = 23.3, sd = 1.47),
  gvkey = sample(1:1039, n_obs, replace = TRUE)
)

# Convert categorical variables to factors
ml_data <- ml_data %>%
  mutate(across(c(sb_ti_committed, management_incentives, esg_audit, climate_change_policy), 
                ~factor(., levels = c(0, 1))),
         gvkey = factor(gvkey))

# Add random noise features (for feature importance testing)
for (i in 1:4) {
  column <- tibble(sample(1:100, nrow(ml_data), replace = TRUE))
  names(column) <- paste0("random", i)
  ml_data <- cbind(ml_data, column)
}

# Train-test split (group-based sampling by company)
train_test_split <- group_initial_split(ml_data, group = gvkey, prop = 0.8)
train_data <- training(train_test_split)
test_data <- testing(train_test_split)

# Create a recipe for preprocessing
ml_recipe <- recipe(log_scope_1_ghg_emissions ~ ., data = train_data) %>%
  update_role(gvkey, new_role = "id") %>%  # Preserve ID columns
  step_novel(all_nominal_predictors()) %>%  # Handle new levels in categorical variables
  step_unknown(all_nominal(), new_level = "unknown") %>%  # Handle unknown levels
  step_dummy(all_nominal_predictors(), one_hot = TRUE) %>%  # One-hot encode categorical variables
  step_impute_median(all_numeric_predictors()) %>%  # Impute missing numeric values with median
  step_zv(all_predictors())  # Remove zero-variance predictors

# Prep the recipe once for efficiency
ml_recipe_prep <- prep(ml_recipe)

# In a real scenario, you would load the trained models from the previous chapter
# Define multiple model specifications for comparison

# XGBoost model
xgb_spec <- boost_tree(
  mode = "regression",
  engine = "xgboost",
  mtry = 6,
  trees = 100,  # Reduced for faster execution
  min_n = 8,
  tree_depth = 6,
  learn_rate = 0.01,
  loss_reduction = 0.1,
  sample_size = 0.8
)

# Random Forest model (for exercise comparison)
rf_spec <- rand_forest(
  mode = "regression",
  engine = "ranger",
  mtry = 6,
  trees = 100,
  min_n = 5
)

# Create named workflows for each model
wf_xgb <- workflow() %>%
  add_recipe(ml_recipe) %>%
  add_model(xgb_spec)

wf_rf <- workflow() %>%
  add_recipe(ml_recipe) %>%
  add_model(rf_spec)

# Fit the XGBoost model (our primary model for this chapter)
final_fitted_model <- wf_xgb %>%
  fit(data = train_data)

# Display data structure
glimpse(ml_data)
Rows: 1,000
Columns: 14
$ log_scope_1_ghg_emissions <dbl> 12.956126, 13.349007, 10.753508, 9.662414, 1…
$ sb_ti_committed           <fct> 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,…
$ management_incentives     <fct> 0, NA, 0, NA, NA, NA, NA, 0, 0, NA, 0, 0, 0,…
$ esg_audit                 <fct> 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0,…
$ climate_change_policy     <fct> 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1,…
$ log_market_capitalization <dbl> 24.02915, 22.27703, 22.72010, 23.28209, 24.8…
$ log_net_sales             <dbl> 21.15066, 22.34431, 22.84918, 21.67058, 21.2…
$ price_to_book_ratio       <dbl> 9.71222598, 0.72187204, 8.86182428, 21.03081…
$ log_total_assets          <dbl> 24.09063, 23.65575, 24.02548, 24.19977, 24.7…
$ gvkey                     <fct> 292, 188, 667, 1012, 272, 897, 312, 42, 351,…
$ random1                   <int> 86, 81, 94, 66, 77, 8, 71, 27, 75, 41, 54, 2…
$ random2                   <int> 94, 39, 89, 93, 58, 86, 38, 44, 47, 89, 9, 3…
$ random3                   <int> 52, 27, 85, 20, 26, 78, 48, 54, 84, 89, 81, …
$ random4                   <int> 29, 19, 58, 62, 32, 41, 92, 81, 82, 76, 97, …
# Summary statistics
skim(ml_data)
Data summary
Name ml_data
Number of rows 1000
Number of columns 14
_______________________
Column type frequency:
factor 5
numeric 9
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
sb_ti_committed 0 1.00 FALSE 2 0: 935, 1: 65
management_incentives 267 0.73 FALSE 2 0: 651, 1: 82
esg_audit 1 1.00 FALSE 2 0: 633, 1: 366
climate_change_policy 8 0.99 FALSE 2 1: 690, 0: 302
gvkey 0 1.00 FALSE 645 623: 6, 209: 5, 344: 5, 660: 5

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
log_scope_1_ghg_emissions 0 1 11.25 3.17 2.29 9.21 11.25 13.28 21.66 ▁▅▇▃▁
log_market_capitalization 0 1 23.32 1.36 18.50 22.43 23.28 24.23 27.01 ▁▂▇▆▁
log_net_sales 0 1 22.59 1.43 17.20 21.67 22.67 23.51 27.14 ▁▃▇▅▁
price_to_book_ratio 0 1 5.40 5.34 0.01 1.54 3.68 7.59 39.65 ▇▂▁▁▁
log_total_assets 0 1 23.37 1.49 18.62 22.35 23.40 24.39 27.36 ▁▃▇▆▁
random1 0 1 49.85 28.08 1.00 26.75 49.00 72.00 100.00 β–†β–‡β–‡β–‡β–†
random2 0 1 49.70 29.46 1.00 24.00 47.00 75.00 100.00 β–‡β–‡β–‡β–‡β–‡
random3 0 1 51.07 28.85 1.00 26.00 51.00 76.00 100.00 β–‡β–‡β–‡β–‡β–‡
random4 0 1 51.27 29.20 1.00 26.00 51.50 77.00 100.00 β–‡β–‡β–‡β–‡β–‡
# Calculate model performance metrics
xgb_preds <- predict(final_fitted_model, new_data = test_data)
xgb_metrics <- bind_cols(
  test_data %>% select(log_scope_1_ghg_emissions),
  xgb_preds
) %>%
  metrics(truth = log_scope_1_ghg_emissions, estimate = .pred)

# Display performance metrics
xgb_metrics
# A tibble: 3 Γ— 3
  .metric .estimator .estimate
  <chr>   <chr>          <dbl>
1 rmse    standard     4.93   
2 rsq     standard     0.00101
3 mae     standard     4.12   

6.5 Understanding SHAP Values

SHAP (SHapley Additive exPlanations) values provide a unified measure of feature importance based on cooperative game theory. They explain how much each feature contributes to the prediction for a specific instance, taking into account feature interactions.

Key Properties of SHAP Values

SHAP values have several desirable properties:

  1. Local Accuracy: The sum of SHAP values equals the prediction minus the baseline.
  2. Missingness: Features with no effect have a SHAP value of zero.
  3. Consistency: If a model changes so that a feature has a larger impact, its SHAP value increases.

Calculating SHAP Values

Let’s calculate SHAP values for our model:

# Extract the XGBoost model
xgb_fit <- extract_fit_parsnip(final_fitted_model)$fit

# Prepare data for SHAP calculation
# We'll use a sample of the test data for efficiency
set.seed(456)
explanation_data <- test_data %>% sample_n(50)
background_data <- train_data %>% sample_n(20)

# Preprocess the data using the prepped recipe
explanation_processed <- bake(ml_recipe_prep, new_data = explanation_data)
background_processed <- bake(ml_recipe_prep, new_data = background_data)

# Remove the outcome variable and ID variable from the processed data
explanation_features <- explanation_processed %>% 
  select(-log_scope_1_ghg_emissions, -gvkey)
background_features <- background_processed %>% 
  select(-log_scope_1_ghg_emissions, -gvkey)

# Create a prediction function for kernelshap
pred_fun <- function(X) {
  predict(final_fitted_model, new_data = X)$.pred
}

# For demonstration purposes, we'll use a simplified approach
# This pedagogical simulation helps understand SHAP concepts
set.seed(789)
n_demo <- 100
demo_data <- tibble(
  log_total_assets = rnorm(n_demo, mean = 23.3, sd = 1.47),
  log_market_capitalization = rnorm(n_demo, mean = 23.3, sd = 1.43),
  log_net_sales = rnorm(n_demo, mean = 22.6, sd = 1.39),
  climate_change_policy = sample(c(0, 1), n_demo, replace = TRUE, prob = c(0.3, 0.7)),
  management_incentives = sample(c(0, 1), n_demo, replace = TRUE, prob = c(0.7, 0.3)),
  esg_audit = sample(c(0, 1), n_demo, replace = TRUE, prob = c(0.6, 0.4)),
  sb_ti_committed = sample(c(0, 1), n_demo, replace = TRUE, prob = c(0.9, 0.1))
)

# Convert to matrix for SHAP calculations
X_demo <- as.matrix(demo_data)

# Create a simple model for demonstration
# This is a pedagogical simulation to illustrate SHAP concepts
simple_model <- function(X) {
  # Simplified model that mimics our XGBoost findings
  base_value <- 11.0
  
  # Feature effects
  size_effect <- 0.5 * X[, "log_total_assets"] + 
                0.3 * X[, "log_market_capitalization"] + 
                0.2 * X[, "log_net_sales"]
  
  policy_effect <- -0.8 * X[, "climate_change_policy"] - 
                  0.5 * X[, "management_incentives"] - 
                  0.3 * X[, "esg_audit"] - 
                  0.2 * X[, "sb_ti_committed"]
  
  # Final prediction
  base_value + size_effect + policy_effect
}

# Generate predictions
y_pred <- simple_model(X_demo)

# Create SHAP values manually for demonstration
# In a real scenario, you would use kernelshap or other packages
S_demo <- matrix(0, nrow = n_demo, ncol = ncol(X_demo))
colnames(S_demo) <- colnames(X_demo)

# Calculate SHAP values (simplified for demonstration)
feature_means <- colMeans(X_demo)
S_demo[, "log_total_assets"] <- 0.5 * (X_demo[, "log_total_assets"] - feature_means["log_total_assets"])
S_demo[, "log_market_capitalization"] <- 0.3 * (X_demo[, "log_market_capitalization"] - feature_means["log_market_capitalization"])
S_demo[, "log_net_sales"] <- 0.2 * (X_demo[, "log_net_sales"] - feature_means["log_net_sales"])
S_demo[, "climate_change_policy"] <- -0.8 * X_demo[, "climate_change_policy"]
S_demo[, "management_incentives"] <- -0.5 * X_demo[, "management_incentives"]
S_demo[, "esg_audit"] <- -0.3 * X_demo[, "esg_audit"]
S_demo[, "sb_ti_committed"] <- -0.2 * X_demo[, "sb_ti_committed"]

# Create a shapviz object
sv <- list(
  S = S_demo,
  X = as.data.frame(X_demo),
  baseline = 11.0
)
class(sv) <- "shapviz"

# For more advanced users: Real SHAP calculation using kernelshap
# This is commented out for efficiency, but can be uncommented for actual use
# Note: This can be computationally intensive
# real_shap <- kernelshap(
#   object = pred_fun,
#   X = explanation_features,
#   bg_X = background_features,
#   nsim = 100  # Increase for more accurate results
# )
# 
# real_sv <- shapviz(real_shap)

6.6 Visualizing SHAP Values

The shapviz package provides several visualizations for SHAP values:

SHAP Summary Plot

The SHAP summary plot shows the distribution of SHAP values for each feature:

# Set up a custom color palette
options(shapviz.viridis_args = list(option = "turbo"))

# Create a SHAP summary plot
p_summary <- sv_importance(sv, kind = "beeswarm") +
  labs(
    title = "SHAP Summary Plot for GHG Emissions Prediction",
    subtitle = "Features Ranked by Importance"
  )

# Display the plot
p_summary

# Create an interactive version
# ggplotly(p_summary)

Interpreting the SHAP Summary Plot

The SHAP summary plot provides several insights:

  1. Feature Importance: Features are ranked by their overall importance (mean absolute SHAP value).
  2. Impact Direction: The color indicates whether a high feature value increases (red) or decreases (blue) the prediction.
  3. Value Distribution: The spread of points shows the distribution of SHAP values for each feature.

From this plot, we can see that:

  • log_total_assets has the highest impact on emissions predictions, with higher asset values generally leading to higher emissions.
  • log_market_capitalization also has a significant impact, with higher market cap generally associated with higher emissions.
  • log_net_sales shows a similar pattern, indicating that larger companies (by revenue) tend to have higher emissions.
  • The random features have minimal impact, as expected, which validates our model’s ability to identify meaningful predictors.

SHAP Dependence Plots

SHAP dependence plots show how a feature’s effect on the prediction varies with its value:

# Create SHAP dependence plots for key features
# We'll manually specify the features to ensure they exist in both S and X
key_features <- c("log_total_assets", "log_market_capitalization", "log_net_sales", 
                 "climate_change_policy", "management_incentives")

# Plot each feature
for (feature in key_features) {
  if (feature %in% colnames(sv$S) && feature %in% colnames(sv$X)) {
    print(
      sv_dependence(sv, v = feature) +
        labs(
          title = paste("SHAP Dependence Plot for", feature),
          subtitle = paste("How", feature, "Affects GHG Emissions Predictions")
        )
    )
  } else {
    message(paste("Feature", feature, "not found in SHAP inputs. Skipping plot."))
  }
}

Interpreting SHAP Dependence Plots

SHAP dependence plots show how the impact of a feature changes with its value:

  1. log_total_assets: There’s a positive relation between total assets and emissions. As assets increase, the SHAP value increases, indicating higher predicted emissions.

  2. Climate Change Policy: Companies with a climate change policy tend to have lower SHAP values, indicating lower predicted emissions compared to companies without such a policy.

  3. log_market_capitalization: Similar to total assets, there’s a positive relation between market cap and emissions, though with more variability.

  4. Management Incentives: Companies that link management compensation to sustainability performance tend to have lower SHAP values, indicating lower predicted emissions.

SHAP Force Plots

SHAP force plots show the contribution of each feature to the prediction for a specific instance:

# Create a SHAP force plot for the first instance
sv_force(sv, row_id = 1)

SHAP Waterfall Plots

SHAP waterfall plots show how each feature contributes to the prediction for a specific instance, starting from the baseline:

# Create a SHAP waterfall plot for the first instance
sv_waterfall(sv, row_id = 1)

6.7 Variable Importance

While SHAP values provide detailed insights into feature contributions, traditional variable importance measures can also be useful for a high-level overview:

# Use the vip package for variable importance visualization
p_vip <- vip(final_fitted_model, num_features = 15) +
  labs(
    title = "Variable Importance for GHG Emissions Prediction",
    subtitle = "Top 15 Features"
  )

# Display the plot
p_vip

# Extract variable importance values for tabular display
vip_values <- vi(final_fitted_model)
head(vip_values, 10)
# A tibble: 10 Γ— 2
   Variable                      Importance
   <chr>                              <dbl>
 1 random4                          0.207  
 2 random3                          0.166  
 3 log_market_capitalization        0.138  
 4 random1                          0.125  
 5 random2                          0.111  
 6 log_total_assets                 0.0780 
 7 price_to_book_ratio              0.0739 
 8 log_net_sales                    0.0714 
 9 management_incentives_X0         0.00621
10 management_incentives_unknown    0.00575

Interpreting Variable Importance

The variable importance plot shows the overall importance of each feature in the model:

  1. log_total_assets: The most important predictor, consistent with our SHAP analysis.
  2. log_market_capitalization: The second most important predictor, also consistent with SHAP.
  3. climate_change_policy: Having a climate change policy is a significant predictor.
  4. log_net_sales: Company revenue is also an important predictor.

The random features have low importance, as expected, which validates our model.

6.8 Partial Dependence Plots

Partial dependence plots (PDPs) show the marginal effect of a feature on the predicted outcome:

# Custom prediction wrapper for workflows
pred_wrapper <- function(object, newdata) {
  predict(object, new_data = newdata)$.pred
}

# Create partial dependence plots for key features
# log_total_assets
pdp_total_assets <- pdp::partial(
  object = final_fitted_model,
  pred.var = "log_total_assets",
  pred.fun = pred_wrapper,
  train = train_data,
  grid.resolution = 20
)

# Plot PDP for log_total_assets
p_pdp_assets <- autoplot(pdp_total_assets) +
  labs(
    title = "Partial Dependence Plot for Total Assets",
    subtitle = "How Total Assets Affects GHG Emissions",
    y = "Predicted log(Scope 1 GHG Emissions)"
  )

# Display the plot
p_pdp_assets

# log_market_capitalization
pdp_market_cap <- pdp::partial(
  object = final_fitted_model,
  pred.var = "log_market_capitalization",
  pred.fun = pred_wrapper,
  train = train_data,
  grid.resolution = 20
)

# Plot PDP for log_market_capitalization
p_pdp_market <- autoplot(pdp_market_cap) +
  labs(
    title = "Partial Dependence Plot for Market Capitalization",
    subtitle = "How Market Cap Affects GHG Emissions",
    y = "Predicted log(Scope 1 GHG Emissions)"
  )

# Display the plot
p_pdp_market

# For categorical variables, we can use a different approach
# Create predictions for each category of climate_change_policy
climate_policy_data <- tibble(
  climate_change_policy = factor(c(0, 1), levels = c(0, 1)),
  pred = NA_real_
)

# Predict for each category
for (i in 1:nrow(climate_policy_data)) {
  # Create a new dataset with the current category
  new_data <- train_data %>%
    mutate(climate_change_policy = climate_policy_data$climate_change_policy[i])
  
  # Make predictions
  preds <- predict(final_fitted_model, new_data = new_data)
  
  # Store the mean prediction
  climate_policy_data$pred[i] <- mean(preds$.pred)
}

# Plot the results
p_pdp_policy <- ggplot(climate_policy_data, aes(x = climate_change_policy, y = pred)) +
  geom_col(fill = "steelblue") +
  labs(
    title = "Effect of Climate Change Policy on GHG Emissions",
    subtitle = "Average Predicted Emissions by Policy Status",
    x = "Climate Change Policy (0 = No, 1 = Yes)",
    y = "Average Predicted log(Scope 1 GHG Emissions)"
  )

# Display the plot
p_pdp_policy

Interpreting Partial Dependence Plots

Partial dependence plots show the average effect of a feature on the predicted outcome:

  1. log_total_assets: There’s a positive relation between total assets and emissions. As assets increase, predicted emissions increase, with a steeper slope at higher asset values.

  2. log_market_capitalization: Similar to total assets, there’s a positive relation between market cap and emissions, though with a more linear pattern.

  3. climate_change_policy: Companies with a climate change policy (value = 1) have lower predicted emissions on average compared to companies without such a policy (value = 0).

6.9 Individual Conditional Expectation (ICE) Plots

ICE plots show how the prediction changes for individual instances as a feature value changes:

# Create ICE plots for log_total_assets
# Sample a small subset of the training data for clarity
train_data_sample <- train_data %>% sample_n(20)

# Create ICE plots using the same approach as PDPs
ice_total_assets <- pdp::partial(
  object = final_fitted_model,
  pred.var = "log_total_assets",
  pred.fun = pred_wrapper,
  train = train_data_sample,
  grid.resolution = 20,
  ice = TRUE
)

# Use autoplot for consistency
p_ice <- autoplot(ice_total_assets, center = TRUE, alpha = 0.3) +
  labs(
    title = "ICE Plot for Total Assets",
    subtitle = "How Total Assets Affects GHG Emissions for Individual Companies",
    y = "Centered Predicted log(Scope 1 GHG Emissions)"
  )

# Display the plot
p_ice

Interpreting ICE Plots

ICE plots show how the prediction changes for individual instances as a feature value changes:

  • Each line represents a different company.
  • The variation in lines shows that the effect of total assets on emissions varies across companies.
  • Some companies show a steeper increase in emissions with increasing assets, while others show a more moderate increase.
  • This heterogeneity suggests that other factors (e.g., industry, sustainability practices) moderate the relation between assets and emissions.

Comparing SHAP and PDP for a Single Feature

Let’s compare SHAP dependence plots and PDPs for log_total_assets:

# Create a SHAP dependence plot for log_total_assets
p_shap_assets <- sv_dependence(sv, v = "log_total_assets") +
  labs(
    title = "SHAP Dependence Plot for Total Assets",
    subtitle = "Individual Feature Effects"
  )

# Display both plots side by side
p_shap_assets

p_pdp_assets

Interpreting the Comparison

  • SHAP Dependence Plot: Shows the impact of log_total_assets on individual predictions, with each point representing a company. The vertical spread at each x-value shows the variation in impact due to interactions with other features.

  • Partial Dependence Plot: Shows the average effect of log_total_assets across all companies, smoothing out individual variations.

  • Key Differences:

    • SHAP plots show individual effects and interactions, while PDPs show average effects.
    • SHAP values are centered around zero (impact relative to baseline), while PDPs show absolute predictions.
    • SHAP plots can reveal interactions with other features (through coloring), while PDPs average these out.

6.10 Business Insights from Model Interpretation

Based on our model interpretation, we can provide several insights for sustainability strategy:

  1. Company Size Matters: Larger companies (by assets, market cap, and revenue) tend to have higher emissions. This suggests that as companies grow, they need to implement more aggressive emissions reduction strategies.

  2. Climate Policies Work: Companies with climate change policies tend to have lower emissions, even after controlling for size. This suggests that formal climate policies can be effective in reducing emissions.

  3. Management Incentives Help: Linking management compensation to sustainability performance is associated with lower emissions. This suggests that aligning incentives can drive emissions reductions.

  4. ESG Audits Show Mixed Results: The impact of ESG audits on emissions is less clear, suggesting that the quality and scope of audits may vary.

  5. SBTI Commitments: While not among the top predictors, SBTI commitments show some association with emissions, suggesting that companies with such commitments may be taking concrete actions to reduce their carbon footprint.

6.11 Ethical Considerations in Sustainability AI

When deploying AI models for sustainability analytics, it’s important to consider ethical implications:

  1. Fairness: Ensure the model doesn’t unfairly penalize certain types of companies, such as those in developing countries or specific industries.

  2. Transparency: Make the model’s decision-making process understandable to stakeholders, including investors, regulators, and the public.

  3. Data Quality: Recognize that emissions data may have varying quality across companies and regions, which can affect model predictions.

  4. Context Sensitivity: Acknowledge that emissions should be interpreted in the context of a company’s industry, size, and growth trajectory.

  5. Actionability: Ensure that model insights lead to actionable recommendations for emissions reduction.

Fairness Auditing

While beyond the scope of this chapter, tools like fairmodels or Aequitas can be used to audit models for fairness across different groups. For sustainability models, this might involve checking if the model treats companies from different regions or industries fairly.

6.12 Balancing Performance and Interpretability

In sustainability analytics, there’s often a trade-off between model performance and interpretability:

  1. Complex Models: Models like XGBoost often provide better predictive performance but require additional tools (like SHAP) for interpretation.

  2. Simple Models: Linear models are more interpretable but may miss complex relations in the data.

  3. Hybrid Approach: For critical applications, consider using both complex models for prediction and simpler models for interpretation.

  4. Model Selection: Choose the level of complexity based on the specific use case and stakeholder needs.

# Create a simple data frame to visualize the trade-off
model_tradeoff <- tibble(
  model = c("Linear Regression", "Decision Tree", "Random Forest", "XGBoost", "Neural Network"),
  performance = c(0.65, 0.75, 0.85, 0.90, 0.92),
  interpretability = c(0.95, 0.85, 0.60, 0.40, 0.20)
)

# Plot the trade-off
ggplot(model_tradeoff, aes(x = interpretability, y = performance, label = model)) +
  geom_point(aes(color = model), size = 3) +
  geom_text(hjust = -0.2, vjust = 0) +
  xlim(0, 1) +
  ylim(0.6, 1) +
  labs(
    title = "Performance vs. Interpretability Trade-off",
    subtitle = "Different models offer different balances",
    x = "Interpretability",
    y = "Performance"
  ) +
  theme(legend.position = "none")

6.13 Case Study: Identifying High-Impact Sustainability Initiatives

Let’s apply our model interpretation to a business case study:

# Create a simulated dataset of potential sustainability initiatives
initiatives <- tibble(
  initiative_id = 1:5,
  initiative_name = c(
    "Implement Climate Change Policy",
    "Link Management Compensation to Sustainability",
    "Conduct ESG Audit",
    "Commit to SBTI Targets",
    "Increase Transparency in Sustainability Reporting"
  ),
  estimated_cost = c(500000, 2000000, 300000, 1000000, 200000),
  implementation_time = c(6, 12, 3, 24, 4)  # months
)

# Estimate the impact of each initiative based on our model interpretation
initiatives <- initiatives %>%
  mutate(
    estimated_emissions_reduction = c(0.15, 0.10, 0.05, 0.08, 0.03),  # log scale
    roi = estimated_emissions_reduction / (estimated_cost / 1000000)
  )

# Display the initiatives
initiatives
# A tibble: 5 Γ— 6
  initiative_id initiative_name               estimated_cost implementation_time
          <int> <chr>                                  <dbl>               <dbl>
1             1 Implement Climate Change Pol…         500000                   6
2             2 Link Management Compensation…        2000000                  12
3             3 Conduct ESG Audit                     300000                   3
4             4 Commit to SBTI Targets               1000000                  24
5             5 Increase Transparency in Sus…         200000                   4
# β„Ή 2 more variables: estimated_emissions_reduction <dbl>, roi <dbl>
# Visualize the ROI of each initiative
p_roi <- initiatives %>%
  mutate(initiative_name = fct_reorder(initiative_name, roi)) %>%
  ggplot(aes(x = initiative_name, y = roi, fill = estimated_emissions_reduction)) +
  geom_col() +
  coord_flip() +
  scale_fill_viridis_c(option = "turbo") +
  labs(
    title = "ROI of Sustainability Initiatives",
    subtitle = "Based on Model Interpretation",
    x = NULL,
    y = "ROI (Emissions Reduction per $1M Cost)",
    fill = "Estimated\nEmissions\nReduction\n(log scale)"
  )

# Display the plot
p_roi

# Create an interactive version
# ggplotly(p_roi)

Interpreting the Case Study

Based on our model interpretation, we can prioritize sustainability initiatives:

  1. Implement Climate Change Policy: Highest ROI due to relatively low cost and significant impact on emissions.
  2. Increase Transparency in Sustainability Reporting: Good ROI due to very low cost, though with modest emissions impact.
  3. Conduct ESG Audit: Moderate ROI with relatively low cost and modest emissions impact.
  4. Commit to SBTI Targets: Lower ROI due to higher cost and longer implementation time, though with significant emissions impact.
  5. Link Management Compensation to Sustainability: Lowest ROI due to high cost, though with substantial emissions impact.

This analysis helps companies prioritize sustainability initiatives based on both cost and impact.

6.14 Summary

In this chapter, we’ve explored various techniques for interpreting machine learning models in the context of sustainability analytics:

  1. SHAP Values: Provide detailed insights into how each feature contributes to predictions for individual instances.
  2. Variable Importance: Offers a high-level overview of which features matter most in the model.
  3. Partial Dependence Plots: Show the average effect of a feature on the predicted outcome.
  4. ICE Plots: Illustrate how predictions change for individual instances as a feature value changes.

These techniques help us understand the factors driving greenhouse gas emissions and identify effective sustainability strategies. By combining predictive power with interpretability, we can develop models that not only accurately predict emissions but also provide actionable insights for emissions reduction.

6.15 Exercises

Exercise 1: SHAP Analysis with Different Models

Using the dataset from this chapter:

  1. Fit the Random Forest model (wf_rf) to the training data.
  2. Calculate SHAP values for both the XGBoost and Random Forest models.
  3. Compare the SHAP summary plots between these models.
  4. Identify similarities and differences in feature importance and impact direction.
  5. Discuss which model provides more interpretable results and why.

Exercise 2: Feature Interaction Analysis

Using the dataset from this chapter:

  1. Create SHAP interaction plots for different pairs of features (e.g., log_total_assets and climate_change_policy).
  2. Identify the strongest interactions between features.
  3. Interpret these interactions in the context of corporate sustainability.
  4. Discuss how these interactions could inform sustainability strategy.

Exercise 3: Comparing SHAP and PDP

For a feature of your choice:

  1. Create both a SHAP dependence plot and a partial dependence plot.
  2. Compare the insights gained from each visualization.
  3. Discuss the benefits and limitations of each approach.
  4. Explain when you would prefer one visualization over the other.

Exercise 4: Counterfactual Analysis

Using the dataset from this chapter:

  1. Select a company with high predicted emissions.
  2. Use SHAP values to identify the features contributing most to the high prediction.
  3. Create a counterfactual scenario by modifying these features to reduce predicted emissions.
  4. Quantify the potential emissions reduction from these changes.
  5. Discuss the feasibility of implementing these changes in practice.

Exercise 5: Business Application

You are a sustainability consultant advising a large corporation on emissions reduction:

  1. Use the model and interpretation techniques from this chapter to identify the most effective emissions reduction strategies.
  2. Estimate the potential emissions reduction from each strategy.
  3. Create a prioritized list of recommendations based on impact and feasibility.
  4. Design a dashboard to communicate these insights to company executives.
  5. Discuss how you would address potential ethical concerns in your recommendations.

6.16 References