{"spec_id":"shap-summary","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nshap-summary: SHAP Summary Plot\nLibrary: bokeh 3.9.0 | Python 3.13.13\nQuality: 90/100 | Created: 2026-05-14\n\"\"\"\n\nimport os\nimport time\nfrom pathlib import Path\n\nimport numpy as np\nimport pandas as pd\nfrom bokeh.models import ColumnDataSource\nfrom bokeh.palettes import BrBG11\nfrom bokeh.plotting import figure, output_file, save\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\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\"\n\nnp.random.seed(42)\n\nfeature_names = [\n    \"Square Footage\",\n    \"Age (years)\",\n    \"Bedrooms\",\n    \"Bathrooms\",\n    \"Distance to Center\",\n    \"Lot Size\",\n    \"Condition Score\",\n    \"Parking Spaces\",\n    \"Garage Size\",\n    \"Year Built\",\n]\n\nn_samples = 150\nn_features = len(feature_names)\n\nshap_values = np.random.normal(0, 1, (n_samples, n_features))\nshap_values[:, 0] *= 1.5\nshap_values[:, 1] *= 1.2\nshap_values[:, 5] *= 0.8\n\nfeature_values = np.zeros((n_samples, n_features))\nfeature_values[:, 0] = np.random.normal(2500, 800, n_samples)\nfeature_values[:, 1] = np.random.normal(30, 15, n_samples)\nfeature_values[:, 2] = np.random.normal(3.5, 1.2, n_samples)\nfeature_values[:, 3] = np.random.normal(2.0, 0.8, n_samples)\nfeature_values[:, 4] = np.random.normal(10, 5, n_samples)\nfeature_values[:, 5] = np.random.normal(8000, 3000, n_samples)\nfeature_values[:, 6] = np.random.normal(70, 20, n_samples)\nfeature_values[:, 7] = np.random.normal(2.0, 0.7, n_samples)\nfeature_values[:, 8] = np.random.normal(500, 250, n_samples)\nfeature_values[:, 9] = np.random.normal(1995, 20, n_samples)\n\nmean_abs_shap = np.abs(shap_values).mean(axis=0)\nsorted_indices = np.argsort(-mean_abs_shap)\nsorted_feature_names = [feature_names[i] for i in sorted_indices]\nsorted_shap = shap_values[:, sorted_indices]\nsorted_values = feature_values[:, sorted_indices]\n\ndata_dict = {\"x\": [], \"y\": [], \"color_val\": [], \"feature\": []}\n\nfor feature_idx, feature_name in enumerate(sorted_feature_names):\n    n = len(sorted_shap[:, feature_idx])\n    y_positions = np.linspace(feature_idx - 0.3, feature_idx + 0.3, n)\n    np.random.shuffle(y_positions)\n\n    data_dict[\"x\"].extend(sorted_shap[:, feature_idx])\n    data_dict[\"y\"].extend(y_positions)\n    data_dict[\"color_val\"].extend(sorted_values[:, feature_idx])\n    data_dict[\"feature\"].extend([feature_name] * n)\n\ndf = pd.DataFrame(data_dict)\nfeature_min = df[\"color_val\"].min()\nfeature_max = df[\"color_val\"].max()\ndf[\"color_normalized\"] = (df[\"color_val\"] - feature_min) / (feature_max - feature_min) * 10\ndf[\"color_normalized\"] = df[\"color_normalized\"].round(0).astype(int).clip(0, 10)\n\ncolor_palette_map = {\n    0: BrBG11[0],\n    1: BrBG11[1],\n    2: BrBG11[2],\n    3: BrBG11[3],\n    4: BrBG11[4],\n    5: BrBG11[5],\n    6: BrBG11[6],\n    7: BrBG11[7],\n    8: BrBG11[8],\n    9: BrBG11[9],\n    10: BrBG11[10],\n}\ndf[\"color\"] = df[\"color_normalized\"].map(color_palette_map)\n\nsource = ColumnDataSource(df)\n\np = figure(width=4800, height=2700, y_range=sorted_feature_names[::-1], title=\"shap-summary · bokeh · anyplot.ai\")\n\np.circle(x=\"x\", y=\"y\", source=source, size=12, color=\"color\", alpha=0.6, line_color=None)\n\np.line(x=[0, 0], y=[-1, len(sorted_feature_names)], line_width=2, color=INK_SOFT, alpha=0.5)\n\np.xaxis.axis_label = \"SHAP Value\"\np.yaxis.axis_label = \"Feature\"\n\np.title.text_font_size = \"28pt\"\np.title.text_color = INK\n\np.xaxis.axis_label_text_font_size = \"22pt\"\np.xaxis.axis_label_text_color = INK\np.yaxis.axis_label_text_font_size = \"22pt\"\np.yaxis.axis_label_text_color = INK\n\np.xaxis.major_label_text_font_size = \"18pt\"\np.xaxis.major_label_text_color = INK_SOFT\np.yaxis.major_label_text_font_size = \"18pt\"\np.yaxis.major_label_text_color = INK_SOFT\n\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\np.outline_line_color = INK_SOFT\n\np.xgrid.grid_line_color = INK\np.ygrid.grid_line_color = INK\np.xgrid.grid_line_alpha = 0.10\np.ygrid.grid_line_alpha = 0.10\n\np.xaxis.axis_line_color = INK_SOFT\np.yaxis.axis_line_color = INK_SOFT\np.xaxis.major_tick_line_color = INK_SOFT\np.yaxis.major_tick_line_color = INK_SOFT\n\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\nW, H = 4800, 2700\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.set_window_size(W, H)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}