|
1 | 1 | """ pyplots.ai |
2 | 2 | dendrogram-basic: Basic Dendrogram |
3 | | -Library: matplotlib 3.10.8 | Python 3.13.11 |
4 | | -Quality: 91/100 | Created: 2025-12-23 |
| 3 | +Library: matplotlib 3.10.8 | Python 3.14.3 |
| 4 | +Quality: 92/100 | Updated: 2026-04-05 |
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import matplotlib.pyplot as plt |
8 | 8 | import numpy as np |
9 | | -from scipy.cluster.hierarchy import dendrogram, linkage |
| 9 | +from matplotlib.collections import LineCollection |
| 10 | +from scipy.cluster.hierarchy import dendrogram, linkage, set_link_color_palette |
10 | 11 |
|
11 | 12 |
|
12 | 13 | # Data - Iris flower measurements (4 features for 15 samples) |
13 | 14 | np.random.seed(42) |
14 | 15 |
|
15 | | -# Simulate iris-like measurements: sepal length, sepal width, petal length, petal width |
16 | | -# Three species with distinct characteristics |
17 | 16 | samples_per_species = 5 |
18 | | - |
19 | 17 | labels = [] |
20 | 18 | data = [] |
21 | 19 |
|
|
24 | 22 | labels.append(f"Setosa-{i + 1}") |
25 | 23 | data.append( |
26 | 24 | [ |
27 | | - 5.0 + np.random.randn() * 0.3, # sepal length |
28 | | - 3.4 + np.random.randn() * 0.3, # sepal width |
29 | | - 1.5 + np.random.randn() * 0.2, # petal length |
30 | | - 0.3 + np.random.randn() * 0.1, # petal width |
| 25 | + 5.0 + np.random.randn() * 0.3, |
| 26 | + 3.4 + np.random.randn() * 0.3, |
| 27 | + 1.5 + np.random.randn() * 0.2, |
| 28 | + 0.3 + np.random.randn() * 0.1, |
31 | 29 | ] |
32 | 30 | ) |
33 | 31 |
|
|
36 | 34 | labels.append(f"Versicolor-{i + 1}") |
37 | 35 | data.append( |
38 | 36 | [ |
39 | | - 5.9 + np.random.randn() * 0.4, # sepal length |
40 | | - 2.8 + np.random.randn() * 0.3, # sepal width |
41 | | - 4.3 + np.random.randn() * 0.4, # petal length |
42 | | - 1.3 + np.random.randn() * 0.2, # petal width |
| 37 | + 5.9 + np.random.randn() * 0.4, |
| 38 | + 2.8 + np.random.randn() * 0.3, |
| 39 | + 4.3 + np.random.randn() * 0.4, |
| 40 | + 1.3 + np.random.randn() * 0.2, |
43 | 41 | ] |
44 | 42 | ) |
45 | 43 |
|
|
48 | 46 | labels.append(f"Virginica-{i + 1}") |
49 | 47 | data.append( |
50 | 48 | [ |
51 | | - 6.6 + np.random.randn() * 0.5, # sepal length |
52 | | - 3.0 + np.random.randn() * 0.3, # sepal width |
53 | | - 5.5 + np.random.randn() * 0.5, # petal length |
54 | | - 2.0 + np.random.randn() * 0.3, # petal width |
| 49 | + 6.6 + np.random.randn() * 0.5, |
| 50 | + 3.0 + np.random.randn() * 0.3, |
| 51 | + 5.5 + np.random.randn() * 0.5, |
| 52 | + 2.0 + np.random.randn() * 0.3, |
55 | 53 | ] |
56 | 54 | ) |
57 | 55 |
|
|
61 | 59 | linkage_matrix = linkage(data, method="ward") |
62 | 60 |
|
63 | 61 | # Plot |
64 | | -fig, ax = plt.subplots(figsize=(16, 9)) |
| 62 | +fig, ax = plt.subplots(figsize=(16, 9), facecolor="white") |
| 63 | +ax.set_facecolor("#FAFAFA") |
| 64 | + |
| 65 | +# Custom cluster colors via set_link_color_palette (matplotlib/scipy integration) |
| 66 | +cluster_colors = ["#306998", "#D4722A", "#3A8A5C"] |
| 67 | +set_link_color_palette(cluster_colors) |
| 68 | + |
| 69 | +# Set threshold between the 2nd and 3rd highest merge distances to reveal 3 clusters |
| 70 | +sorted_distances = sorted(linkage_matrix[:, 2]) |
| 71 | +color_threshold = (sorted_distances[-2] + sorted_distances[-3]) / 2 |
65 | 72 |
|
66 | | -# Create dendrogram with custom colors |
67 | | -dendrogram( |
| 73 | +dendro = dendrogram( |
68 | 74 | linkage_matrix, |
69 | 75 | labels=labels, |
70 | 76 | ax=ax, |
71 | | - leaf_rotation=45, |
72 | | - leaf_font_size=14, |
73 | | - above_threshold_color="#306998", # Python Blue for main branches |
74 | | - color_threshold=0.7 * max(linkage_matrix[:, 2]), # Color threshold for clusters |
| 77 | + leaf_rotation=40, |
| 78 | + leaf_font_size=16, |
| 79 | + above_threshold_color="#AAAAAA", |
| 80 | + color_threshold=color_threshold, |
75 | 81 | ) |
76 | 82 |
|
| 83 | +# Post-render enhancement: adjust line widths via LineCollection traversal |
| 84 | +for child in ax.get_children(): |
| 85 | + if isinstance(child, LineCollection): |
| 86 | + child.set_linewidths(3.0) |
| 87 | + child.set_capstyle("round") |
| 88 | + child.set_joinstyle("round") |
| 89 | + |
77 | 90 | # Style |
78 | | -ax.set_xlabel("Sample", fontsize=20) |
79 | | -ax.set_ylabel("Distance (Ward)", fontsize=20) |
80 | | -ax.set_title("dendrogram-basic · matplotlib · pyplots.ai", fontsize=24) |
81 | | -ax.tick_params(axis="both", labelsize=16) |
82 | | -ax.tick_params(axis="x", labelsize=14, rotation=45) |
| 91 | +ax.set_xlabel("Iris Sample", fontsize=20, labelpad=10) |
| 92 | +ax.set_ylabel("Ward Linkage Distance", fontsize=20, labelpad=10) |
| 93 | +ax.set_title( |
| 94 | + "Iris Species Clustering · dendrogram-basic · matplotlib · pyplots.ai", |
| 95 | + fontsize=24, |
| 96 | + fontweight="medium", |
| 97 | + pad=20, |
| 98 | + color="#333333", |
| 99 | +) |
| 100 | +ax.tick_params(axis="both", labelsize=16, colors="#555555") |
| 101 | +ax.tick_params(axis="x", labelsize=16, rotation=40) |
83 | 102 |
|
84 | | -# Adjust spines for cleaner look |
85 | 103 | ax.spines["top"].set_visible(False) |
86 | 104 | ax.spines["right"].set_visible(False) |
| 105 | +ax.spines["left"].set_linewidth(0.6) |
| 106 | +ax.spines["left"].set_color("#CCCCCC") |
| 107 | +ax.spines["bottom"].set_linewidth(0.6) |
| 108 | +ax.spines["bottom"].set_color("#CCCCCC") |
87 | 109 |
|
88 | | -# Add subtle grid on y-axis only |
89 | | -ax.yaxis.grid(True, alpha=0.3, linestyle="--") |
| 110 | +# Subtle grid on y-axis only |
| 111 | +ax.yaxis.grid(True, alpha=0.15, linewidth=0.6, color="#888888") |
90 | 112 | ax.set_axisbelow(True) |
91 | 113 |
|
92 | | -plt.tight_layout() |
93 | | -plt.savefig("plot.png", dpi=300, bbox_inches="tight") |
| 114 | +plt.tight_layout(pad=1.5) |
| 115 | +plt.savefig("plot.png", dpi=300, bbox_inches="tight", facecolor="white") |
0 commit comments