Skip to content

Commit 287b688

Browse files
update(arc-basic): altair — comprehensive quality review (#4371)
## Summary Updated **altair** 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 a6e029c commit 287b688

2 files changed

Lines changed: 222 additions & 148 deletions

File tree

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

77
import altair as alt
@@ -15,9 +15,8 @@
1515
nodes = ["Alice", "Bob", "Carol", "David", "Eve", "Frank", "Grace", "Henry", "Iris", "Jack"]
1616
n_nodes = len(nodes)
1717

18-
# Edges: pairs of connected nodes with weights
1918
edges = [
20-
(0, 1, 3), # Alice-Bob (strong connection)
19+
(0, 1, 3), # Alice-Bob (strong)
2120
(0, 3, 2), # Alice-David
2221
(1, 2, 2), # Bob-Carol
2322
(2, 4, 1), # Carol-Eve
@@ -36,68 +35,106 @@
3635

3736
# Node positions along x-axis
3837
x_positions = np.linspace(0, 100, n_nodes)
39-
y_baseline = 10
38+
y_baseline = 0
4039

41-
# Create node dataframe
42-
nodes_df = pd.DataFrame({"x": x_positions, "y": [y_baseline] * n_nodes, "name": nodes})
40+
# Node dataframe with connection count for sizing
41+
connection_count = [0] * n_nodes
42+
for s, e, w in edges:
43+
connection_count[s] += w
44+
connection_count[e] += w
4345

44-
# Create arc paths - each arc as a series of points following a semicircle
46+
nodes_df = pd.DataFrame({"x": x_positions, "y": [y_baseline] * n_nodes, "name": nodes, "connections": connection_count})
47+
48+
# Build arc paths as semicircular curves
4549
arc_data = []
4650
points_per_arc = 50
51+
max_span = max(abs(e - s) for s, e, _ in edges)
4752

4853
for edge_id, (start, end, weight) in enumerate(edges):
4954
x_start = x_positions[start]
5055
x_end = x_positions[end]
56+
span = abs(end - start)
57+
height = 7 * span
5158

52-
# Arc height proportional to distance between nodes
53-
distance = abs(end - start)
54-
height = 8 * distance
55-
56-
# Generate points along a semicircle arc
5759
angles = np.linspace(0, np.pi, points_per_arc)
58-
5960
x_center = (x_start + x_end) / 2
6061
radius_x = abs(x_end - x_start) / 2
6162
radius_y = height / 2
6263

64+
pair = f"{nodes[start]}{nodes[end]}"
6365
for i, angle in enumerate(angles):
6466
arc_data.append(
6567
{
6668
"edge_id": edge_id,
6769
"x": x_center - radius_x * np.cos(angle),
6870
"y": y_baseline + radius_y * np.sin(angle),
6971
"weight": weight,
72+
"pair": pair,
7073
"order": i,
7174
}
7275
)
7376

7477
arcs_df = pd.DataFrame(arc_data)
7578

76-
# Create arc chart with lines
79+
# Y-domain: tight around data for better canvas use
80+
max_arc_height = 7 * max_span / 2
81+
y_domain = [-5, max_arc_height + 4]
82+
x_domain = [-4, 104]
83+
84+
# Hover selection for interactive arc highlighting (HTML export)
85+
hover = alt.selection_point(on="pointerover", empty=False, fields=["edge_id"])
86+
87+
# Arcs: weight drives color, thickness, and opacity for visual hierarchy
7788
arcs = (
7889
alt.Chart(arcs_df)
79-
.mark_line(strokeWidth=2, opacity=0.6)
90+
.mark_line()
8091
.encode(
81-
x=alt.X("x:Q", axis=None),
82-
y=alt.Y("y:Q", axis=None),
92+
x=alt.X("x:Q", axis=None, scale=alt.Scale(domain=x_domain)),
93+
y=alt.Y("y:Q", axis=None, scale=alt.Scale(domain=y_domain)),
8394
detail="edge_id:N",
84-
strokeWidth=alt.StrokeWidth("weight:Q", scale=alt.Scale(domain=[1, 3], range=[2, 6]), legend=None),
85-
color=alt.value("#306998"),
95+
strokeWidth=alt.StrokeWidth(
96+
"weight:Q",
97+
scale=alt.Scale(domain=[1, 3], range=[1.5, 6]),
98+
legend=alt.Legend(
99+
title="Interaction Strength",
100+
titleFontSize=16,
101+
labelFontSize=16,
102+
orient="top-right",
103+
offset=10,
104+
values=[1, 2, 3],
105+
symbolStrokeWidth=3,
106+
labelExpr="datum.value == 1 ? 'Weak' : datum.value == 2 ? 'Moderate' : 'Strong'",
107+
),
108+
),
109+
strokeOpacity=alt.condition(
110+
hover,
111+
alt.value(0.95),
112+
alt.StrokeOpacity("weight:Q", scale=alt.Scale(domain=[1, 3], range=[0.3, 0.8]), legend=None),
113+
),
114+
color=alt.Color(
115+
"weight:Q", scale=alt.Scale(domain=[1, 2, 3], range=["#7daed4", "#306998", "#152d4a"]), legend=None
116+
),
117+
tooltip=[alt.Tooltip("pair:N", title="Connection"), alt.Tooltip("weight:Q", title="Strength")],
86118
)
87-
.properties(width=1600, height=900)
119+
.add_params(hover)
88120
)
89121

90-
# Create node points
122+
# Nodes: size proportional to total connection weight
91123
node_points = (
92124
alt.Chart(nodes_df)
93-
.mark_circle(size=600, color="#FFD43B", stroke="#306998", strokeWidth=3)
94-
.encode(x=alt.X("x:Q", axis=None), y=alt.Y("y:Q", axis=None))
125+
.mark_circle(color="#FFD43B", stroke="#152d4a", strokeWidth=2.5)
126+
.encode(
127+
x=alt.X("x:Q", axis=None, scale=alt.Scale(domain=x_domain)),
128+
y=alt.Y("y:Q", axis=None, scale=alt.Scale(domain=y_domain)),
129+
size=alt.Size("connections:Q", scale=alt.Scale(domain=[2, 11], range=[500, 1200]), legend=None),
130+
tooltip=[alt.Tooltip("name:N", title="Character"), alt.Tooltip("connections:Q", title="Total Weight")],
131+
)
95132
)
96133

97-
# Create node labels
134+
# Node labels below baseline
98135
node_labels = (
99136
alt.Chart(nodes_df)
100-
.mark_text(dy=30, fontSize=18, fontWeight="bold", color="#306998")
137+
.mark_text(dy=30, fontSize=18, fontWeight="bold", color="#152d4a")
101138
.encode(x=alt.X("x:Q"), y=alt.Y("y:Q"), text="name:N")
102139
)
103140

@@ -107,11 +144,12 @@
107144
.properties(
108145
width=1600,
109146
height=900,
110-
title=alt.Title("Character Interactions · arc-basic · altair · pyplots.ai", fontSize=28, anchor="middle"),
147+
title=alt.Title("arc-basic · altair · pyplots.ai", fontSize=28, anchor="middle", offset=15),
111148
)
112149
.configure_view(strokeWidth=0)
150+
.configure_legend(strokeColor="transparent", padding=12, titleColor="#152d4a", labelColor="#333333")
113151
)
114152

115-
# Save outputs
153+
# Save
116154
chart.save("plot.png", scale_factor=3.0)
117155
chart.save("plot.html")

0 commit comments

Comments
 (0)