{"spec_id":"box-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nbox-basic: Basic Box Plot\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-05-28\n\"\"\"\n\nimport os\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\n\n\n# Theme tokens\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\"\n\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\n# Data — salary distributions across five departments\nnp.random.seed(42)\ndepartments = [\"Engineering\", \"Marketing\", \"Sales\", \"HR\", \"Finance\"]\nparams = {\n    \"Engineering\": (95000, 15000, 80),\n    \"Marketing\": (72000, 12000, 65),\n    \"Sales\": (68000, 18000, 90),\n    \"HR\": (63000, 9000, 55),\n    \"Finance\": (85000, 13000, 70),\n}\n\nrecords = []\nfor dept, (mean, std, n) in params.items():\n    salaries = np.random.normal(mean, std, n)\n    salaries = np.clip(salaries, 25000, None)\n    for s in salaries:\n        records.append({\"Department\": dept, \"Salary\": round(s, -2)})\n\ndf = pd.DataFrame(records)\n\n# Annotation — Engineering has the highest median salary\neng_median = df[df[\"Department\"] == \"Engineering\"][\"Salary\"].median()\nannotation_df = pd.DataFrame(\n    [{\"Department\": \"Engineering\", \"Salary\": 138000, \"Label\": f\"highest median  ${eng_median:,.0f}\"}]\n)\n\n# Plot\ntitle = \"box-basic · python · altair · anyplot.ai\"\n\nboxplot = (\n    alt.Chart(df)\n    .mark_boxplot(\n        size=90,\n        median={\"stroke\": \"white\", \"strokeWidth\": 3},\n        outliers={\"size\": 100, \"strokeWidth\": 1.5, \"opacity\": 0.75},\n    )\n    .encode(\n        x=alt.X(\"Department:N\", title=\"Department\", sort=departments, axis=alt.Axis(labelAngle=0, titlePadding=10)),\n        y=alt.Y(\n            \"Salary:Q\",\n            title=\"Salary (USD)\",\n            scale=alt.Scale(domain=[22000, 142000]),\n            axis=alt.Axis(format=\"$,.0f\", tickCount=7, titlePadding=10),\n        ),\n        color=alt.Color(\"Department:N\", scale=alt.Scale(domain=departments, range=IMPRINT_PALETTE), legend=None),\n        tooltip=[\n            alt.Tooltip(\"Department:N\"),\n            alt.Tooltip(\"median(Salary):Q\", title=\"Median\", format=\"$,.0f\"),\n            alt.Tooltip(\"q1(Salary):Q\", title=\"Q1\", format=\"$,.0f\"),\n            alt.Tooltip(\"q3(Salary):Q\", title=\"Q3\", format=\"$,.0f\"),\n        ],\n    )\n)\n\nannotation = (\n    alt.Chart(annotation_df)\n    .mark_text(align=\"center\", baseline=\"middle\", color=\"#009E73\", fontSize=10, fontWeight=\"bold\")\n    .encode(x=alt.X(\"Department:N\", sort=departments), y=alt.Y(\"Salary:Q\"), text=\"Label:N\")\n)\n\nchart = (\n    alt.layer(boxplot, annotation)\n    .properties(\n        width=620,\n        height=320,\n        background=PAGE_BG,\n        padding={\"left\": 0, \"right\": 0, \"top\": 0, \"bottom\": 0},\n        title=alt.Title(title, fontSize=16, anchor=\"start\", offset=10),\n    )\n    .configure_view(fill=PAGE_BG, stroke=None)\n    .configure_axis(\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        gridColor=INK,\n        gridOpacity=0.15,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        labelFontSize=10,\n        titleFontSize=12,\n    )\n    .configure_title(color=INK)\n    .configure_legend(\n        fillColor=ELEVATED_BG,\n        strokeColor=INK_SOFT,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        labelFontSize=10,\n        titleFontSize=10,\n    )\n)\n\n# Save PNG\nchart.save(f\"plot-{THEME}.png\", scale_factor=4.0)\n\n# Pad to exact canvas target (3200 × 1800)\nTW, TH = 3200, 1800\n_img = Image.open(f\"plot-{THEME}.png\").convert(\"RGB\")\n_w, _h = _img.size\nif _w > TW or _h > TH:\n    raise SystemExit(\n        f\"altair vl-convert produced {_w}×{_h}, exceeds target {TW}×{TH}. \"\n        f\"Shrink chart .properties(width=, height=) values and re-render.\"\n    )\nif _w < TW or _h < TH:\n    _canvas = Image.new(\"RGB\", (TW, TH), PAGE_BG)\n    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))\n    _canvas.save(f\"plot-{THEME}.png\")\n\n# Save HTML\nchart.save(f\"plot-{THEME}.html\")\n"}