{"spec_id":"bar-error","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nbar-error: Bar Chart with Error Bars\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-10\n\"\"\"\n\nimport os\nimport sys\n\n\nsys.path.insert(0, \"/home/runner/work/anyplot/anyplot/.venv/lib/python3.13/site-packages\")\n\nimport altair as alt\nimport pandas as pd\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nBRAND = \"#009E73\"\n\n# Data - Treatment comparison with measurement variability (±1 SD)\ntreatment_order = [\"Control\", \"Drug A\", \"Drug B\", \"Drug C\", \"Combination\"]\ndata = pd.DataFrame(\n    {\"treatment\": treatment_order, \"response\": [45.2, 62.8, 58.3, 71.5, 82.1], \"error\": [8.5, 12.3, 9.8, 15.2, 11.7]}\n)\n\n# Calculate error bar bounds\ndata[\"lower\"] = data[\"response\"] - data[\"error\"]\ndata[\"upper\"] = data[\"response\"] + data[\"error\"]\n\n# Create bars with brand color\nbars = (\n    alt.Chart(data)\n    .mark_bar(size=60, color=BRAND)\n    .encode(\n        x=alt.X(\n            \"treatment:N\",\n            title=\"Treatment Group\",\n            sort=treatment_order,\n            axis=alt.Axis(labelFontSize=18, titleFontSize=22, labelAngle=0),\n        ),\n        y=alt.Y(\n            \"response:Q\",\n            title=\"Response Rate (%)\",\n            scale=alt.Scale(domain=[0, 100]),\n            axis=alt.Axis(labelFontSize=18, titleFontSize=22),\n        ),\n        tooltip=[\n            \"treatment:N\",\n            alt.Tooltip(\"response:Q\", format=\".1f\"),\n            alt.Tooltip(\"error:Q\", format=\".1f\", title=\"±SD\"),\n        ],\n    )\n)\n\n# Create error bars with caps using rule marks\nerror_bars = (\n    alt.Chart(data)\n    .mark_rule(strokeWidth=3, color=INK_SOFT)\n    .encode(x=alt.X(\"treatment:N\", sort=treatment_order), y=\"lower:Q\", y2=\"upper:Q\")\n)\n\n# Error bar caps (top)\ncaps_top = (\n    alt.Chart(data)\n    .mark_tick(size=30, thickness=3, color=INK_SOFT)\n    .encode(x=alt.X(\"treatment:N\", sort=treatment_order), y=\"upper:Q\")\n)\n\n# Error bar caps (bottom)\ncaps_bottom = (\n    alt.Chart(data)\n    .mark_tick(size=30, thickness=3, color=INK_SOFT)\n    .encode(x=alt.X(\"treatment:N\", sort=treatment_order), y=\"lower:Q\")\n)\n\n# Annotation for error bar meaning\nannotation = (\n    alt.Chart(pd.DataFrame({\"text\": [\"Error bars: ±1 SD\"]}))\n    .mark_text(align=\"right\", baseline=\"top\", fontSize=16, color=INK_SOFT)\n    .encode(x=alt.value(1550), y=alt.value(30), text=\"text:N\")\n)\n\n# Combine all layers\nchart = (\n    alt.layer(bars, error_bars, caps_top, caps_bottom, annotation)\n    .properties(\n        width=1600,\n        height=900,\n        background=PAGE_BG,\n        title=alt.Title(\"bar-error · altair · anyplot.ai\", fontSize=28, anchor=\"middle\", color=INK),\n    )\n    .configure_view(fill=PAGE_BG, stroke=None)\n    .configure_axis(\n        domainColor=INK_SOFT, tickColor=INK_SOFT, gridColor=INK, gridOpacity=0.10, labelColor=INK_SOFT, titleColor=INK\n    )\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\n# Save as PNG (1600 × 900 × 3 = 4800 × 2700)\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\n\n# Save as HTML for interactivity\nchart.save(f\"plot-{THEME}.html\")\n"}