{"spec_id":"heatmap-annotated","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nheatmap-annotated: Annotated Heatmap\nLibrary: altair 6.2.2 | Python 3.13.14\nQuality: 88/100 | Updated: 2026-08-05\n\"\"\"\n\nimport os\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\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: Create a correlation matrix with better representation of positive/negative correlations\nnp.random.seed(42)\n\n# Business metrics with meaningful correlations\nmetrics = [\"Revenue\", \"Marketing\", \"R&D\", \"Support\", \"Profit\", \"Growth\", \"Efficiency\"]\nn_metrics = len(metrics)\n\n# Generate base data with controlled correlations\nbase_data = np.random.randn(150, n_metrics)\n\n# Introduce realistic correlations\nbase_data[:, 1] = base_data[:, 0] * 0.7 + np.random.randn(150) * 0.3  # Marketing ↔ Revenue\nbase_data[:, 2] = base_data[:, 0] * 0.6 + np.random.randn(150) * 0.4  # R&D ↔ Revenue\nbase_data[:, 3] = -base_data[:, 1] * 0.5 + np.random.randn(150) * 0.5  # Support ↔ Marketing (inverse)\nbase_data[:, 4] = base_data[:, 0] * 0.8 - base_data[:, 3] * 0.3 + np.random.randn(150) * 0.2  # Profit\nbase_data[:, 5] = base_data[:, 2] * 0.65 + np.random.randn(150) * 0.35  # Growth ↔ R&D\nbase_data[:, 6] = -base_data[:, 1] * 0.4 + base_data[:, 0] * 0.3 + np.random.randn(150) * 0.5  # Efficiency\n\n# Calculate correlation matrix\ncorr_matrix = np.corrcoef(base_data.T)\n\n# Create long-format DataFrame for Altair\nrows = []\nfor i, row_metric in enumerate(metrics):\n    for j, col_metric in enumerate(metrics):\n        value = round(corr_matrix[i, j], 2)\n        rows.append({\"x\": col_metric, \"y\": row_metric, \"correlation\": value, \"abs_correlation\": abs(value)})\n\ndf = pd.DataFrame(rows)\n\n# Create base heatmap with rectangles\nbase_chart = (\n    alt.Chart(df)\n    .mark_rect(stroke=\"white\", strokeWidth=2)\n    .encode(\n        x=alt.X(\n            \"x:N\",\n            title=\"Business Metrics\",\n            sort=metrics,\n            axis=alt.Axis(labelFontSize=11, titleFontSize=12, labelAngle=-45),\n        ),\n        y=alt.Y(\"y:N\", title=\"Business Metrics\", sort=metrics, axis=alt.Axis(labelFontSize=11, titleFontSize=12)),\n        color=alt.Color(\n            \"correlation:Q\",\n            # imprint_div (Imprint diverging cmap): matte-red <-> theme-adaptive midpoint <-> blue\n            scale=alt.Scale(range=[\"#AE3030\", PAGE_BG, \"#4467A3\"], domain=[-1, 1], domainMid=0),\n            legend=alt.Legend(\n                title=\"Correlation\",\n                titleFontSize=10,\n                labelFontSize=10,\n                fillColor=ELEVATED_BG,\n                strokeColor=INK_SOFT,\n                gradientStrokeColor=INK_SOFT,\n                gradientStrokeWidth=0.5,\n            ),\n        ),\n        tooltip=[\n            alt.Tooltip(\"x:N\", title=\"Column\"),\n            alt.Tooltip(\"y:N\", title=\"Row\"),\n            alt.Tooltip(\"correlation:Q\", title=\"Correlation\", format=\".2f\"),\n        ],\n    )\n)\n\n# Create text layer for annotations with conditional color.\n# Annotation size scales with |correlation| so the strongest relationships\n# visually dominate the grid, while weak/near-zero cells recede further.\ntext = (\n    alt.Chart(df)\n    .mark_text(fontWeight=\"bold\")\n    .encode(\n        x=alt.X(\"x:N\", sort=metrics),\n        y=alt.Y(\"y:N\", sort=metrics),\n        text=alt.Text(\"correlation:Q\", format=\".2f\"),\n        size=alt.Size(\"abs_correlation:Q\", scale=alt.Scale(domain=[0, 1], range=[10, 15]), legend=None),\n        color=alt.condition(\n            (alt.datum.correlation > 0.5) | (alt.datum.correlation < -0.5), alt.value(\"white\"), alt.value(INK_SOFT)\n        ),\n    )\n)\n\n# Combine heatmap and text\nchart = (\n    (base_chart + text)\n    .properties(\n        width=420,\n        height=500,\n        padding={\"left\": 0, \"right\": 0, \"top\": 0, \"bottom\": 0},\n        title=alt.Title(\"heatmap-annotated · python · altair · anyplot.ai\", fontSize=16, anchor=\"middle\"),\n        background=PAGE_BG,\n    )\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT, strokeWidth=0)\n    .configure_axis(\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        gridColor=INK_SOFT,\n        gridOpacity=0.0,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n    )\n    .configure_title(color=INK, fontSize=16, anchor=\"middle\")\n    .configure_legend(\n        fillColor=ELEVATED_BG,\n        strokeColor=INK_SOFT,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        titleFontSize=10,\n        labelFontSize=10,\n    )\n)\n\n# Save as PNG — hard target: 2400x2400 (square). See prompts/library/altair.md \"Canvas\".\nchart.save(f\"plot-{THEME}.png\", scale_factor=4.0)\n\n# vl-convert pads the view with title/axis/legend extents outside width/height,\n# so the raw save rarely lands exactly on target — pad (never crop) to match.\nTW, TH = 2400, 2400\n_img = Image.open(f\"plot-{THEME}.png\").convert(\"RGB\")\n_w, _h = _img.size\nif _w > TW or _h > TH:\n    raise SystemExit(\n        f\"altair vl-convert produced {_w}x{_h}, exceeds target {TW}x{TH}. \"\n        f\"Shrink chart .properties(width=, height=) values and re-render.\"\n    )\nif _w < TW or _h < TH:\n    _canvas = Image.new(\"RGB\", (TW, TH), PAGE_BG)\n    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))\n    _canvas.save(f\"plot-{THEME}.png\")\n\n# Save as HTML for interactive version\nchart.save(f\"plot-{THEME}.html\")\n"}