Skip to content

Commit 8023f9f

Browse files
update(arc-basic): plotly — comprehensive quality review (#4369)
## Summary Updated **plotly** implementation for **arc-basic**. **Changes:** Comprehensive quality review and update ### Changes - Updated implementation with improved code quality and visual design ## Test Plan - [x] Preview images uploaded to GCS staging - [x] Implementation file passes ruff format/check - [x] Metadata YAML updated with current versions - [ ] Automated review triggered --- Generated with [Claude Code](https://claude.com/claude-code) `/update` command --------- 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 ce7c88b commit 8023f9f

2 files changed

Lines changed: 302 additions & 165 deletions

File tree

Lines changed: 139 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
""" pyplots.ai
22
arc-basic: Basic Arc Diagram
3-
Library: plotly 6.5.0 | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-23
3+
Library: plotly 6.5.2 | Python 3.14.3
4+
Quality: 90/100 | Updated: 2026-02-23
55
"""
66

77
import numpy as np
@@ -13,102 +13,190 @@
1313
n_nodes = len(nodes)
1414

1515
# Edges: pairs of (source_idx, target_idx, weight)
16-
# Demonstrates short-range and long-range connections with varying weights
1716
edges = [
18-
(0, 1, 3), # Alice-Bob (neighbors, strong connection)
19-
(0, 3, 2), # Alice-David (medium distance)
17+
(0, 1, 3), # Alice-Bob (neighbors, strong)
18+
(0, 3, 2), # Alice-David
2019
(1, 2, 2), # Bob-Carol
21-
(2, 4, 3), # Carol-Eve
20+
(2, 4, 3), # Carol-Eve (strong)
2221
(3, 5, 2), # David-Frank
2322
(4, 6, 2), # Eve-Grace
24-
(5, 7, 3), # Frank-Henry
25-
(0, 8, 1), # Alice-Iris (long arc)
26-
(2, 9, 2), # Carol-Jack (long arc)
27-
(1, 4, 2), # Bob-Eve (medium arc)
28-
(3, 7, 1), # David-Henry (long arc)
23+
(5, 7, 3), # Frank-Henry (strong)
24+
(0, 8, 1), # Alice-Iris (long, weak)
25+
(2, 9, 2), # Carol-Jack (long)
26+
(1, 4, 2), # Bob-Eve
27+
(3, 7, 1), # David-Henry (long, weak)
2928
(6, 9, 2), # Grace-Jack
3029
]
3130

3231
# Node positions along horizontal axis
3332
x_positions = np.linspace(0, 10, n_nodes)
3433

34+
# Weight-based styling: color intensity, opacity, and width encode connection strength
35+
arc_styles = {
36+
1: {"color": "rgba(100, 149, 194, 0.50)", "width": 3.0, "label": "Weak (1)"},
37+
2: {"color": "rgba(48, 105, 152, 0.65)", "width": 3.5, "label": "Medium (2)"},
38+
3: {"color": "rgba(20, 66, 110, 0.90)", "width": 5.0, "label": "Strong (3)"},
39+
}
40+
3541
# Create figure
3642
fig = go.Figure()
3743

44+
# Track weight per trace for interactive filtering
45+
trace_weights = []
46+
3847
# Draw arcs as smooth parabolic curves
3948
for src, tgt, weight in edges:
40-
x_src = x_positions[src]
41-
x_tgt = x_positions[tgt]
42-
43-
# Arc height proportional to distance between nodes
44-
distance = abs(tgt - src)
45-
arc_height = distance * 0.4
49+
x_src, x_tgt = x_positions[src], x_positions[tgt]
50+
arc_height = abs(tgt - src) * 0.45
4651

47-
# Create smooth arc using multiple points
4852
t = np.linspace(0, 1, 50)
4953
x_arc = x_src + t * (x_tgt - x_src)
50-
y_arc = arc_height * 4 * t * (1 - t) # Parabolic arc
51-
52-
# Line width based on weight
53-
line_width = 2 + weight * 1.5
54+
y_arc = arc_height * 4 * t * (1 - t)
5455

56+
style = arc_styles[weight]
57+
distance = abs(tgt - src)
5558
fig.add_trace(
5659
go.Scatter(
5760
x=x_arc,
5861
y=y_arc,
5962
mode="lines",
60-
line={"width": line_width, "color": "#306998"},
61-
opacity=0.6,
62-
hoverinfo="skip",
63+
line={"width": style["width"], "color": style["color"]},
64+
hovertemplate=(
65+
f"<b>{nodes[src]} \u2014 {nodes[tgt]}</b><br>"
66+
f"Weight: <b>{weight}</b> ({style['label'].split(' ')[0].lower()})<br>"
67+
f"Distance: {distance} positions<extra></extra>"
68+
),
6369
showlegend=False,
6470
)
6571
)
72+
trace_weights.append(weight)
73+
74+
# Weight legend using dummy traces
75+
for w in [3, 2, 1]:
76+
style = arc_styles[w]
77+
fig.add_trace(
78+
go.Scatter(
79+
x=[None],
80+
y=[None],
81+
mode="lines",
82+
line={"width": style["width"], "color": style["color"]},
83+
name=style["label"],
84+
showlegend=True,
85+
)
86+
)
87+
trace_weights.append(w)
88+
89+
# Compute per-node connection counts for rich hover
90+
conn_count = [0] * n_nodes
91+
for src, tgt, _ in edges:
92+
conn_count[src] += 1
93+
conn_count[tgt] += 1
6694

6795
# Draw nodes on horizontal axis
6896
fig.add_trace(
6997
go.Scatter(
7098
x=x_positions,
71-
y=[0] * n_nodes,
99+
y=np.zeros(n_nodes),
72100
mode="markers+text",
73-
marker={"size": 24, "color": "#FFD43B", "line": {"width": 3, "color": "#306998"}},
101+
marker={"size": 26, "color": "#FFD43B", "line": {"width": 2.5, "color": "#306998"}},
74102
text=nodes,
75103
textposition="bottom center",
76-
textfont={"size": 18, "color": "#306998"},
77-
hovertemplate="%{text}<extra></extra>",
104+
textfont={"size": 22, "color": "#2a2a2a"},
105+
customdata=np.array(conn_count),
106+
hovertemplate="<b>%{text}</b><br>Connections: %{customdata}<extra></extra>",
78107
showlegend=False,
79108
)
80109
)
110+
trace_weights.append(0) # 0 = always visible
81111

82-
# Draw horizontal baseline
83-
fig.add_trace(
84-
go.Scatter(
85-
x=[x_positions[0] - 0.3, x_positions[-1] + 0.3],
86-
y=[0, 0],
87-
mode="lines",
88-
line={"width": 3, "color": "#888888"},
89-
hoverinfo="skip",
90-
showlegend=False,
91-
)
112+
# Subtle horizontal baseline
113+
fig.add_shape(
114+
type="line", x0=x_positions[0] - 0.3, x1=x_positions[-1] + 0.3, y0=0, y1=0, line={"width": 1.5, "color": "#CCCCCC"}
92115
)
93116

117+
# Annotate longest-range arc as focal point
118+
mid_x = (x_positions[0] + x_positions[8]) / 2
119+
peak_y = abs(8 - 0) * 0.45
120+
fig.add_annotation(
121+
x=mid_x,
122+
y=peak_y,
123+
text="longest range",
124+
showarrow=True,
125+
arrowhead=2,
126+
arrowwidth=1.5,
127+
arrowcolor="#666666",
128+
ax=55,
129+
ay=-25,
130+
font={"size": 20, "color": "#555555", "family": "Arial"},
131+
bgcolor="rgba(255, 255, 255, 0.7)",
132+
borderpad=4,
133+
)
134+
135+
# Build interactive filter buttons (Plotly-distinctive updatemenus)
136+
filter_options = [("All", {1, 2, 3}), ("Strong", {3}), ("Medium", {2}), ("Weak", {1})]
137+
buttons = []
138+
for label, keep in filter_options:
139+
visible = [True if tw == 0 else (tw in keep) for tw in trace_weights]
140+
buttons.append({"label": label, "method": "update", "args": [{"visible": visible}]})
141+
94142
# Layout
95143
fig.update_layout(
96-
title={"text": "arc-basic · plotly · pyplots.ai", "font": {"size": 32}, "x": 0.5, "xanchor": "center"},
97-
xaxis={"showgrid": False, "zeroline": False, "showticklabels": False, "title": None, "range": [-0.5, 10.5]},
98-
yaxis={
99-
"showgrid": False,
100-
"zeroline": False,
101-
"showticklabels": False,
102-
"title": None,
103-
"range": [-1.5, 4],
104-
"scaleanchor": "x",
105-
"scaleratio": 0.5,
144+
title={
145+
"text": (
146+
"arc-basic \u00b7 plotly \u00b7 pyplots.ai"
147+
"<br><span style='font-size:18px;color:#777777;font-weight:normal'>"
148+
"Character interactions in a story narrative</span>"
149+
),
150+
"font": {"size": 30, "color": "#2a2a2a"},
151+
"x": 0.5,
152+
"xanchor": "center",
153+
"y": 0.97,
106154
},
155+
xaxis={"showgrid": False, "zeroline": False, "showticklabels": False, "showline": False, "range": [-0.5, 10.5]},
156+
yaxis={"showgrid": False, "zeroline": False, "showticklabels": False, "showline": False, "range": [-0.7, 4.1]},
157+
hovermode="closest",
158+
hoverlabel={"bgcolor": "white", "font_size": 16, "font_color": "#306998", "bordercolor": "#306998"},
107159
template="plotly_white",
108160
plot_bgcolor="white",
109-
margin={"l": 50, "r": 50, "t": 100, "b": 80},
161+
paper_bgcolor="white",
162+
margin={"l": 30, "r": 30, "t": 90, "b": 30},
163+
legend={
164+
"title": {"text": "Connection Strength", "font": {"size": 18, "color": "#2a2a2a"}},
165+
"font": {"size": 16},
166+
"x": 0.98,
167+
"y": 0.98,
168+
"xanchor": "right",
169+
"yanchor": "top",
170+
"bgcolor": "rgba(255, 255, 255, 0.85)",
171+
"bordercolor": "#CCCCCC",
172+
"borderwidth": 1,
173+
},
174+
updatemenus=[
175+
{
176+
"type": "buttons",
177+
"direction": "right",
178+
"x": 0.02,
179+
"y": 0.98,
180+
"xanchor": "left",
181+
"yanchor": "top",
182+
"buttons": buttons,
183+
"showactive": True,
184+
"bgcolor": "rgba(255, 255, 255, 0.85)",
185+
"bordercolor": "#CCCCCC",
186+
"font": {"size": 14, "color": "#306998"},
187+
"pad": {"r": 8, "t": 8},
188+
}
189+
],
110190
)
111191

112192
# Save outputs
113193
fig.write_image("plot.png", width=1600, height=900, scale=3)
114-
fig.write_html("plot.html", include_plotlyjs="cdn")
194+
fig.write_html(
195+
"plot.html",
196+
include_plotlyjs="cdn",
197+
config={
198+
"displaylogo": False,
199+
"modeBarButtonsToRemove": ["lasso2d", "select2d"],
200+
"toImageButtonOptions": {"width": 4800, "height": 2700, "scale": 1},
201+
},
202+
)

0 commit comments

Comments
 (0)