use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Explanation {
pub shap_values: Vec<Vec<f64>>,
pub base_value: f64,
pub feature_names: Option<Vec<String>>,
pub data: Option<Vec<f64>>,
pub metadata: Option<std::collections::HashMap<String, String>>,
}
impl Explanation {
pub fn new(
shap_values: Vec<Vec<f64>>,
base_value: f64,
feature_names: Option<Vec<String>>,
data: Option<Vec<f64>>,
metadata: Option<std::collections::HashMap<String, String>>,
) -> Self {
Self {
shap_values,
base_value,
feature_names,
data,
metadata,
}
}
pub fn values(&self) -> &Vec<Vec<f64>> {
&self.shap_values
}
pub fn summary(&self) {
println!("SHAP Explanation Summary");
println!("------------------------");
println!("Base value: {}", self.base_value);
match &self.feature_names {
Some(names) => println!("Features: {:?}", names),
None => println!("Features: None"),
}
println!(
"SHAP matrix: {} samples × {} features",
self.shap_values.len(),
if self.shap_values.is_empty() { 0 } else { self.shap_values[0].len() }
);
if let Some(d) = &self.data {
println!("Contains data sample of length {}", d.len());
} else {
println!("Data sample: None");
}
}
}
impl Explanation {
pub fn shap_for_feature(&self, index: usize) -> Option<Vec<f64>> {
if self.shap_values.is_empty() || index >= self.shap_values[0].len() {
return None;
}
let mut column = Vec::new();
for row in &self.shap_values {
column.push(row[index]);
}
Some(column)
}
}