{"spec_id":"heatmap-correlation","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nheatmap-correlation: Correlation Matrix Heatmap\nLibrary: plotly 6.9.0 | Python 3.13.15\nQuality: 94/100 | Updated: 2026-08-18\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Imprint diverging colormap — matte red (negative) -> theme midpoint -> blue (positive)\nmidpoint = PAGE_BG\nimprint_div = [[0.0, \"#AE3030\"], [0.5, midpoint], [1.0, \"#4467A3\"]]\n\n\ndef hex_to_rgb(hex_color):\n    hex_color = hex_color.lstrip(\"#\")\n    return tuple(int(hex_color[i : i + 2], 16) for i in (0, 2, 4))\n\n\ndef cell_text_color(r, mid_rgb):\n    # Re-derive the interpolated cell color to pick a legibly-contrasting text\n    # color — the midpoint stop is theme-adaptive, so a fixed threshold on |r|\n    # would pick the wrong tone for one of the two themes.\n    red_rgb, blue_rgb = hex_to_rgb(\"#AE3030\"), hex_to_rgb(\"#4467A3\")\n    position = (r + 1) / 2\n    lo, hi, t = (red_rgb, mid_rgb, position / 0.5) if position <= 0.5 else (mid_rgb, blue_rgb, (position - 0.5) / 0.5)\n    cell_rgb = [lo[k] + (hi[k] - lo[k]) * t for k in range(3)]\n    luminance = 0.299 * cell_rgb[0] + 0.587 * cell_rgb[1] + 0.114 * cell_rgb[2]\n    return \"#1A1A17\" if luminance > 140 else \"#FAF8F1\"\n\n\n# Data - Healthcare metrics correlation matrix\nnp.random.seed(42)\nvariables = [\n    \"Heart Rate\",\n    \"Blood Pressure\",\n    \"Cholesterol\",\n    \"BMI\",\n    \"Sleep Hours\",\n    \"Exercise (hrs)\",\n    \"Stress Level\",\n    \"Resting O2\",\n]\n\n# Create realistic correlation matrix with meaningful health relationships\nn_vars = len(variables)\nbase = np.random.randn(200, n_vars)\n\n# Add realistic correlations based on health domain knowledge\nbase[:, 1] = base[:, 0] * 0.65 + np.random.randn(200) * 0.4  # BP ~ Heart Rate\nbase[:, 2] = base[:, 0] * 0.5 + base[:, 3] * 0.6 + np.random.randn(200) * 0.4  # Cholesterol\nbase[:, 3] = np.random.randn(200)  # BMI (independent)\nbase[:, 4] = -base[:, 6] * 0.7 + np.random.randn(200) * 0.4  # Sleep ~ -Stress\nbase[:, 5] = (\n    -base[:, 0] * 0.5 - base[:, 6] * 0.4 + np.random.randn(200) * 0.5\n)  # Exercise inversely related to HR and Stress\nbase[:, 6] = np.random.randn(200)  # Stress (independent)\nbase[:, 7] = base[:, 4] * 0.6 + np.random.randn(200) * 0.5  # O2 ~ Sleep\n\n# Calculate correlation matrix\ncorrelation_matrix = np.corrcoef(base.T)\n\n# Mask the strict upper triangle so each unique pair is shown once\nmask = np.triu(np.ones_like(correlation_matrix, dtype=bool), k=1)\nmasked_corr = np.where(mask, np.nan, correlation_matrix)\n\n# Numeric axis positions keep shape placement simple (see strong-pair highlight below)\npositions = list(range(n_vars))\nmid_rgb = hex_to_rgb(midpoint)\n\n# Cell annotations — text color follows the actual interpolated cell color so it\n# stays legible whether that cell renders light (near the theme midpoint) or a\n# fully saturated red/blue (near the fixed scale endpoints).\nannotations = []\nfor i in range(n_vars):\n    for j in range(n_vars):\n        if mask[i, j]:\n            continue\n        r = correlation_matrix[i, j]\n        annotations.append(\n            {\n                \"x\": j,\n                \"y\": i,\n                \"text\": f\"{r:.2f}\",\n                \"showarrow\": False,\n                \"font\": {\"size\": 11, \"color\": cell_text_color(r, mid_rgb)},\n            }\n        )\n\n# Rich hover text interpreting correlation strength and direction\nhover_text = []\nfor i in range(n_vars):\n    row = []\n    for j in range(n_vars):\n        if mask[i, j]:\n            row.append(\"\")\n        else:\n            r = correlation_matrix[i, j]\n            if abs(r) >= 0.7:\n                strength = \"Strong\"\n            elif abs(r) >= 0.4:\n                strength = \"Moderate\"\n            else:\n                strength = \"Weak\"\n            direction = \"positive\" if r > 0 else \"negative\" if r < 0 else \"none\"\n            row.append(\n                f\"<b>{variables[i]}</b> vs <b>{variables[j]}</b><br>\"\n                f\"Correlation: <b>{r:.3f}</b><br>\"\n                f\"Strength: {strength} {direction}\"\n            )\n    hover_text.append(row)\n\n# Heatmap — Imprint diverging scale, thin page-background gaps replace axis gridlines\nfig = go.Figure(\n    data=go.Heatmap(\n        z=masked_corr,\n        x=positions,\n        y=positions,\n        colorscale=imprint_div,\n        zmin=-1,\n        zmax=1,\n        xgap=2,\n        ygap=2,\n        colorbar={\n            \"title\": {\"text\": \"Pearson r\", \"font\": {\"size\": 11, \"color\": INK}},\n            \"tickfont\": {\"size\": 9, \"color\": INK_SOFT},\n            \"thickness\": 15,\n            \"len\": 0.8,\n            \"tickvals\": [-1, -0.5, 0, 0.5, 1],\n            \"outlinewidth\": 0,\n        },\n        hoverongaps=False,\n        hovertemplate=\"%{customdata}<extra></extra>\",\n        customdata=hover_text,\n    )\n)\n\n# Highlight strong pairs (|r| >= 0.7, off-diagonal) with an outlined cell border —\n# a real data-driven emphasis, not a simulated interaction. The stroke color\n# contrasts against PAGE_BG (the xgap/ygap color the border sits on top of), not\n# against the cell fill — otherwise a fill that interpolates near PAGE_BG makes\n# the border blend into the surrounding gap and disappear.\nfor i in range(n_vars):\n    for j in range(i):\n        r = correlation_matrix[i, j]\n        if abs(r) >= 0.7:\n            fig.add_shape(\n                type=\"rect\",\n                x0=j - 0.5,\n                x1=j + 0.5,\n                y0=i - 0.5,\n                y1=i + 0.5,\n                line={\"color\": INK, \"width\": 2.5},\n                fillcolor=\"rgba(0,0,0,0)\",\n                layer=\"above\",\n            )\n\n# Layout for 2400x2400 px (square — symmetric matrix)\nfig.update_layout(\n    autosize=False,\n    title={\n        \"text\": \"heatmap-correlation · python · plotly · anyplot.ai\",\n        \"font\": {\"size\": 18, \"color\": INK},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n    },\n    xaxis={\n        \"title\": {\"text\": \"Health Metrics\", \"font\": {\"size\": 13, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"tickmode\": \"array\",\n        \"tickvals\": positions,\n        \"ticktext\": variables,\n        \"side\": \"bottom\",\n        \"tickangle\": 45,\n        \"showgrid\": False,\n        \"zeroline\": False,\n        \"showline\": False,\n    },\n    yaxis={\n        \"title\": {\"text\": \"Health Metrics\", \"font\": {\"size\": 13, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"tickmode\": \"array\",\n        \"tickvals\": positions,\n        \"ticktext\": variables,\n        \"autorange\": \"reversed\",\n        \"showgrid\": False,\n        \"zeroline\": False,\n        \"showline\": False,\n    },\n    annotations=annotations,\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    margin={\"l\": 70, \"r\": 30, \"t\": 60, \"b\": 90},\n    width=600,\n    height=600,\n)\n\n# Save as PNG and HTML with theme-suffixed filenames\nfig.write_image(f\"plot-{THEME}.png\", width=600, height=600, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}