|
1 | 1 | """ pyplots.ai |
2 | 2 | heatmap-basic: Basic Heatmap |
3 | | -Library: matplotlib 3.10.8 | Python 3.13.11 |
4 | | -Quality: 94/100 | Created: 2025-12-23 |
| 3 | +Library: matplotlib 3.10.8 | Python 3.14.3 |
| 4 | +Quality: 90/100 | Updated: 2026-02-16 |
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import matplotlib.pyplot as plt |
8 | 8 | import numpy as np |
| 9 | +from matplotlib.colors import TwoSlopeNorm |
9 | 10 |
|
10 | 11 |
|
11 | | -# Data - correlation-like matrix with meaningful labels |
12 | | -np.random.seed(42) |
13 | | -categories = ["Sales", "Marketing", "Support", "Dev", "HR", "Finance", "Ops", "Legal"] |
14 | | -n = len(categories) |
| 12 | +# Data - department correlation matrix with clear clustering patterns |
| 13 | +# Grouped: Revenue (Sales, Marketing, Finance) cluster positively; |
| 14 | +# Technical (Dev, Ops, Support) cluster positively; HR & Legal are distinct |
| 15 | +departments = ["Sales", "Marketing", "Finance", "Dev", "Ops", "Support", "HR", "Legal"] |
| 16 | +n = len(departments) |
15 | 17 |
|
16 | | -# Generate a correlation-like symmetric matrix |
17 | | -raw = np.random.randn(n, n) |
18 | | -data = (raw + raw.T) / 2 # Make symmetric |
19 | | -np.fill_diagonal(data, 1) # Perfect correlation on diagonal |
20 | | -data = np.clip(data, -1, 1) # Clip to valid correlation range |
| 18 | +data = np.array( |
| 19 | + [ |
| 20 | + [1.00, 0.82, 0.61, 0.12, 0.44, 0.35, -0.15, 0.08], # Sales |
| 21 | + [0.82, 1.00, 0.48, 0.18, 0.30, 0.28, 0.10, -0.20], # Marketing |
| 22 | + [0.61, 0.48, 1.00, -0.65, 0.05, -0.38, 0.30, 0.40], # Finance |
| 23 | + [0.12, 0.18, -0.65, 1.00, 0.42, 0.55, -0.10, -0.30], # Dev |
| 24 | + [0.44, 0.30, 0.05, 0.42, 1.00, 0.60, -0.08, 0.20], # Ops |
| 25 | + [0.35, 0.28, -0.38, 0.55, 0.60, 1.00, 0.22, 0.15], # Support |
| 26 | + [-0.15, 0.10, 0.30, -0.10, -0.08, 0.22, 1.00, 0.52], # HR |
| 27 | + [0.08, -0.20, 0.40, -0.30, 0.20, 0.15, 0.52, 1.00], # Legal |
| 28 | + ] |
| 29 | +) |
21 | 30 |
|
22 | | -# Create plot (3600x3600 px - square format best for heatmaps) |
| 31 | +# Plot - square figure for 3600x3600 at 300 dpi |
23 | 32 | fig, ax = plt.subplots(figsize=(12, 12)) |
24 | 33 |
|
25 | | -# Heatmap with diverging colormap for positive/negative values |
26 | | -im = ax.imshow(data, cmap="RdBu_r", vmin=-1, vmax=1, aspect="equal") |
| 34 | +# Heatmap with RdBu_r - perceptually uniform and colorblind-friendly diverging map |
| 35 | +norm = TwoSlopeNorm(vmin=-1, vcenter=0, vmax=1) |
| 36 | +im = ax.imshow(data, cmap="RdBu_r", norm=norm, aspect="equal") |
27 | 37 |
|
28 | | -# Add colorbar |
29 | | -cbar = fig.colorbar(im, ax=ax, shrink=0.8, pad=0.02) |
30 | | -cbar.ax.tick_params(labelsize=16) |
31 | | -cbar.set_label("Correlation Coefficient", fontsize=18) |
| 38 | +# Remove spines for a modern look |
| 39 | +for spine in ax.spines.values(): |
| 40 | + spine.set_visible(False) |
| 41 | + |
| 42 | +# Cell separation via minor-tick grid lines |
| 43 | +ax.set_xticks(np.arange(n + 1) - 0.5, minor=True) |
| 44 | +ax.set_yticks(np.arange(n + 1) - 0.5, minor=True) |
| 45 | +ax.grid(which="minor", color="white", linewidth=2) |
| 46 | +ax.tick_params(which="minor", bottom=False, left=False) |
32 | 47 |
|
33 | | -# Set ticks and labels |
| 48 | +# Tick labels |
34 | 49 | ax.set_xticks(np.arange(n)) |
35 | 50 | ax.set_yticks(np.arange(n)) |
36 | | -ax.set_xticklabels(categories, fontsize=16, rotation=45, ha="right") |
37 | | -ax.set_yticklabels(categories, fontsize=16) |
| 51 | +ax.set_xticklabels(departments, fontsize=16, rotation=45, ha="right") |
| 52 | +ax.set_yticklabels(departments, fontsize=16) |
| 53 | +ax.tick_params(axis="both", length=0) |
38 | 54 |
|
39 | | -# Add value annotations in cells |
| 55 | +# Colorbar |
| 56 | +cbar = fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04, aspect=30) |
| 57 | +cbar.ax.tick_params(labelsize=16) |
| 58 | +cbar.set_label("Correlation Coefficient", fontsize=18, labelpad=12) |
| 59 | +cbar.outline.set_visible(False) |
| 60 | + |
| 61 | +# Cell annotations - adaptive color and emphasis on strong correlations |
40 | 62 | for i in range(n): |
41 | 63 | for j in range(n): |
42 | | - value = data[i, j] |
43 | | - # Use white text on dark cells, black on light cells |
44 | | - text_color = "white" if abs(value) > 0.5 else "black" |
45 | | - ax.text(j, i, f"{value:.2f}", ha="center", va="center", fontsize=14, color=text_color, fontweight="bold") |
46 | | - |
47 | | -# Labels and title |
48 | | -ax.set_xlabel("Department", fontsize=20) |
49 | | -ax.set_ylabel("Department", fontsize=20) |
50 | | -ax.set_title("heatmap-basic · matplotlib · pyplots.ai", fontsize=24, pad=15) |
| 64 | + val = data[i, j] |
| 65 | + strong = abs(val) >= 0.6 and i != j |
| 66 | + ax.text( |
| 67 | + j, |
| 68 | + i, |
| 69 | + f"{val:.2f}", |
| 70 | + ha="center", |
| 71 | + va="center", |
| 72 | + fontsize=16 if strong else 13, |
| 73 | + fontweight="bold" if strong else "medium", |
| 74 | + color="white" if abs(val) > 0.55 else "#333333", |
| 75 | + ) |
| 76 | + |
| 77 | +# Title and differentiated axis labels |
| 78 | +ax.set_title("heatmap-basic · matplotlib · pyplots.ai", fontsize=24, fontweight="medium", pad=20) |
| 79 | +ax.set_ylabel("Department (row)", fontsize=20, labelpad=12) |
| 80 | +ax.set_xlabel("Department (column)", fontsize=20, labelpad=12) |
51 | 81 |
|
52 | 82 | plt.tight_layout() |
53 | 83 | plt.savefig("plot.png", dpi=300, bbox_inches="tight") |
0 commit comments