{"spec_id":"heatmap-correlation","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nheatmap-correlation: Correlation Matrix Heatmap\nLibrary: matplotlib 3.11.1 | Python 3.13.15\nQuality: 90/100 | Updated: 2026-08-18\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom matplotlib.colors import LinearSegmentedColormap\nfrom matplotlib.patches import Rectangle\n\n\n# Theme tokens (see prompts/default-style-guide.md \"Background\" + \"Theme-adaptive Chrome\")\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nEMPHASIS_POS = \"#009E73\"  # Imprint palette position 1 — strong positive correlation\nEMPHASIS_NEG = \"#AE3030\"  # Imprint palette position 5 — strong negative correlation\n\n# Data - realistic weather-station correlation matrix\nnp.random.seed(42)\n\nvariables = [\n    \"Temperature\",\n    \"Humidity\",\n    \"Wind Speed\",\n    \"Precipitation\",\n    \"Air Pressure\",\n    \"Solar Radiation\",\n    \"UV Index\",\n    \"Cloud Cover\",\n]\nn_vars = len(variables)\n\ncorrelation_matrix = np.eye(n_vars)\ncorrelations = {\n    (0, 1): -0.82,  # Temperature - Humidity (strong negative)\n    (0, 2): 0.15,  # Temperature - Wind Speed (weak positive)\n    (0, 3): -0.48,  # Temperature - Precipitation (negative)\n    (0, 4): -0.71,  # Temperature - Air Pressure (strong negative)\n    (0, 5): 0.88,  # Temperature - Solar Radiation (strong positive)\n    (0, 6): 0.79,  # Temperature - UV Index (strong positive)\n    (0, 7): -0.65,  # Temperature - Cloud Cover (negative)\n    (1, 2): 0.35,  # Humidity - Wind Speed (weak positive)\n    (1, 3): 0.72,  # Humidity - Precipitation (strong positive)\n    (1, 4): 0.58,  # Humidity - Air Pressure (positive)\n    (1, 5): -0.84,  # Humidity - Solar Radiation (strong negative)\n    (1, 6): -0.76,  # Humidity - UV Index (strong negative)\n    (1, 7): 0.81,  # Humidity - Cloud Cover (strong positive)\n    (2, 3): 0.42,  # Wind Speed - Precipitation (positive)\n    (2, 4): -0.31,  # Wind Speed - Air Pressure (weak negative)\n    (2, 5): 0.09,  # Wind Speed - Solar Radiation (very weak)\n    (2, 6): -0.12,  # Wind Speed - UV Index (very weak negative)\n    (2, 7): 0.38,  # Wind Speed - Cloud Cover (weak positive)\n    (3, 4): -0.52,  # Precipitation - Air Pressure (negative)\n    (3, 5): -0.68,  # Precipitation - Solar Radiation (strong negative)\n    (3, 6): -0.61,  # Precipitation - UV Index (strong negative)\n    (3, 7): 0.74,  # Precipitation - Cloud Cover (strong positive)\n    (4, 5): 0.45,  # Air Pressure - Solar Radiation (positive)\n    (4, 6): 0.39,  # Air Pressure - UV Index (positive)\n    (4, 7): -0.43,  # Air Pressure - Cloud Cover (negative)\n    (5, 6): 0.85,  # Solar Radiation - UV Index (strong positive)\n    (5, 7): -0.79,  # Solar Radiation - Cloud Cover (strong negative)\n    (6, 7): -0.71,  # UV Index - Cloud Cover (strong negative)\n}\nfor (i, j), corr in correlations.items():\n    correlation_matrix[i, j] = corr\n    correlation_matrix[j, i] = corr\n\n# Mask the upper triangle so each 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# Imprint diverging colormap — matte-red (negative) through the theme midpoint to blue (positive)\nmidpoint = PAGE_BG\nimprint_div = LinearSegmentedColormap.from_list(\"imprint_div\", [\"#AE3030\", midpoint, \"#4467A3\"])\n\n# Plot — square canvas for this symmetric matrix (see default-style-guide.md \"Dimensions\")\nfig, ax = plt.subplots(figsize=(6, 6), dpi=400, facecolor=PAGE_BG, layout=\"constrained\")\nax.set_facecolor(PAGE_BG)\n\nim = ax.imshow(masked_corr, cmap=imprint_div, vmin=-1, vmax=1, aspect=\"equal\")\n\n# Subtle cell-boundary grid\nax.set_xticks(np.arange(n_vars) - 0.5, minor=True)\nax.set_yticks(np.arange(n_vars) - 0.5, minor=True)\nax.grid(which=\"minor\", color=INK_MUTED, linestyle=\"-\", linewidth=0.6, alpha=0.25)\nax.tick_params(which=\"minor\", bottom=False, left=False)\n\n# Colorbar, fixed to the full correlation range\ncbar = ax.figure.colorbar(im, ax=ax, shrink=0.74, pad=0.03)\ncbar.ax.set_ylabel(\"Correlation Coefficient\", fontsize=10, labelpad=10, color=INK)\ncbar.ax.tick_params(labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)\ncbar.outline.set_visible(False)\n\n# Ticks and axis labels\nax.set_xticks(np.arange(n_vars))\nax.set_yticks(np.arange(n_vars))\nax.set_xticklabels(variables, fontsize=9, rotation=45, ha=\"right\", rotation_mode=\"anchor\", color=INK_SOFT)\nax.set_yticklabels(variables, fontsize=9, color=INK_SOFT)\nax.tick_params(which=\"major\", bottom=False, left=False)\n\nax.set_xlabel(\"Weather Variables\", fontsize=10, labelpad=12, color=INK)\nax.set_ylabel(\"Weather Variables\", fontsize=10, labelpad=12, color=INK)\n\n# Emphasis borders on strong correlations — solid for positive, dashed for negative,\n# so the sign reads even without checking the colorbar\nfor i in range(n_vars):\n    for j in range(n_vars):\n        if not mask[i, j] and i != j and abs(correlation_matrix[i, j]) > 0.75:\n            positive = correlation_matrix[i, j] > 0\n            rect = Rectangle(\n                (j - 0.45, i - 0.45),\n                0.9,\n                0.9,\n                linewidth=1.8,\n                edgecolor=EMPHASIS_POS if positive else EMPHASIS_NEG,\n                facecolor=\"none\",\n                linestyle=\"-\" if positive else \"--\",\n                alpha=0.85,\n            )\n            ax.add_patch(rect)\n\n# Cell annotations — text color follows the actual cell luminance so it stays\n# legible against both saturated correlation colors and the near-neutral midpoint\nfor i in range(n_vars):\n    for j in range(n_vars):\n        if not mask[i, j]:\n            value = correlation_matrix[i, j]\n            r, g, b, _ = imprint_div((value + 1) / 2)\n            luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b\n            text_color = \"white\" if luminance < 0.55 else INK_SOFT\n            ax.text(j, i, f\"{value:.2f}\", ha=\"center\", va=\"center\", color=text_color, fontsize=9, fontweight=\"bold\")\n\n# Title on the full figure (not just the heatmap axes) so it stays centered over\n# the colorbar too — the square canvas is narrower than the landscape default,\n# so the mandated title needs a smaller fontsize than the 12pt landscape baseline\n# to stay clear of the colorbar's top tick label.\nfig.suptitle(\n    \"heatmap-correlation · python · matplotlib · anyplot.ai\", fontsize=11, y=0.97, fontweight=\"medium\", color=INK\n)\n\nfor spine in ax.spines.values():\n    spine.set_visible(False)\n\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}