|
| 1 | +""" pyplots.ai |
| 2 | +network-directed: Directed Network Graph |
| 3 | +Library: seaborn 0.13.2 | Python 3.13.11 |
| 4 | +Quality: 91/100 | Created: 2025-12-30 |
| 5 | +""" |
| 6 | + |
| 7 | +import matplotlib.patches as mpatches |
| 8 | +import matplotlib.pyplot as plt |
| 9 | +import numpy as np |
| 10 | +import pandas as pd |
| 11 | +import seaborn as sns |
| 12 | + |
| 13 | + |
| 14 | +# Set seaborn style |
| 15 | +sns.set_theme(style="white") |
| 16 | + |
| 17 | +np.random.seed(42) |
| 18 | + |
| 19 | +# Software module dependencies data with improved circular-hierarchical layout |
| 20 | +# Positions organized in concentric layers for better visual balance |
| 21 | +modules = { |
| 22 | + "app": {"group": "core", "pos": (0.5, 0.88)}, |
| 23 | + "config": {"group": "core", "pos": (0.18, 0.70)}, |
| 24 | + "api": {"group": "services", "pos": (0.50, 0.68)}, |
| 25 | + "auth": {"group": "services", "pos": (0.82, 0.70)}, |
| 26 | + "db": {"group": "data", "pos": (0.28, 0.45)}, |
| 27 | + "cache": {"group": "data", "pos": (0.72, 0.45)}, |
| 28 | + "log": {"group": "utils", "pos": (0.15, 0.22)}, |
| 29 | + "valid": {"group": "utils", "pos": (0.50, 0.22)}, |
| 30 | + "router": {"group": "services", "pos": (0.85, 0.22)}, |
| 31 | + "model": {"group": "data", "pos": (0.32, 0.05)}, |
| 32 | + "mware": {"group": "services", "pos": (0.68, 0.05)}, |
| 33 | +} |
| 34 | + |
| 35 | +# Directed edges with weights (source → target, weight) |
| 36 | +# Weight represents dependency strength/frequency |
| 37 | +edges = [ |
| 38 | + ("app", "config", 3), |
| 39 | + ("app", "db", 5), |
| 40 | + ("app", "api", 5), |
| 41 | + ("app", "auth", 4), |
| 42 | + ("api", "valid", 4), |
| 43 | + ("api", "router", 3), |
| 44 | + ("api", "log", 2), |
| 45 | + ("auth", "cache", 4), |
| 46 | + ("auth", "db", 3), |
| 47 | + ("auth", "log", 2), |
| 48 | + ("db", "config", 3), |
| 49 | + ("db", "log", 1), |
| 50 | + ("cache", "config", 2), |
| 51 | + ("router", "valid", 3), |
| 52 | + ("router", "mware", 2), |
| 53 | + ("model", "db", 4), |
| 54 | + ("model", "valid", 3), |
| 55 | + ("mware", "auth", 3), |
| 56 | + ("mware", "log", 1), |
| 57 | +] |
| 58 | + |
| 59 | +# Color palette using seaborn |
| 60 | +groups = ["core", "data", "services", "utils"] |
| 61 | +palette = sns.color_palette(["#306998", "#FFD43B", "#4CAF50", "#E57373"], n_colors=4) |
| 62 | +group_colors = dict(zip(groups, palette, strict=True)) |
| 63 | + |
| 64 | +# Prepare node data as DataFrame for seaborn |
| 65 | +node_data = [] |
| 66 | +for name, data in modules.items(): |
| 67 | + node_data.append({"name": name, "x": data["pos"][0], "y": data["pos"][1], "group": data["group"]}) |
| 68 | +nodes_df = pd.DataFrame(node_data) |
| 69 | + |
| 70 | +# Create figure |
| 71 | +fig, ax = plt.subplots(figsize=(16, 9)) |
| 72 | + |
| 73 | +# Draw all edges with directed arrows - thickness based on weight |
| 74 | +for source, target, weight in edges: |
| 75 | + start = modules[source]["pos"] |
| 76 | + end = modules[target]["pos"] |
| 77 | + |
| 78 | + # Calculate direction for shortening arrows |
| 79 | + dx = end[0] - start[0] |
| 80 | + dy = end[1] - start[1] |
| 81 | + length = np.sqrt(dx**2 + dy**2) |
| 82 | + dx_norm = dx / length if length > 0 else 0 |
| 83 | + dy_norm = dy / length if length > 0 else 0 |
| 84 | + |
| 85 | + # Shorten to avoid overlapping nodes |
| 86 | + shrink = 0.055 |
| 87 | + start_adj = (start[0] + dx_norm * shrink, start[1] + dy_norm * shrink) |
| 88 | + end_adj = (end[0] - dx_norm * shrink, end[1] - dy_norm * shrink) |
| 89 | + |
| 90 | + # Line width based on edge weight (1-5 scale to 1.5-4.0 linewidth) |
| 91 | + line_width = 1.0 + weight * 0.6 |
| 92 | + |
| 93 | + # Draw arrow using FancyArrowPatch with weight-based thickness |
| 94 | + arrow = mpatches.FancyArrowPatch( |
| 95 | + start_adj, |
| 96 | + end_adj, |
| 97 | + connectionstyle="arc3,rad=0.1", |
| 98 | + arrowstyle="->,head_length=8,head_width=5", |
| 99 | + color="#555555", |
| 100 | + alpha=0.5 + weight * 0.08, |
| 101 | + linewidth=line_width, |
| 102 | + zorder=1, |
| 103 | + ) |
| 104 | + ax.add_patch(arrow) |
| 105 | + |
| 106 | +# Draw nodes using seaborn's scatterplot |
| 107 | +sns.scatterplot( |
| 108 | + data=nodes_df, |
| 109 | + x="x", |
| 110 | + y="y", |
| 111 | + hue="group", |
| 112 | + palette=group_colors, |
| 113 | + s=3000, |
| 114 | + edgecolor="white", |
| 115 | + linewidth=3, |
| 116 | + legend=False, |
| 117 | + ax=ax, |
| 118 | + zorder=2, |
| 119 | +) |
| 120 | + |
| 121 | +# Add node labels with larger font size (14-16pt per feedback) |
| 122 | +for name, data in modules.items(): |
| 123 | + ax.text( |
| 124 | + data["pos"][0], |
| 125 | + data["pos"][1], |
| 126 | + name, |
| 127 | + ha="center", |
| 128 | + va="center", |
| 129 | + fontsize=14, |
| 130 | + fontweight="bold", |
| 131 | + color="white" if data["group"] != "data" else "#333333", |
| 132 | + zorder=3, |
| 133 | + ) |
| 134 | + |
| 135 | +# Create legend with edge weight indicator |
| 136 | +legend_handles = [ |
| 137 | + plt.scatter([], [], c=[group_colors[g]], s=400, label=g.capitalize(), edgecolors="white", linewidths=2) |
| 138 | + for g in groups |
| 139 | +] |
| 140 | +# Add edge weight legend entries |
| 141 | +legend_handles.append(plt.Line2D([0], [0], color="#555555", linewidth=1.6, label="Weak (1)", alpha=0.6)) |
| 142 | +legend_handles.append(plt.Line2D([0], [0], color="#555555", linewidth=4.0, label="Strong (5)", alpha=0.9)) |
| 143 | + |
| 144 | +ax.legend( |
| 145 | + handles=legend_handles, |
| 146 | + loc="upper left", |
| 147 | + fontsize=14, |
| 148 | + title="Module Type / Edge Weight", |
| 149 | + title_fontsize=16, |
| 150 | + framealpha=0.95, |
| 151 | + markerscale=1.0, |
| 152 | + borderpad=1, |
| 153 | +) |
| 154 | + |
| 155 | +# Styling |
| 156 | +ax.set_title("network-directed · seaborn · pyplots.ai", fontsize=24, fontweight="bold", pad=20) |
| 157 | +ax.set_xlim(-0.02, 1.02) |
| 158 | +ax.set_ylim(-0.08, 1.02) |
| 159 | +ax.axis("off") |
| 160 | +ax.set_facecolor("#fafafa") |
| 161 | + |
| 162 | +plt.tight_layout() |
| 163 | +plt.savefig("plot.png", dpi=300, bbox_inches="tight", facecolor="white") |
0 commit comments