from __future__ import annotations from typing import List import numpy as np import pandas as pd import shap from sklearn.pipeline import Pipeline def compute_shap_explanation(pipeline: Pipeline, feature_row: dict, feature_columns: list) -> list: """ Returns top-3 SHAP feature contributions as a list of dicts. """ xgb_model = pipeline.named_steps["model"] explainer = shap.TreeExplainer(xgb_model) preprocessor = Pipeline(steps=pipeline.steps[:-1]) row_df = pd.DataFrame([feature_row], columns=feature_columns) transformed = preprocessor.transform(row_df) shap_values = explainer.shap_values(transformed) if isinstance(shap_values, list): probabilities = xgb_model.predict_proba(transformed) class_index = int(np.argmax(probabilities[0])) row_values = np.asarray(shap_values[class_index])[0] else: row_values = np.asarray(shap_values)[0] top_indices = np.argsort(np.abs(row_values))[::-1][:3] result = [] for idx in top_indices: value = float(row_values[idx]) result.append({ "feature": feature_columns[idx], "shap_value": round(value, 4), "direction": "positive" if value >= 0 else "negative", }) return result