{"spec_id":"lollipop-grouped","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nlollipop-grouped: Grouped Lollipop Chart\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 99/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\nimport sys\n\n\n# Remove current directory from path to avoid shadowing the altair package\nsys.path = [p for p in sys.path if os.path.abspath(p) != os.getcwd()]\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\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\n# Okabe-Ito palette\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\"]\n\n# Data: Quarterly performance metrics across regions\nnp.random.seed(42)\n\ncategories = [\"North\", \"South\", \"East\", \"West\"]\nseries = [\"Q1 2024\", \"Q2 2024\", \"Q3 2024\"]\n\ndata = []\nbase_values = {\"North\": 85, \"South\": 72, \"East\": 78, \"West\": 90}\nquarter_adjustments = {\"Q1 2024\": 0, \"Q2 2024\": 5, \"Q3 2024\": 10}\n\nfor cat in categories:\n    for ser in series:\n        value = base_values[cat] + quarter_adjustments[ser] + np.random.randint(-8, 12)\n        data.append({\"category\": cat, \"series\": ser, \"value\": value})\n\ndf = pd.DataFrame(data)\n\n# Create offset positions for grouped lollipops\ndf[\"series_order\"] = df[\"series\"].map({s: i for i, s in enumerate(series)})\ndf[\"x_offset\"] = (df[\"series_order\"] - (len(series) - 1) / 2) * 0.25\n\n# Color scale using Okabe-Ito palette\ncolor_scale = alt.Scale(domain=series, range=IMPRINT[: len(series)])\n\n# Base chart\nbase = alt.Chart(df).encode(\n    x=alt.X(\"category:N\", title=\"Region\", axis=alt.Axis(labelFontSize=18, titleFontSize=22)),\n    xOffset=\"x_offset:Q\",\n    color=alt.Color(\n        \"series:N\",\n        scale=color_scale,\n        title=\"Quarter\",\n        legend=alt.Legend(titleFontSize=18, labelFontSize=16, symbolSize=200),\n    ),\n    tooltip=[\"category:N\", \"series:N\", alt.Tooltip(\"value:Q\", format=\".0f\")],\n)\n\n# Stems (lines from 0 to value)\nstems = base.mark_rule(strokeWidth=4).encode(\n    y=alt.Y(\n        \"value:Q\",\n        title=\"Performance Score\",\n        axis=alt.Axis(labelFontSize=18, titleFontSize=22),\n        scale=alt.Scale(domain=[0, 110]),\n    ),\n    y2=alt.datum(0),\n)\n\n# Markers (circular dots)\nmarkers = base.mark_circle(size=400, opacity=1).encode(y=alt.Y(\"value:Q\"))\n\n# Combine stems and markers\nchart = (\n    (stems + markers)\n    .properties(\n        width=1600,\n        height=900,\n        background=PAGE_BG,\n        title=alt.Title(\"lollipop-grouped · altair · pyplots.ai\", fontSize=28, anchor=\"middle\"),\n    )\n    .configure_axis(\n        grid=True,\n        gridOpacity=0.10,\n        gridColor=INK_SOFT,\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n    )\n    .configure_view(strokeWidth=0, fill=PAGE_BG)\n    .configure_title(color=INK)\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\n# Save outputs\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}