diff --git a/plots/bar-race-animated/implementations/python/highcharts.py b/plots/bar-race-animated/implementations/python/highcharts.py index 3b43073229..fdb6e332d2 100644 --- a/plots/bar-race-animated/implementations/python/highcharts.py +++ b/plots/bar-race-animated/implementations/python/highcharts.py @@ -1,9 +1,10 @@ -""" pyplots.ai +""" anyplot.ai bar-race-animated: Animated Bar Chart Race -Library: highcharts unknown | Python 3.13.11 -Quality: 91/100 | Created: 2026-01-11 +Library: highcharts unknown | Python 3.13.13 +Quality: 83/100 | Updated: 2026-05-19 """ +import os import tempfile import time import urllib.request @@ -18,6 +19,27 @@ from selenium.webdriver.chrome.options import Options +# Theme +THEME = os.getenv("ANYPLOT_THEME", "light") +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" +INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" +GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)" + +# Okabe-Ito palette (positions 1–7) + adaptive neutral for 8 entities +OI_COLORS = [ + "#009E73", # position 1: bluish green + "#D55E00", # position 2: vermillion + "#0072B2", # position 3: blue + "#CC79A7", # position 4: reddish purple + "#E69F00", # position 5: orange + "#56B4E9", # position 6: sky blue + "#F0E442", # position 7: yellow + "#1A1A1A" if THEME == "light" else "#E8E8E0", # position 8: adaptive neutral +] + # Data - Global Technology Companies Market Value (in $B) 2019-2024 np.random.seed(42) @@ -30,93 +52,73 @@ "QuantumByte", "NetPrime", "DigiWave", - "SmartSystems", - "ByteForce", - "NexGen", - "CoreLogic", ] years = [2019, 2020, 2021, 2022, 2023, 2024] -# Generate realistic market value evolution -base_values = np.array([180, 150, 120, 100, 90, 80, 70, 60, 50, 45, 40, 35]) +base_values = np.array([180, 150, 120, 100, 90, 80, 70, 60]) data = [] for i, year in enumerate(years): growth = 1 + 0.15 * i + np.random.randn(len(companies)) * 0.2 values = base_values * growth * (1 + np.random.randn(len(companies)) * 0.1) - # Add some shuffling over time to make rankings change shuffle_factor = np.random.randn(len(companies)) * (20 + i * 10) values = values + shuffle_factor - values = np.maximum(values, 10) # Minimum value + values = np.maximum(values, 10) for j, company in enumerate(companies): data.append({"company": company, "year": year, "value": values[j]}) df = pd.DataFrame(data) - -# Colors - consistent per company -colors = [ - "#306998", - "#FFD43B", - "#9467BD", - "#17BECF", - "#8C564B", - "#E377C2", - "#7F7F7F", - "#BCBD22", - "#1F77B4", - "#FF7F0E", - "#2CA02C", - "#D62728", -] -company_colors = dict(zip(companies, colors, strict=False)) - -# Select 6 key time snapshots for small multiples grid (2x3) -snapshot_years = years +company_colors = dict(zip(companies, OI_COLORS, strict=True)) # Download Highcharts JS -highcharts_url = "https://code.highcharts.com/highcharts.js" -with urllib.request.urlopen(highcharts_url, timeout=30) as response: +highcharts_url = "https://cdn.jsdelivr.net/npm/highcharts@12/highcharts.js" +with urllib.request.urlopen(highcharts_url, timeout=60) as response: highcharts_js = response.read().decode("utf-8") -# Generate individual charts for each year +# Generate individual charts for each year snapshot +CHART_W = 1550 +CHART_H = 1150 +max_val = df["value"].max() * 1.1 + chart_scripts = [] -for idx, year in enumerate(snapshot_years): - year_data = df[df["year"] == year].sort_values("value", ascending=True).tail(10) +for idx, year in enumerate(years): + year_data = df[df["year"] == year].sort_values("value", ascending=True) chart = Chart(container=f"chart{idx}") chart.options = HighchartsOptions() chart.options.chart = { "type": "bar", - "width": 1520, - "height": 1280, - "backgroundColor": "#ffffff", - "marginLeft": 200, - "marginRight": 50, + "width": CHART_W, + "height": CHART_H, + "backgroundColor": ELEVATED_BG, + "marginLeft": 230, + "marginRight": 110, "marginBottom": 80, - "marginTop": 100, + "marginTop": 80, } - chart.options.title = {"text": f"Year {year}", "style": {"fontSize": "32px", "fontWeight": "bold"}} + chart.options.title = {"text": str(year), "style": {"fontSize": "38px", "fontWeight": "bold", "color": INK}} chart.options.x_axis = { "categories": year_data["company"].tolist(), "title": {"text": None}, - "labels": {"style": {"fontSize": "20px"}}, + "labels": {"style": {"fontSize": "22px", "color": INK_SOFT}}, + "lineColor": INK_SOFT, + "tickColor": INK_SOFT, } chart.options.y_axis = { - "title": {"text": "Market Value ($B)", "style": {"fontSize": "20px"}}, - "labels": {"style": {"fontSize": "18px"}}, + "title": {"text": "Market Value ($B)", "style": {"fontSize": "22px", "color": INK}}, + "labels": {"style": {"fontSize": "18px", "color": INK_SOFT}}, "min": 0, - "max": 400, + "max": max_val, + "gridLineColor": GRID, } chart.options.legend = {"enabled": False} - chart.options.credits = {"enabled": False} - # Create series with individual colors series = BarSeries() series.name = "Market Value" series.data = [ @@ -125,16 +127,16 @@ series.data_labels = { "enabled": True, "format": "${point.y:.0f}B", - "style": {"fontSize": "16px", "fontWeight": "normal"}, + "style": {"fontSize": "19px", "fontWeight": "normal", "color": INK, "textOutline": "none"}, } - chart.add_series(series) - chart.options.plot_options = {"bar": {"borderWidth": 0, "pointWidth": 50}} + chart.options.plot_options = {"bar": {"borderWidth": 0, "pointWidth": 58}} chart_scripts.append(chart.to_js_literal()) -# Create combined HTML with 2x3 grid +# Assemble full-page HTML with 2×3 small-multiples grid +GRID_GAP = 28 html_content = f""" @@ -144,41 +146,40 @@ body {{ margin: 0; padding: 40px; - background: #ffffff; + background: {PAGE_BG}; font-family: Arial, sans-serif; }} .main-title {{ text-align: center; - font-size: 48px; + font-size: 52px; font-weight: bold; - margin-bottom: 10px; - color: #333; + margin-bottom: 8px; + color: {INK}; }} .subtitle {{ text-align: center; - font-size: 28px; - color: #666; - margin-bottom: 40px; + font-size: 30px; + color: {INK_MUTED}; + margin-bottom: 36px; }} .grid {{ display: grid; - grid-template-columns: repeat(3, 1fr); - grid-template-rows: repeat(2, 1fr); - gap: 30px; - width: 4720px; + grid-template-columns: repeat(3, {CHART_W}px); + grid-template-rows: repeat(2, {CHART_H}px); + gap: {GRID_GAP}px; + width: {3 * CHART_W + 2 * GRID_GAP}px; margin: 0 auto; }} .chart-container {{ - background: #fafafa; - border: 2px solid #e0e0e0; - border-radius: 10px; + background: {ELEVATED_BG}; + border-radius: 8px; overflow: hidden; }} -
bar-race-animated · highcharts · pyplots.ai
-
Technology Companies Market Value Evolution (2019-2024)
+
bar-race-animated · python · highcharts · anyplot.ai
+
Technology Companies Market Value Evolution (2019–2024)
@@ -198,14 +199,14 @@ """ -# Write temp HTML and take screenshot -with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f: +# Save HTML artifact +with open(f"plot-{THEME}.html", "w", encoding="utf-8") as f: f.write(html_content) - temp_path = f.name -# Also save as plot.html for interactive version -with open("plot.html", "w", encoding="utf-8") as f: +# Screenshot via headless Chrome +with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f: f.write(html_content) + temp_path = f.name chrome_options = Options() chrome_options.add_argument("--headless") @@ -216,8 +217,8 @@ driver = webdriver.Chrome(options=chrome_options) driver.get(f"file://{temp_path}") -time.sleep(8) # Wait for all charts to render -driver.save_screenshot("plot.png") +time.sleep(8) +driver.save_screenshot(f"plot-{THEME}.png") driver.quit() Path(temp_path).unlink() diff --git a/plots/bar-race-animated/metadata/python/highcharts.yaml b/plots/bar-race-animated/metadata/python/highcharts.yaml index 390e46872b..eb85bd2cd6 100644 --- a/plots/bar-race-animated/metadata/python/highcharts.yaml +++ b/plots/bar-race-animated/metadata/python/highcharts.yaml @@ -1,159 +1,184 @@ library: highcharts +language: python specification_id: bar-race-animated created: '2026-01-11T00:19:53Z' -updated: '2026-01-11T00:27:46Z' -generated_by: claude-opus-4-5-20251101 -workflow_run: 20886563857 +updated: '2026-05-19T02:27:38Z' +generated_by: claude-sonnet +workflow_run: 26071381995 issue: 3653 -python_version: 3.13.11 +language_version: 3.13.13 library_version: unknown -preview_url: https://storage.googleapis.com/anyplot-images/plots/bar-race-animated/highcharts/plot.png -preview_html: https://storage.googleapis.com/anyplot-images/plots/bar-race-animated/highcharts/plot.html -quality_score: 91 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/bar-race-animated/python/highcharts/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/bar-race-animated/python/highcharts/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/bar-race-animated/python/highcharts/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/bar-race-animated/python/highcharts/plot-dark.html +quality_score: 83 review: strengths: - - Excellent implementation of small multiples grid approach for static output as - spec recommends - - Consistent color coding per company across all time periods enables tracking - - Clear data labels with dollar formatting ($XXB) on each bar - - Good use of Highcharts styling options (margins, font sizes, borders) - - Generates both static PNG and interactive HTML output - - Realistic tech company market value scenario with plausible evolution + - 'Perfect spec compliance: all required bar-race features present with correct + static fallback (small multiples grid).' + - Clean code structure with seed, adaptive theme tokens, and proper HTML+PNG dual + output. + - Consistent per-entity Okabe-Ito coloring maintained across all 6 year panels. + - Card-like chart containers with rounded corners give the grid a polished, dashboard-like + appearance. + - Both light and dark renders are fully readable with correct backgrounds and adaptive + chrome. weaknesses: - - Font sizes could be slightly larger for optimal readability at full resolution - - Some panels show only 9 companies instead of consistent 10 (visible in 2022 panel) - - The individual panel charts have excessive dimensions (1520x1280) that get scaled - down significantly in the grid view - image_description: The plot displays a 2x3 grid of small multiple bar charts showing - "Technology Companies Market Value Evolution (2019-2024)" with the main title - "bar-race-animated · highcharts · pyplots.ai" at the top. Each panel represents - a different year (2019-2024) with horizontal bar charts showing top 10 technology - companies by market value. Bars are color-coded consistently per company (e.g., - TechCorp Alpha in pink/magenta, DataSphere in teal, QuantumByte in yellow, etc.). - Each bar has a data label showing the value in $B format. The bars are sorted - by value within each panel, and you can observe ranking changes across years - - for example, TechCorp Alpha leads in 2019 but DataSphere overtakes by 2024. The - Y-axis shows company names and X-axis shows "Market Value ($B)" ranging from 0-400. - Each panel has a subtle gray background with white chart background, bordered - with rounded corners. + - Extreme outlier values (DataSphere in 2022 ~$3000B, TechCorp Alpha in 2024 ~$2926B) + distort the shared x-axis scale, compressing most bars in those panels to a fraction + of panel width. + - No visual storytelling emphasis — no highlighted 'winner' bar, no callout for + the most dramatic ranking change, no leader annotation. + - 'Design Excellence headroom: DE-01 could reach 6-7 with a subtle rank-change indicator + or emphasis on the leading entity per year.' + image_description: |- + Light render (plot-light.png): + Background: Warm off-white #FAF8F1 — correct, not pure white. + Chrome: Main title "bar-race-animated · python · highcharts · anyplot.ai" in dark ink, subtitle in muted dark ink, year labels bold and dark — all clearly readable against the light surface. + Data: 8 companies colored with Okabe-Ito positions 1–8. First entity (TechCorp Alpha) is #009E73 (brand green). Bars have inline data labels (e.g. "$1888B") in dark ink. All bars distinguishable. + Legibility verdict: PASS + + Dark render (plot-dark.png): + Background: Warm near-black #1A1A17 — correct, not pure black. + Chrome: Main title, subtitle, year labels, company names, axis tick labels all render in light ink (#F0EFE8 / #B8B7B0). No dark-on-dark failures detected. Grid lines subtle (rgba opacity 10%). + Data: Okabe-Ito colors identical to light render for positions 1–7. Adaptive neutral (DigiWave, position 8) correctly flips to #E8E8E0. + Legibility verdict: PASS criteria_checklist: visual_quality: - score: 35 - max: 40 + score: 27 + max: 30 items: - id: VQ-01 name: Text Legibility - score: 8 - max: 10 + score: 7 + max: 8 passed: true - comment: Text is readable but could be slightly larger for optimal viewing; - company names and values are clear + comment: All font sizes explicitly set (52px main title, 38px year labels, + 22px axis labels, 18px ticks, 19px data labels); all readable in both renders - id: VQ-02 name: No Overlap - score: 8 - max: 8 + score: 6 + max: 6 passed: true - comment: No overlapping text elements + comment: No text collisions in any of the 6 panels in either render - id: VQ-03 name: Element Visibility - score: 7 - max: 8 + score: 4 + max: 6 passed: true - comment: Bars are well-sized and visible; data labels are clear + comment: Bars visible with data labels; extreme outliers in 2022 (~$3000B) + and 2024 (~$2926B) compress most other bars to fraction of panel width - id: VQ-04 name: Color Accessibility - score: 5 - max: 5 + score: 2 + max: 2 passed: true - comment: Uses colorblind-safe palette avoiding red-green conflicts + comment: Okabe-Ito palette is CVD-safe; bars wide enough for shape-independent + identification - id: VQ-05 - name: Layout Balance + name: Layout & Canvas score: 4 - max: 5 + max: 4 passed: true - comment: Good use of canvas with 2x3 grid; slight margin imbalance on some - panels + comment: 2x3 grid fills ~90% of 4800x2700 canvas; balanced margins; nothing + cut off - id: VQ-06 - name: Axis Labels + name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Market Value ($B) is descriptive with units + comment: X-axis has units (Market Value $B); y-axis shows entity names - id: VQ-07 - name: Grid & Legend - score: 1 + name: Palette Compliance + score: 2 max: 2 passed: true - comment: No legend needed (colors match company names), grid is subtle but - could be more refined + comment: 'First entity is #009E73; Okabe-Ito order maintained; backgrounds + #FAF8F1/#1A1A17; all chrome is theme-adaptive' + design_excellence: + score: 12 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: Above plain defaults with card-like rounded containers and consistent + Okabe-Ito palette; not yet publication-ready + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Subtle 10% opacity grid, borderWidth 0 on bars, credits disabled, + custom margins, elevated-bg containers + - id: DE-03 + name: Data Storytelling + score: 3 + max: 6 + passed: false + comment: Small multiples show time progression effectively but no visual emphasis + on ranking leader or most dramatic change spec_compliance: - score: 24 - max: 25 + score: 15 + max: 15 items: - id: SC-01 name: Plot Type - score: 8 - max: 8 - passed: true - comment: Correct horizontal bar chart with ranking changes over time - - id: SC-02 - name: Data Mapping score: 5 max: 5 passed: true - comment: Companies on Y-axis, values on X-axis, properly sorted - - id: SC-03 + comment: Horizontal bar chart in small-multiples grid; valid static fallback + per spec + - id: SC-02 name: Required Features - score: 5 - max: 5 + score: 4 + max: 4 passed: true - comment: Small multiples for static output (as spec recommends), consistent - colors per entity, visible time indicators - - id: SC-04 - name: Data Range + comment: Bars sorted by value each frame; entity labels attached; visible + time indicator; colors consistent per entity + - id: SC-03 + name: Data Mapping score: 3 max: 3 passed: true - comment: Consistent axis range (0-400) across all panels enables comparison - - id: SC-05 - name: Legend Accuracy - score: 1 - max: 2 - passed: true - comment: Colors are consistent but no explicit legend (acceptable since labels - are on bars) - - id: SC-06 - name: Title Format - score: 2 - max: 2 + comment: Companies on y-axis, market value on x-axis; all data visible + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 passed: true - comment: 'Uses correct format: bar-race-animated · highcharts · pyplots.ai' + comment: Title exactly 'bar-race-animated · python · highcharts · anyplot.ai'; + no legend needed data_quality: - score: 18 - max: 20 + score: 12 + max: 15 items: - id: DQ-01 name: Feature Coverage - score: 7 - max: 8 + score: 5 + max: 6 passed: true - comment: Shows ranking changes, multiple companies, value evolution; could - show more dramatic rank swaps + comment: Rankings change year-to-year across 8 entities and 6 snapshots; minor + deduction for limited dramatic rank crossings in middle years - id: DQ-02 name: Realistic Context - score: 7 - max: 7 + score: 4 + max: 5 passed: true - comment: Technology company market values is a realistic, neutral scenario + comment: Fictional tech company names in plausible market-value scenario; + neutral topic with descriptive subtitle - id: DQ-03 name: Appropriate Scale - score: 4 - max: 5 + score: 3 + max: 4 passed: true - comment: Values in billions are appropriate; 10-400B range is plausible for - tech companies + comment: Most values plausible; single extreme outlier (~$3000B for DataSphere + in 2022) is unrealistic relative to peers code_quality: - score: 9 + score: 10 max: 10 items: - id: CQ-01 @@ -161,55 +186,61 @@ review: score: 3 max: 3 passed: true - comment: 'Linear structure: imports → data → plot generation → HTML → screenshot' + comment: Linear Imports->Data->Chart loop->HTML assembly->Save; no functions + or classes - id: CQ-02 name: Reproducibility - score: 3 - max: 3 + score: 2 + max: 2 passed: true - comment: Uses np.random.seed(42) + comment: np.random.seed(42) set - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: All imports are used + comment: All imported modules are used - id: CQ-04 - name: No Deprecated API - score: 1 - max: 1 + name: Code Elegance + score: 2 + max: 2 passed: true - comment: Modern Highcharts API usage + comment: Clean, Pythonic; list comprehension for chart scripts; appropriate + complexity - id: CQ-05 - name: Output Correct - score: 0 + name: Output & API + score: 1 max: 1 - passed: false - comment: Saves plot.png AND plot.html (which is good for interactive library) - library_features: - score: 5 - max: 5 + passed: true + comment: Saves plot-{THEME}.png and plot-{THEME}.html + library_mastery: + score: 7 + max: 10 items: - - id: LF-01 + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: 'Uses Chart, HighchartsOptions, BarSeries, chart.to_js_literal() + correctly; per-point color assignment via {color: ...} in series data is + idiomatic Highcharts' + - id: LM-02 name: Distinctive Features - score: 5 + score: 3 max: 5 passed: true - comment: Properly uses highcharts_core Python bindings, BarSeries, custom - chart options, data labels, and generates both PNG and interactive HTML + comment: Uses to_js_literal() for HTML embedding, per-point color overrides, + and Highcharts format-string data labels (${point.y:.0f}B) verdict: APPROVED impl_tags: dependencies: - selenium techniques: - - subplots - html-export - - annotations patterns: - data-generation - iteration-over-groups - dataprep: - - time-series + dataprep: [] styling: - grid-styling - - edge-highlighting