Skip to content

Commit 25ed938

Browse files
update(bubble-basic): highcharts — comprehensive quality review and improvement (#4268)
## Summary Updated **highcharts** implementation for **bubble-basic**. **Changes:** comprehensive quality review and improvement ### Changes - Replaced generic x/y data with realistic, domain-relevant dataset - Improved visual design: white bubble edges, subtler grid, better alpha - Area-based bubble scaling per spec requirement - Meaningful axis labels with units - Enhanced library-specific feature usage - Quality self-assessment: see agent report ## 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 59b2b12 commit 25ed938

2 files changed

Lines changed: 320 additions & 164 deletions

File tree

plots/bubble-basic/implementations/highcharts.py

Lines changed: 105 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
""" pyplots.ai
22
bubble-basic: Basic Bubble Chart
3-
Library: highcharts unknown | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-23
3+
Library: highcharts 1.10.3 | Python 3.14.3
4+
Quality: 91/100 | Updated: 2026-02-16
55
"""
66

77
import tempfile
@@ -17,98 +17,60 @@
1717
from selenium.webdriver.chrome.options import Options
1818

1919

20-
# Data - Tech companies comparison
20+
# Data - Tech companies by sector
2121
np.random.seed(42)
22-
n = 30
23-
24-
# Revenue (billions USD) - x axis
25-
revenue = np.array(
26-
[
27-
5,
28-
12,
29-
25,
30-
38,
31-
45,
32-
52,
33-
68,
34-
75,
35-
82,
36-
95,
37-
110,
38-
125,
39-
140,
40-
155,
41-
170,
42-
185,
43-
200,
44-
220,
45-
245,
46-
270,
47-
300,
48-
330,
49-
360,
50-
390,
51-
420,
52-
460,
53-
500,
54-
550,
55-
600,
56-
650,
57-
]
58-
)
59-
60-
# Growth rate (%) - y axis
61-
growth = np.array(
62-
[
63-
45,
64-
38,
65-
52,
66-
28,
67-
35,
68-
22,
69-
42,
70-
18,
71-
30,
72-
25,
73-
33,
74-
20,
75-
28,
76-
15,
77-
38,
78-
12,
79-
25,
80-
18,
81-
22,
82-
15,
83-
20,
84-
12,
85-
18,
86-
10,
87-
15,
88-
8,
89-
12,
90-
6,
91-
10,
92-
5,
93-
]
94-
)
9522

96-
# Add realistic variation
97-
revenue = revenue + np.random.uniform(-3, 3, n)
98-
growth = growth + np.random.uniform(-3, 3, n)
23+
sectors = {
24+
"Cloud & SaaS": {
25+
"color": "rgba(48, 105, 152, 0.65)",
26+
"border": "#1e4f7a",
27+
"revenues": [12, 38, 68, 125, 200, 330, 500],
28+
"growth_base": [48, 35, 42, 28, 22, 15, 10],
29+
},
30+
"E-Commerce": {
31+
"color": "rgba(180, 90, 50, 0.65)",
32+
"border": "#8c3a1a",
33+
"revenues": [25, 75, 155, 270, 420, 600],
34+
"growth_base": [40, 30, 18, 14, 12, 6],
35+
},
36+
"Semiconductors": {
37+
"color": "rgba(60, 145, 80, 0.65)",
38+
"border": "#2a6e3a",
39+
"revenues": [5, 45, 95, 170, 300, 460],
40+
"growth_base": [52, 33, 25, 20, 16, 8],
41+
},
42+
"Social & Media": {
43+
"color": "rgba(140, 80, 160, 0.65)",
44+
"border": "#6b3480",
45+
"revenues": [8, 52, 110, 185, 245, 390, 550],
46+
"growth_base": [55, 38, 30, 22, 18, 12, 7],
47+
},
48+
"Fintech": {
49+
"color": "rgba(200, 160, 50, 0.65)",
50+
"border": "#9a7a1a",
51+
"revenues": [15, 82, 140, 220],
52+
"growth_base": [45, 28, 20, 15],
53+
},
54+
}
9955

100-
# Market cap (billions USD) - bubble size
101-
market_cap = revenue * (1 + growth / 100) * np.random.uniform(2, 8, n)
56+
# Build series data with realistic variation
57+
all_series = []
58+
for sector_name, sector in sectors.items():
59+
n = len(sector["revenues"])
60+
rev = np.array(sector["revenues"], dtype=float) + np.random.uniform(-3, 3, n)
61+
grw = np.array(sector["growth_base"], dtype=float) + np.random.uniform(-2, 2, n)
62+
cap = rev * (1 + grw / 100) * np.random.uniform(2.5, 7, n)
10263

103-
# Scale bubble z values for Highcharts (controls visual size)
104-
min_cap, max_cap = market_cap.min(), market_cap.max()
105-
z_scaled = 20 + (market_cap - min_cap) / (max_cap - min_cap) * 80
64+
data = [
65+
{"x": round(float(rev[i]), 1), "y": round(float(grw[i]), 1), "z": round(float(cap[i]), 1)} for i in range(n)
66+
]
10667

107-
# Format data for Highcharts bubble chart
108-
bubble_data = [
109-
{"x": float(revenue[i]), "y": float(growth[i]), "z": float(z_scaled[i]), "marketCap": float(market_cap[i])}
110-
for i in range(n)
111-
]
68+
s = BubbleSeries()
69+
s.name = sector_name
70+
s.data = data
71+
s.color = sector["color"]
72+
s.marker = {"lineWidth": 3, "lineColor": sector["border"]}
73+
all_series.append(s)
11274

11375
# Create chart
11476
chart = Chart(container="container")
@@ -118,85 +80,93 @@
11880
"type": "bubble",
11981
"width": 4800,
12082
"height": 2700,
121-
"backgroundColor": "#ffffff",
122-
"plotBorderWidth": 1,
123-
"plotBorderColor": "#cccccc",
124-
"spacingBottom": 120,
125-
"spacingRight": 100,
83+
"backgroundColor": "#fafafa",
84+
"spacing": [40, 60, 180, 60],
85+
"style": {"fontFamily": "'Segoe UI', Arial, sans-serif"},
12686
}
12787

