{"spec_id":"heatmap-annotated","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nheatmap-annotated: Annotated Heatmap\nLibrary: bokeh 3.9.2 | Python 3.13.14\nQuality: 90/100 | Updated: 2026-08-05\n\"\"\"\n\nimport os\nimport sys\nimport time\nfrom pathlib import Path\n\nimport numpy as np\n\n\n# Remove script's own directory from sys.path so 'bokeh' resolves to the installed package\n_this_dir = os.path.dirname(os.path.abspath(__file__))\nif _this_dir in sys.path:\n    sys.path.remove(_this_dir)\n\nfrom bokeh.io import output_file, save\nfrom bokeh.models import BasicTicker, ColorBar, ColumnDataSource, HoverTool, LinearColorMapper\nfrom bokeh.plotting import figure\nfrom bokeh.transform import transform\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\n# Theme tokens\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\n# Data: Correlation matrix for financial metrics\nnp.random.seed(42)\nvariables = [\"Revenue\", \"Profit\", \"Assets\", \"Debt\", \"Growth\", \"ROI\", \"Market Cap\", \"Volume\"]\nn = len(variables)\n\n# Generate realistic correlation matrix\nbase = np.random.randn(100, n)\nbase[:, 1] = base[:, 0] * 0.8 + np.random.randn(100) * 0.5\nbase[:, 5] = base[:, 1] * 0.7 + np.random.randn(100) * 0.6\nbase[:, 6] = base[:, 0] * 0.6 + np.random.randn(100) * 0.7\nbase[:, 3] = -base[:, 5] * 1.0 + np.random.randn(100) * 0.5\ncorr_matrix = np.corrcoef(base.T)\nnp.fill_diagonal(corr_matrix, 1.0)\n\n\ndef _lerp_hex(c0, c1, t):\n    r0, g0, b0 = (int(c0[i : i + 2], 16) for i in (1, 3, 5))\n    r1, g1, b1 = (int(c1[i : i + 2], 16) for i in (1, 3, 5))\n    r = int(round(r0 + (r1 - r0) * t))\n    g = int(round(g0 + (g1 - g0) * t))\n    b = int(round(b0 + (b1 - b0) * t))\n    return f\"#{r:02X}{g:02X}{b:02X}\"\n\n\ndef _luminance(hex_color):\n    r, g, b = (int(hex_color[i : i + 2], 16) / 255 for i in (1, 3, 5))\n    return 0.2126 * r + 0.7152 * g + 0.0722 * b\n\n\ndef _value_to_hex(v):\n    v = max(-1.0, min(1.0, v))\n    return _lerp_hex(\"#AE3030\", _midpoint, v + 1.0) if v < 0 else _lerp_hex(_midpoint, \"#4467A3\", v)\n\n\n# Imprint diverging colormap (matte-red -> theme-adaptive midpoint -> blue) for\n# signed correlation data. Never a library-native cmap (BrBG etc.) — Imprint identity.\n_midpoint = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nANYPLOT_DIV256 = [_lerp_hex(\"#AE3030\", _midpoint, t / 127.0) for t in range(128)] + [\n    _lerp_hex(_midpoint, \"#4467A3\", t / 127.0) for t in range(128)\n]\nmapper = LinearColorMapper(palette=ANYPLOT_DIV256, low=-1, high=1)\n\n# Prepare data for bokeh. Text color picks the ink that contrasts with each\n# cell's ACTUAL fill (not a fixed light/dark split) — the diverging colormap's\n# midpoint equals the page background, so near-zero cells in dark mode render\n# near-black and a fixed \"black\" text would be invisible against them. Cell\n# borders get the same near-background treatment, so |value| < 0.05 cells get\n# a faint INK_SOFT-tinted border instead of a pure PAGE_BG one to keep the grid\n# structure visible where the fill would otherwise vanish into the canvas.\n_floor_line = _lerp_hex(PAGE_BG, INK_SOFT, 0.15)\nx_coords = []\ny_coords = []\nvalues = []\ntext_values = []\ntext_colors = []\nline_colors = []\n\nfor i, row_var in enumerate(variables):\n    for j, col_var in enumerate(variables):\n        x_coords.append(col_var)\n        y_coords.append(row_var)\n        val = corr_matrix[i, j]\n        values.append(val)\n        text_values.append(f\"{val:.2f}\")\n        fill_hex = _value_to_hex(val)\n        text_colors.append(\"#1A1A17\" if _luminance(fill_hex) > 0.5 else \"#F0EFE8\")\n        line_colors.append(_floor_line if abs(val) < 0.05 else PAGE_BG)\n\nsource = ColumnDataSource(\n    data={\n        \"x\": x_coords,\n        \"y\": y_coords,\n        \"value\": values,\n        \"text\": text_values,\n        \"text_color\": text_colors,\n        \"line_color\": line_colors,\n    }\n)\n\n# Canvas: 2400x2400 px square (hard contract — symmetric matrix, no preferred\n# horizontal axis). min_border_top is large because x_axis_location=\"above\"\n# stacks the title, x-axis label, and rotated x tick labels all above the plot.\nW, H = 2400, 2400\np = figure(\n    width=W,\n    height=H,\n    x_range=variables,\n    y_range=list(reversed(variables)),\n    title=\"heatmap-annotated · python · bokeh · anyplot.ai\",\n    x_axis_location=\"above\",\n    toolbar_location=None,  # bokeh's default toolbar shrinks the saved PNG below `height=`\n    min_border_top=380,  # title (50pt) + x-axis label (42pt) + rotated x tick labels (34pt)\n    min_border_bottom=60,\n    min_border_left=260,  # y tick labels (34pt) + y-axis label (42pt)\n    min_border_right=260,  # ColorBar + its tick/title labels\n)\n\n# Add heatmap rectangles\np.rect(\n    x=\"x\",\n    y=\"y\",\n    width=1,\n    height=1,\n    source=source,\n    fill_color=transform(\"value\", mapper),\n    line_color=\"line_color\",\n    line_width=3,\n)\n\n# Add text annotations\np.text(\n    x=\"x\",\n    y=\"y\",\n    text=\"text\",\n    source=source,\n    text_align=\"center\",\n    text_baseline=\"middle\",\n    text_font_size=\"28pt\",\n    text_color=\"text_color\",\n)\n\n# Add hover tooltip for interactivity\nhover = HoverTool(\n    tooltips=[(\"Row Metric\", \"@y\"), (\"Column Metric\", \"@x\"), (\"Pearson Correlation\", \"@value{0.00}\")], mode=\"mouse\"\n)\np.add_tools(hover)\n\n# Style the figure\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\np.outline_line_color = INK_SOFT\n\np.title.text_font_size = \"50pt\"\np.title.text_color = INK\np.title.align = \"center\"\n\np.xaxis.axis_label = \"Financial Metric\"\np.yaxis.axis_label = \"Financial Metric\"\np.xaxis.axis_label_text_font_size = \"42pt\"\np.yaxis.axis_label_text_font_size = \"42pt\"\np.xaxis.axis_label_text_color = INK\np.yaxis.axis_label_text_color = INK\np.xaxis.major_label_text_font_size = \"34pt\"\np.yaxis.major_label_text_font_size = \"34pt\"\np.xaxis.major_label_text_color = INK_SOFT\np.yaxis.major_label_text_color = INK_SOFT\np.xaxis.axis_line_color = INK_SOFT\np.yaxis.axis_line_color = INK_SOFT\np.xaxis.major_tick_line_color = None\np.yaxis.major_tick_line_color = None\np.xaxis.major_label_orientation = 0.7\np.axis.axis_line_color = None\np.axis.major_tick_line_color = None\np.grid.grid_line_color = None\n\n# Add colorbar — text colors set explicitly theme-adaptive; the ColorBar's own\n# panel sits against border_fill_color (PAGE_BG), so Bokeh's default (black)\n# label color is unreadable in dark mode unless overridden here.\ncolor_bar = ColorBar(\n    color_mapper=mapper,\n    ticker=BasicTicker(desired_num_ticks=9),\n    label_standoff=12,\n    major_label_text_font_size=\"26pt\",\n    major_label_text_color=INK_SOFT,\n    title=\"Pearson Correlation\",\n    title_text_font_size=\"30pt\",\n    title_text_color=INK,\n    major_tick_line_color=INK_SOFT,\n    background_fill_color=PAGE_BG,\n    border_line_color=None,\n    width=40,\n    location=(0, 0),\n)\np.add_layout(color_bar, \"right\")\n\n\n# Save\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\n# Screenshot with headless Chrome via Selenium (do NOT use export_png — chromedriver snap issues)\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 + 200}\",\n    \"--hide-scrollbars\",\n    \"--force-device-scale-factor=1\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\n# IMPORTANT: headless Chrome's --window-size sets the OUTER window, which still\n# reserves a phantom title-bar height even headless — pin the viewport exactly via CDP.\ndriver.execute_cdp_cmd(\n    \"Emulation.setDeviceMetricsOverride\", {\"width\": W, \"height\": H, \"deviceScaleFactor\": 1, \"mobile\": False}\n)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)  # let bokeh's JS render the canvas\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}