{"spec_id":"heatmap-adjacency","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nheatmap-adjacency: Network Adjacency Matrix Heatmap\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 85/100 | Created: 2026-05-08\n\"\"\"\n\nimport os\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\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: academic co-authorship network — 3 research departments, 8 members each\nnp.random.seed(42)\nn_per_dept = 8\nnode_names = (\n    [f\"Ph{i + 1:02d}\" for i in range(8)] + [f\"Bi{i + 1:02d}\" for i in range(8)] + [f\"Ch{i + 1:02d}\" for i in range(8)]\n)\nn_nodes = len(node_names)\n\n# Symmetric adjacency matrix with strong within-dept and sparse cross-dept ties\nadj = np.zeros((n_nodes, n_nodes))\nfor i in range(n_nodes):\n    for j in range(i + 1, n_nodes):\n        same_dept = (i // n_per_dept) == (j // n_per_dept)\n        if same_dept:\n            weight = np.random.uniform(0.45, 1.0) if np.random.random() > 0.18 else 0.0\n        else:\n            weight = np.random.uniform(0.08, 0.38) if np.random.random() > 0.74 else 0.0\n        adj[i, j] = weight\n        adj[j, i] = weight  # undirected: fill both triangles\n\n# Long-format dataframe for Altair\nrecords = [\n    {\"from\": node_names[i], \"to\": node_names[j], \"weight\": adj[i, j]} for i in range(n_nodes) for j in range(n_nodes)\n]\ndf = pd.DataFrame(records)\n\n# Plot\nTITLE = \"heatmap-adjacency · altair · anyplot.ai\"\n\nchart = (\n    alt.Chart(df)\n    .mark_rect()\n    .encode(\n        x=alt.X(\"from:O\", sort=node_names, axis=alt.Axis(title=\"Researcher\")),\n        y=alt.Y(\"to:O\", sort=node_names, axis=alt.Axis(title=\"Researcher\")),\n        color=alt.Color(\n            \"weight:Q\",\n            scale=alt.Scale(scheme=\"viridis\", domain=[0, 1]),\n            legend=alt.Legend(title=\"Collab. Strength\", gradientLength=300, gradientThickness=22),\n        ),\n        tooltip=[\n            alt.Tooltip(\"from:O\", title=\"From\"),\n            alt.Tooltip(\"to:O\", title=\"To\"),\n            alt.Tooltip(\"weight:Q\", title=\"Strength\", format=\".2f\"),\n        ],\n    )\n    .properties(width=1100, height=1100, title=TITLE, background=PAGE_BG)\n    .configure_title(fontSize=28, color=INK)\n    .configure_axis(\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        gridOpacity=0.0,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        labelFontSize=16,\n        titleFontSize=22,\n    )\n    .configure_axisX(labelAngle=-45)\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT)\n    .configure_legend(\n        fillColor=ELEVATED_BG,\n        strokeColor=INK_SOFT,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        titleFontSize=18,\n        labelFontSize=16,\n    )\n)\n\n# Save\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}