12888
chart.options.title = {
129-
"text": "bubble-basic · highcharts · pyplots.ai",
130-
"style": {"fontSize": "64px", "fontWeight": "bold"},
89+
"text": "bubble-basic \u00b7 highcharts \u00b7 pyplots.ai",
90+
"style": {"fontSize": "60px", "fontWeight": "bold", "color": "#2a2a2a"},
13191
}
13292

13393
chart.options.subtitle = {
134-
"text": "Bubble size represents Market Capitalization",
135-
"style": {"fontSize": "40px", "color": "#666666"},
94+
"text": "Bubble size represents Market Capitalization \u2014 Tech companies by sector",
95+
"style": {"fontSize": "38px", "color": "#666666"},
13696
}
13797

13898
chart.options.x_axis = {
139-
"title": {"text": "Revenue (Billion USD)", "style": {"fontSize": "48px"}, "margin": 30},
140-
"labels": {"style": {"fontSize": "36px"}},
99+
"title": {"text": "Revenue (Billion USD)", "style": {"fontSize": "42px", "color": "#3a3a3a"}, "margin": 24},
100+
"labels": {"style": {"fontSize": "34px", "color": "#555555"}},
141101
"gridLineWidth": 1,
142-
"gridLineColor": "#e6e6e6",
102+
"gridLineColor": "rgba(0, 0, 0, 0.06)",
103+
"gridLineDashStyle": "Dot",
104+
"lineColor": "#cccccc",
105+
"lineWidth": 2,
106+
"tickColor": "#cccccc",
143107
"min": 0,
108+
"tickInterval": 100,
144109
}
145110

146111
chart.options.y_axis = {
147-
"title": {"text": "Growth Rate (%)", "style": {"fontSize": "48px"}},
148-
"labels": {"style": {"fontSize": "36px"}},
112+
"title": {"text": "Growth Rate (%)", "style": {"fontSize": "42px", "color": "#3a3a3a"}, "margin": 24},
113+
"labels": {"style": {"fontSize": "34px", "color": "#555555"}},
149114
"gridLineWidth": 1,
150-
"gridLineColor": "#e6e6e6",
115+
"gridLineColor": "rgba(0, 0, 0, 0.06)",
116+
"gridLineDashStyle": "Dot",
117+
"lineColor": "#cccccc",
118+
"lineWidth": 2,
151119
"min": 0,
120+
"tickInterval": 10,
152121
}
153122

