|
1 | 1 | """ pyplots.ai |
2 | 2 | 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 |
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import tempfile |
|
17 | 17 | from selenium.webdriver.chrome.options import Options |
18 | 18 |
|
19 | 19 |
|
20 | | -# Data - Tech companies comparison |
| 20 | +# Data - Tech companies by sector |
21 | 21 | 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 | | -) |
95 | 22 |
|
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 | +} |
99 | 55 |
|
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) |
102 | 63 |
|
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 | + ] |
106 | 67 |
|
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) |
112 | 74 |
|
113 | 75 | # Create chart |
114 | 76 | chart = Chart(container="container") |
|
118 | 80 | "type": "bubble", |
119 | 81 | "width": 4800, |
120 | 82 | "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"}, |
126 | 86 | } |
127 | 87 |
|
128 | 88 | 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"}, |
131 | 91 | } |
132 | 92 |
|
133 | 93 | 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"}, |
136 | 96 | } |
137 | 97 |
|
138 | 98 | 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"}}, |
141 | 101 | "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", |
143 | 107 | "min": 0, |
| 108 | + "tickInterval": 100, |
144 | 109 | } |
145 | 110 |
|
146 | 111 | 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"}}, |
149 | 114 | "gridLineWidth": 1, |
150 | | - "gridLineColor": "#e6e6e6", |
| 115 | + "gridLineColor": "rgba(0, 0, 0, 0.06)", |
| 116 | + "gridLineDashStyle": "Dot", |
| 117 | + "lineColor": "#cccccc", |
| 118 | + "lineWidth": 2, |
151 | 119 | "min": 0, |
| 120 | + "tickInterval": 10, |
152 | 121 | } |
153 | 122 |
|
154 | 123 | chart.options.legend = { |
155 | 124 | "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, |
157 | 139 | "bubbleLegend": { |
158 | 140 | "enabled": True, |
159 | | - "borderColor": "#306998", |
| 141 | + "borderColor": "#888888", |
160 | 142 | "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, |
170 | 149 | }, |
171 | 150 | } |
172 | 151 |
|
173 | 152 | chart.options.tooltip = { |
174 | 153 | "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}, |
180 | 162 | } |
181 | 163 |
|
182 | 164 | 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} |
191 | 166 | } |
192 | 167 |
|
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) |
200 | 170 |
|
201 | 171 | # Download Highcharts JS and highcharts-more.js for bubble support |
202 | 172 | highcharts_url = "https://code.highcharts.com/highcharts.js" |
|
223 | 193 | </body> |
224 | 194 | </html>""" |
225 | 195 |
|
226 | | -# Write temp HTML and take screenshot |
227 | 196 | with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f: |
228 | 197 | f.write(html_content) |
229 | 198 | temp_path = f.name |
230 | 199 |
|
231 | | -# Also save interactive HTML |
232 | 200 | with open("plot.html", "w", encoding="utf-8") as f: |
233 | 201 | f.write(html_content) |
234 | 202 |
|
|
0 commit comments