{"spec_id":"shap-waterfall","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nshap-waterfall: SHAP Waterfall Plot for Feature Attribution\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 90/100 | Created: 2026-05-07\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nGRID = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\n# SHAP semantic colors (Okabe-Ito positions used for semantic encoding)\nPOS_COLOR = \"#AE3030\"  # imprint red — positive SHAP (pushes prediction up)\nNEG_COLOR = \"#4467A3\"  # blue — negative SHAP (pushes prediction down)\nREF_COLOR = \"#009E73\"  # brand green — reference bars (base & final)\n\n# Data — credit risk model SHAP waterfall for a single high-risk loan applicant\nbase_value = 0.35  # E[f(x)]: average predicted default rate across training data\nfinal_value = 0.72  # f(x): model's default probability prediction for this applicant\n\nfeatures = [\n    \"Debt-to-Income Ratio\",\n    \"Missed Payments (12 mo)\",\n    \"Credit Utilization\",\n    \"Credit Score\",\n    \"Annual Income\",\n    \"Employment Stability\",\n    \"Credit History Length\",\n    \"Account Diversity\",\n    \"Property Ownership\",\n]\nshap_values = [0.18, 0.15, 0.12, -0.09, 0.08, -0.06, 0.06, -0.04, -0.03]\n\n# Sort ascending by |SHAP| so the largest contributor appears nearest the top\n# (Plotly horizontal waterfall: first y item = bottom, last y item = top)\norder = np.argsort(np.abs(shap_values))\nsorted_features = [features[i] for i in order]\nsorted_shap = [shap_values[i] for i in order]\n\n# Waterfall layers: base (absolute) → features (relative, small→large) → prediction (total)\ny_labels = [\"E[f(x)] = 0.35\"] + sorted_features + [\"f(x) = 0.72\"]\nx_vals = [base_value] + sorted_shap + [0]\nmeasures = [\"absolute\"] + [\"relative\"] * len(sorted_features) + [\"total\"]\ntext_vals = [f\"{base_value:.2f}\"] + [f\"{v:+.3f}\" for v in sorted_shap] + [f\"{final_value:.2f}\"]\n\n# Plot\nfig = go.Figure(\n    go.Waterfall(\n        orientation=\"h\",\n        measure=measures,\n        y=y_labels,\n        x=x_vals,\n        text=text_vals,\n        textposition=\"outside\",\n        textfont=dict(size=17, color=INK),\n        increasing=dict(marker=dict(color=POS_COLOR, line=dict(color=POS_COLOR, width=0))),\n        decreasing=dict(marker=dict(color=NEG_COLOR, line=dict(color=NEG_COLOR, width=0))),\n        totals=dict(marker=dict(color=REF_COLOR, line=dict(color=REF_COLOR, width=0))),\n        connector=dict(line=dict(color=INK_MUTED, width=1.5, dash=\"dot\")),\n        cliponaxis=False,\n    )\n)\n\n# Layout\nfig.update_layout(\n    title=dict(\n        text=\"Credit Default Risk · shap-waterfall · plotly · anyplot.ai\",\n        font=dict(size=26, color=INK),\n        x=0.5,\n        xanchor=\"center\",\n    ),\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font=dict(color=INK),\n    xaxis=dict(\n        title=dict(text=\"SHAP Value (impact on predicted default probability)\", font=dict(size=22, color=INK)),\n        tickfont=dict(size=18, color=INK_SOFT),\n        gridcolor=GRID,\n        linecolor=INK_SOFT,\n        zerolinecolor=INK_SOFT,\n        zerolinewidth=2,\n        range=[-0.20, 0.95],\n    ),\n    yaxis=dict(tickfont=dict(size=18, color=INK_SOFT), linecolor=INK_SOFT, showgrid=False),\n    showlegend=False,\n    margin=dict(l=240, r=160, t=80, b=80),\n)\n\n# Save\nfig.write_image(f\"plot-{THEME}.png\", width=1600, height=900, scale=3)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}