154123
chart.options.legend = {
155124
"enabled": True,
156-
"itemStyle": {"fontSize": "36px"},
125+
"align": "right",
126+
"verticalAlign": "top",
127+
"layout": "vertical",
128+
"x": -30,
129+
"y": 80,
130+
"floating": True,
131+
"backgroundColor": "rgba(255, 255, 255, 0.85)",
132+
"borderColor": "#dddddd",
133+
"borderWidth": 1,
134+
"borderRadius": 8,
135+
"padding": 20,
136+
"itemStyle": {"fontSize": "32px", "fontWeight": "normal", "color": "#333333"},
137+
"itemMarginBottom": 10,
138+
"symbolRadius": 6,
157139
"bubbleLegend": {
158140
"enabled": True,
159-
"borderColor": "#306998",
141+
"borderColor": "#888888",
160142
"borderWidth": 2,
161-
"color": "rgba(48, 105, 152, 0.5)",
162-
"connectorColor": "#306998",
163-
"labels": {"style": {"fontSize": "28px"}},
164-
"legendIndex": 0,
165-
"ranges": [
166-
{"value": 20, "borderColor": "#306998", "color": "rgba(48, 105, 152, 0.5)"},
167-
{"value": 60, "borderColor": "#306998", "color": "rgba(48, 105, 152, 0.5)"},
168-
{"value": 100, "borderColor": "#306998", "color": "rgba(48, 105, 152, 0.5)"},
169-
],
143+
"color": "rgba(200, 200, 200, 0.3)",
144+
"connectorColor": "#999999",
145+
"connectorWidth": 2,
146+
"labels": {"style": {"fontSize": "26px", "color": "#555555"}, "format": "{value:.0f}B"},
147+
"minSize": 16,
148+
"maxSize": 55,
170149
},
171150
}
172151

173152
chart.options.tooltip = {
174153
"useHTML": True,
175-
"headerFormat": "",
176-
"pointFormat": '<span style="font-size: 28px"><b>Company Data</b></span><br/>'
177-
'<span style="font-size: 24px">Revenue: ${point.x:.1f}B<br/>'
178-
"Growth: {point.y:.1f}%<br/>"
179-
"Market Cap: ${point.marketCap:.0f}B</span>",
154+
"headerFormat": '<span style="font-size: 28px; font-weight: bold; color: {series.color}">{series.name}</span><br/>',
155+
"pointFormat": '<span style="font-size: 24px">Revenue: <b>${point.x:.1f}B</b><br/>'
156+
"Growth: <b>{point.y:.1f}%</b><br/>"
157+
"Market Cap: <b>${point.z:.0f}B</b></span>",
158+
"backgroundColor": "rgba(255, 255, 255, 0.95)",
159+
"borderColor": "#cccccc",
160+
"borderRadius": 8,
161+
"shadow": {"color": "rgba(0, 0, 0, 0.15)", "offsetX": 2, "offsetY": 2, "width": 4},
180162
}
181163

182164
chart.options.plot_options = {
183-
"bubble": {
184-
"minSize": 50,
185-
"maxSize": 200,
186-
"color": "#306998",
187-
"marker": {"fillOpacity": 0.6, "lineWidth": 3, "lineColor": "#1e4f7a"},
188-
"dataLabels": {"enabled": False},
189-
"sizeBy": "area",
190-
}
165+
"bubble": {"minSize": 30, "maxSize": 200, "sizeBy": "area", "dataLabels": {"enabled": False}, "zMin": 0}
191166
}
192167

193-
# Create bubble series
194-
series = BubbleSeries()
195-
series.name = "Market Cap"
196-
series.data = bubble_data
197-
series.color = "#306998"
198-
199-
chart.add_series(series)
168+
for s in all_series:
169+
chart.add_series(s)
200170

201171
# Download Highcharts JS and highcharts-more.js for bubble support
202172
highcharts_url = "https://code.highcharts.com/highcharts.js"
@@ -223,12 +193,10 @@
223193
</body>
224194
</html>"""
225195

226-
# Write temp HTML and take screenshot
227196
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
228197
f.write(html_content)
229198
temp_path = f.name
230199

231-
# Also save interactive HTML
232200
with open("plot.html", "w", encoding="utf-8") as f:
233201
f.write(html_content)
234202

0 commit comments

Comments
 (0)