Skip to content

Commit 8ce914f

Browse files
feat(seaborn): implement network-directed (#2889)
## Implementation: `network-directed` - seaborn Implements the **seaborn** version of `network-directed`. **File:** `plots/network-directed/implementations/seaborn.py` **Parent Issue:** #2858 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20608483455)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 221a02b commit 8ce914f

2 files changed

Lines changed: 191 additions & 0 deletions

File tree

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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")
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
library: seaborn
2+
specification_id: network-directed
3+
created: '2025-12-30T23:56:22Z'
4+
updated: '2025-12-31T00:12:11Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20608483455
7+
issue: 2858
8+
python_version: 3.13.11
9+
library_version: 0.13.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/network-directed/seaborn/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/network-directed/seaborn/plot_thumb.png
12+
preview_html: null
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent visual clarity with well-sized nodes and clearly visible directed arrows
17+
- Smart use of curved edges (arc3,rad=0.1) to prevent arrow overlap on bidirectional-style
18+
edges
19+
- Edge weight visualization through both line thickness and alpha transparency creates
20+
good visual hierarchy
21+
- Proper title format and professional layout with light background
22+
- Realistic software module dependency scenario with meaningful groupings
23+
- Clean code structure following KISS principles
24+
weaknesses:
25+
- Legend shows module types but edge weight legend (Weak/Strong) mentioned in code
26+
may not be rendering in final image
27+
- Node label font size at 14pt is acceptable but 16pt would improve readability
28+
at full resolution

0 commit comments

Comments
 (0)