|
1 | 1 | """ anyplot.ai |
2 | 2 | timeseries-forecast-uncertainty: Time Series Forecast with Uncertainty Band |
3 | 3 | Library: highcharts unknown | Python 3.13.13 |
4 | | -Quality: 92/100 | Updated: 2026-05-16 |
| 4 | +Quality: 87/100 | Updated: 2026-05-19 |
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import os |
|
19 | 19 | from selenium.webdriver.chrome.options import Options |
20 | 20 |
|
21 | 21 |
|
| 22 | +def _download_js(urls): |
| 23 | + for url in urls: |
| 24 | + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"}) |
| 25 | + for attempt in range(2): |
| 26 | + try: |
| 27 | + with urllib.request.urlopen(req, timeout=30) as response: |
| 28 | + return response.read().decode("utf-8") |
| 29 | + except (urllib.error.URLError, urllib.error.HTTPError): |
| 30 | + if attempt == 0: |
| 31 | + time.sleep(1) |
| 32 | + return None |
| 33 | + |
| 34 | + |
22 | 35 | # Theme tokens |
23 | 36 | THEME = os.getenv("ANYPLOT_THEME", "light") |
24 | 37 | PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
|
71 | 84 | forecast_start_ts = forecast_timestamps[0] |
72 | 85 |
|
73 | 86 | # Download Highcharts JS from CDN with fallbacks |
74 | | -cdn_urls = [ |
75 | | - "https://cdn.jsdelivr.net/npm/highcharts@11/highcharts.min.js", |
76 | | - "https://unpkg.com/highcharts@11/highcharts.js", |
77 | | - "https://code.highcharts.com/highcharts.js", |
78 | | -] |
79 | | - |
80 | | -highcharts_js = None |
81 | | -for url in cdn_urls: |
82 | | - req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"}) |
83 | | - max_retries = 2 |
84 | | - for attempt in range(max_retries): |
85 | | - try: |
86 | | - with urllib.request.urlopen(req, timeout=30) as response: |
87 | | - highcharts_js = response.read().decode("utf-8") |
88 | | - break |
89 | | - except (urllib.error.URLError, urllib.error.HTTPError): |
90 | | - if attempt < max_retries - 1: |
91 | | - time.sleep(1) |
92 | | - if highcharts_js: |
93 | | - break |
94 | | - |
| 87 | +highcharts_js = _download_js( |
| 88 | + [ |
| 89 | + "https://cdn.jsdelivr.net/npm/highcharts@11/highcharts.min.js", |
| 90 | + "https://unpkg.com/highcharts@11/highcharts.js", |
| 91 | + "https://code.highcharts.com/highcharts.js", |
| 92 | + ] |
| 93 | +) |
95 | 94 | if not highcharts_js: |
96 | 95 | raise RuntimeError("Could not download Highcharts from any CDN") |
97 | 96 |
|
98 | 97 | # Download Highcharts More for arearange |
99 | | -cdn_more_urls = [ |
100 | | - "https://cdn.jsdelivr.net/npm/highcharts@11/highcharts-more.min.js", |
101 | | - "https://unpkg.com/highcharts@11/highcharts-more.js", |
102 | | - "https://code.highcharts.com/highcharts-more.js", |
103 | | -] |
104 | | - |
105 | | -highcharts_more_js = None |
106 | | -for url in cdn_more_urls: |
107 | | - req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"}) |
108 | | - max_retries = 2 |
109 | | - for attempt in range(max_retries): |
110 | | - try: |
111 | | - with urllib.request.urlopen(req, timeout=30) as response: |
112 | | - highcharts_more_js = response.read().decode("utf-8") |
113 | | - break |
114 | | - except (urllib.error.URLError, urllib.error.HTTPError): |
115 | | - if attempt < max_retries - 1: |
116 | | - time.sleep(1) |
117 | | - if highcharts_more_js: |
118 | | - break |
119 | | - |
| 98 | +highcharts_more_js = _download_js( |
| 99 | + [ |
| 100 | + "https://cdn.jsdelivr.net/npm/highcharts@11/highcharts-more.min.js", |
| 101 | + "https://unpkg.com/highcharts@11/highcharts-more.js", |
| 102 | + "https://code.highcharts.com/highcharts-more.js", |
| 103 | + ] |
| 104 | +) |
120 | 105 | if not highcharts_more_js: |
121 | 106 | raise RuntimeError("Could not download Highcharts More from any CDN") |
122 | 107 |
|
|
127 | 112 | # Chart configuration |
128 | 113 | chart.options.chart = { |
129 | 114 | "type": "line", |
130 | | - "width": 4800, |
131 | | - "height": 2700, |
| 115 | + "width": 3200, |
| 116 | + "height": 1800, |
132 | 117 | "backgroundColor": PAGE_BG, |
133 | | - "spacingTop": 60, |
134 | | - "spacingBottom": 120, |
135 | | - "spacingLeft": 100, |
136 | | - "spacingRight": 120, |
| 118 | + "spacingTop": 40, |
| 119 | + "spacingBottom": 100, |
| 120 | + "spacingLeft": 60, |
| 121 | + "spacingRight": 80, |
137 | 122 | } |
138 | 123 |
|
139 | 124 | # Title |
140 | 125 | chart.options.title = { |
141 | | - "text": "timeseries-forecast-uncertainty · highcharts · anyplot.ai", |
142 | | - "style": {"fontSize": "28px", "fontWeight": "medium", "color": INK}, |
143 | | - "margin": 40, |
| 126 | + "text": "timeseries-forecast-uncertainty · python · highcharts · anyplot.ai", |
| 127 | + "style": {"fontSize": "18px", "fontWeight": "medium", "color": INK}, |
| 128 | + "margin": 30, |
144 | 129 | } |
145 | 130 |
|
146 | 131 | # X-axis (datetime) |
147 | 132 | chart.options.x_axis = { |
148 | 133 | "type": "datetime", |
149 | | - "title": {"text": "Date", "style": {"fontSize": "22px", "color": INK}, "margin": 25}, |
150 | | - "labels": {"style": {"fontSize": "18px", "color": INK_SOFT}}, |
| 134 | + "title": {"text": "Date", "style": {"fontSize": "14px", "color": INK}, "margin": 15}, |
| 135 | + "labels": {"style": {"fontSize": "12px", "color": INK_SOFT}}, |
151 | 136 | "dateTimeLabelFormats": {"month": "%b %Y"}, |
152 | 137 | "gridLineWidth": 1, |
153 | 138 | "gridLineColor": GRID, |
|
157 | 142 | { |
158 | 143 | "value": forecast_start_ts, |
159 | 144 | "color": INK_SOFT, |
160 | | - "width": 3, |
| 145 | + "width": 2, |
161 | 146 | "dashStyle": "Dash", |
162 | 147 | "label": { |
163 | 148 | "text": "Forecast Start", |
164 | | - "style": {"fontSize": "18px", "color": INK_SOFT, "fontWeight": "normal"}, |
| 149 | + "style": {"fontSize": "12px", "color": INK_SOFT, "fontWeight": "normal"}, |
165 | 150 | "rotation": 0, |
166 | | - "y": -15, |
| 151 | + "y": -10, |
167 | 152 | }, |
168 | 153 | "zIndex": 5, |
169 | 154 | } |
|
172 | 157 |
|
173 | 158 | # Y-axis |
174 | 159 | chart.options.y_axis = { |
175 | | - "title": {"text": "Product Demand (Units)", "style": {"fontSize": "22px", "color": INK}, "margin": 25}, |
176 | | - "labels": {"style": {"fontSize": "18px", "color": INK_SOFT}}, |
| 160 | + "title": {"text": "Product Demand (Units)", "style": {"fontSize": "14px", "color": INK}, "margin": 15}, |
| 161 | + "labels": {"style": {"fontSize": "12px", "color": INK_SOFT}}, |
177 | 162 | "gridLineWidth": 1, |
178 | 163 | "gridLineColor": GRID, |
179 | 164 | "lineColor": INK_SOFT, |
180 | 165 | "tickColor": INK_SOFT, |
181 | 166 | } |
182 | 167 |
|
183 | | -# Legend with larger symbols |
| 168 | +# Legend |
184 | 169 | chart.options.legend = { |
185 | 170 | "enabled": True, |
186 | | - "itemStyle": {"fontSize": "18px", "color": INK_SOFT}, |
187 | | - "symbolWidth": 40, |
188 | | - "symbolHeight": 24, |
| 171 | + "itemStyle": {"fontSize": "12px", "color": INK_SOFT}, |
| 172 | + "symbolWidth": 24, |
| 173 | + "symbolHeight": 14, |
189 | 174 | "symbolRadius": 3, |
190 | | - "margin": 30, |
| 175 | + "margin": 20, |
191 | 176 | } |
192 | 177 |
|
193 | 178 | # Tooltip |
194 | 179 | chart.options.tooltip = { |
195 | 180 | "shared": True, |
196 | | - "style": {"fontSize": "16px", "color": INK}, |
| 181 | + "style": {"fontSize": "12px", "color": INK}, |
197 | 182 | "xDateFormat": "%B %Y", |
198 | 183 | "valueDecimals": 1, |
199 | 184 | } |
|
207 | 192 | ci_95_series.data = [ |
208 | 193 | {"x": forecast_timestamps[i], "low": float(lower_95[i]), "high": float(upper_95[i])} for i in range(n_forecast) |
209 | 194 | ] |
210 | | -ci_95_series.color = "#56B4E9" if THEME == "light" else "#87CEEB" |
211 | | -ci_95_series.fill_opacity = 0.15 |
| 195 | +ci_95_series.color = "#56B4E9" |
| 196 | +ci_95_series.fill_opacity = 0.25 |
212 | 197 | ci_95_series.line_width = 0 |
213 | 198 | ci_95_series.marker = {"enabled": False} |
214 | 199 | ci_95_series.z_index = 0 |
|
265 | 250 | <script>{highcharts_more_js}</script> |
266 | 251 | </head> |
267 | 252 | <body style="margin:0; background:{PAGE_BG};"> |
268 | | - <div id="container" style="width: 4800px; height: 2700px;"></div> |
| 253 | + <div id="container" style="width: 3200px; height: 1800px;"></div> |
269 | 254 | <script>{html_str}</script> |
270 | 255 | </body> |
271 | 256 | </html>""" |
|
284 | 269 | chrome_options.add_argument("--no-sandbox") |
285 | 270 | chrome_options.add_argument("--disable-dev-shm-usage") |
286 | 271 | chrome_options.add_argument("--disable-gpu") |
287 | | -chrome_options.add_argument("--window-size=4800,2700") |
| 272 | +chrome_options.add_argument("--window-size=3200,1800") |
288 | 273 |
|
289 | 274 | driver = webdriver.Chrome(options=chrome_options) |
290 | 275 | driver.get(f"file://{temp_path}") |
|
0 commit comments