{"spec_id":"shap-summary","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nshap-summary: SHAP Summary Plot\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\n\n\nLetsPlot.setup_html()\n\n# Theme setup\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Data: Simulate SHAP values for a healthcare outcome prediction model\nnp.random.seed(42)\nn_samples = 250\nn_features = 12\n\nfeature_names = [\n    \"BMI\",\n    \"Blood Pressure\",\n    \"Glucose Level\",\n    \"Cholesterol\",\n    \"Heart Rate\",\n    \"Sleep Hours\",\n    \"Exercise Days\",\n    \"Stress Score\",\n    \"Age\",\n    \"Triglycerides\",\n    \"HDL Cholesterol\",\n    \"LDL Cholesterol\",\n]\n\n# Generate realistic medical feature values\nfeature_values = np.column_stack(\n    [\n        np.random.normal(26, 5, n_samples),  # BMI\n        np.random.normal(130, 15, n_samples),  # Blood Pressure (systolic)\n        np.random.normal(110, 25, n_samples),  # Glucose Level\n        np.random.normal(200, 40, n_samples),  # Cholesterol\n        np.random.normal(75, 12, n_samples),  # Heart Rate\n        np.random.normal(7, 1.5, n_samples),  # Sleep Hours\n        np.random.normal(4, 2, n_samples),  # Exercise Days/week\n        np.random.normal(50, 20, n_samples),  # Stress Score (0-100)\n        np.random.normal(55, 15, n_samples),  # Age\n        np.random.normal(150, 50, n_samples),  # Triglycerides\n        np.random.normal(50, 12, n_samples),  # HDL Cholesterol\n        np.random.normal(130, 30, n_samples),  # LDL Cholesterol\n    ]\n)\n\n# Generate SHAP values with realistic medical relationships\nshap_values = np.zeros((n_samples, n_features))\n\n# BMI: positive effect on risk (higher BMI = worse outcome)\nbmi_norm = (feature_values[:, 0] - 25) / 5\nshap_values[:, 0] = bmi_norm * 0.8 + np.random.normal(0, 0.15, n_samples)\n\n# Blood Pressure: positive effect\nbp_norm = (feature_values[:, 1] - 120) / 20\nshap_values[:, 1] = bp_norm * 0.9 + np.random.normal(0, 0.14, n_samples)\n\n# Glucose Level: strong positive effect\nglucose_norm = (feature_values[:, 2] - 100) / 30\nshap_values[:, 2] = glucose_norm * 1.2 + np.random.normal(0, 0.18, n_samples)\n\n# Cholesterol: positive effect\nchol_norm = (feature_values[:, 3] - 200) / 50\nshap_values[:, 3] = chol_norm * 0.7 + np.random.normal(0, 0.16, n_samples)\n\n# Heart Rate: U-shaped effect (too low or too high is bad)\nhr_centered = np.abs(feature_values[:, 4] - 72)\nshap_values[:, 4] = (hr_centered / 15) * 0.5 + np.random.normal(0, 0.12, n_samples)\n\n# Sleep Hours: negative effect (more sleep = better)\nshap_values[:, 5] = -(feature_values[:, 5] - 7) * 0.15 + np.random.normal(0, 0.1, n_samples)\n\n# Exercise Days: strong negative effect (protective)\nshap_values[:, 6] = -(feature_values[:, 6] - 3.5) * 0.2 + np.random.normal(0, 0.12, n_samples)\n\n# Stress Score: positive effect\nstress_norm = (feature_values[:, 7] - 40) / 25\nshap_values[:, 7] = stress_norm * 0.6 + np.random.normal(0, 0.13, n_samples)\n\n# Age: positive effect\nage_norm = (feature_values[:, 8] - 50) / 20\nshap_values[:, 8] = age_norm * 0.7 + np.random.normal(0, 0.14, n_samples)\n\n# Triglycerides: moderate positive effect\ntrig_norm = (feature_values[:, 9] - 150) / 60\nshap_values[:, 9] = trig_norm * 0.45 + np.random.normal(0, 0.11, n_samples)\n\n# HDL Cholesterol: negative effect (protective)\nhdl_norm = (feature_values[:, 10] - 50) / 15\nshap_values[:, 10] = -hdl_norm * 0.35 + np.random.normal(0, 0.09, n_samples)\n\n# LDL Cholesterol: positive effect\nldl_norm = (feature_values[:, 11] - 130) / 40\nshap_values[:, 11] = ldl_norm * 0.55 + np.random.normal(0, 0.13, n_samples)\n\n# Calculate mean absolute SHAP value for feature importance\nmean_abs_shap = np.abs(shap_values).mean(axis=0)\nfeature_order = np.argsort(mean_abs_shap)[::-1]\n\n# Select top 10 features\ntop_k = 10\ntop_indices = feature_order[:top_k]\n\n# Create long-form DataFrame for plotting\ndata_records = []\nfor rank, feat_idx in enumerate(top_indices):\n    feat_name = feature_names[feat_idx]\n    feat_shap = shap_values[:, feat_idx]\n    feat_val = feature_values[:, feat_idx]\n    # Normalize feature values to 0-1 for consistent coloring\n    feat_val_norm = (feat_val - feat_val.min()) / (feat_val.max() - feat_val.min() + 1e-8)\n    # Add vertical jitter for visibility\n    jitter = np.random.uniform(-0.25, 0.25, n_samples)\n\n    for i in range(n_samples):\n        data_records.append(\n            {\n                \"Feature\": feat_name,\n                \"SHAP Value\": feat_shap[i],\n                \"Feature Value\": feat_val_norm[i],\n                \"y_position\": (top_k - 1 - rank) + jitter[i],\n                \"importance_rank\": rank,\n            }\n        )\n\ndf = pd.DataFrame(data_records)\n\n# Create ordered feature list (most important at top)\nordered_features = [feature_names[i] for i in top_indices]\n\n# Theme configuration\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG),\n    panel_grid_major_x=element_line(color=INK_MUTED, size=0.3),\n    panel_grid_minor=element_blank(),\n    axis_title=element_text(color=INK, size=20),\n    axis_text=element_text(color=INK_SOFT, size=16),\n    plot_title=element_text(color=INK, size=24),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_title=element_text(color=INK, size=16),\n    legend_text=element_text(color=INK_SOFT, size=16),\n    panel_grid_major_y=element_blank(),\n)\n\n# Plot\nplot = (\n    ggplot(df, aes(x=\"SHAP Value\", y=\"y_position\", color=\"Feature Value\"))\n    + geom_point(size=3, alpha=0.7)\n    + geom_vline(xintercept=0, color=INK_MUTED, size=0.8, linetype=\"dashed\")\n    + scale_color_gradient(low=\"#4467A3\", high=\"#C475FD\", name=\"Feature\\nValue\")\n    + scale_y_continuous(breaks=list(range(top_k)), labels=ordered_features[::-1])\n    + labs(x=\"SHAP Value (impact on model output)\", y=\"\", title=\"shap-summary · letsplot · anyplot.ai\")\n    + ggsize(1600, 900)\n    + anyplot_theme\n)\n\n# Save as PNG and HTML\n_plot_dir = os.path.dirname(os.path.abspath(__file__))\nggsave(plot, f\"{_plot_dir}/plot-{THEME}.png\", scale=3)\nggsave(plot, f\"{_plot_dir}/plot-{THEME}.html\")\n"}