{"spec_id":"lollipop-grouped","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nlollipop-grouped: Grouped Lollipop Chart\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 89/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\n# Theme tokens (see prompts/default-style-guide.md)\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\"\nGRID = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\n# Okabe-Ito palette (first series is always #009E73)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Data - Quarterly revenue by product line across regions (in millions $)\nnp.random.seed(42)\ncategories = [\"North America\", \"Europe\", \"Asia Pacific\", \"Latin America\", \"Middle East\"]\nseries_names = [\"Electronics\", \"Apparel\", \"Home Goods\"]\n\n# Generate realistic revenue data\nrevenue_data = {\n    \"Electronics\": [42.5, 38.2, 51.3, 18.7, 12.4],\n    \"Apparel\": [28.3, 45.6, 32.1, 22.8, 8.9],\n    \"Home Goods\": [35.1, 29.8, 25.4, 15.2, 10.6],\n}\n\n# Create figure\nfig = go.Figure()\n\n# Calculate positions for grouped lollipops\nn_categories = len(categories)\nn_series = len(series_names)\ngroup_width = 0.7\nbar_width = group_width / n_series\nx_base = np.arange(n_categories)\n\n# Add stems and markers for each series\nfor i, (series, color) in enumerate(zip(series_names, IMPRINT, strict=False)):\n    # Calculate x positions with offset for grouping\n    offset = (i - (n_series - 1) / 2) * bar_width\n    x_positions = x_base + offset\n    values = revenue_data[series]\n\n    # Add stems (lines from 0 to value)\n    for x_pos, val in zip(x_positions, values, strict=False):\n        fig.add_trace(\n            go.Scatter(\n                x=[x_pos, x_pos],\n                y=[0, val],\n                mode=\"lines\",\n                line={\"color\": color, \"width\": 3},\n                showlegend=False,\n                hoverinfo=\"skip\",\n            )\n        )\n\n    # Add markers (dots at the top)\n    fig.add_trace(\n        go.Scatter(\n            x=x_positions,\n            y=values,\n            mode=\"markers\",\n            marker={\"size\": 16, \"color\": color, \"line\": {\"color\": PAGE_BG, \"width\": 2}},\n            name=series,\n            hovertemplate=\"%{text}<br>%{y:.1f}M $<extra></extra>\",\n            text=[f\"{series} - {cat}\" for cat in categories],\n        )\n    )\n\n# Update layout\nfig.update_layout(\n    title={\n        \"text\": \"lollipop-grouped · Python · plotly · anyplot.ai\",\n        \"font\": {\"size\": 28, \"color\": INK},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n    },\n    xaxis={\n        \"title\": {\"text\": \"Region\", \"font\": {\"size\": 22, \"color\": INK}},\n        \"tickfont\": {\"size\": 18, \"color\": INK_SOFT},\n        \"tickvals\": x_base,\n        \"ticktext\": categories,\n        \"showgrid\": False,\n        \"linecolor\": INK_SOFT,\n        \"zerolinecolor\": INK_SOFT,\n    },\n    yaxis={\n        \"title\": {\"text\": \"Quarterly Revenue (Million $)\", \"font\": {\"size\": 22, \"color\": INK}},\n        \"tickfont\": {\"size\": 18, \"color\": INK_SOFT},\n        \"gridcolor\": GRID,\n        \"gridwidth\": 1,\n        \"zeroline\": True,\n        \"zerolinecolor\": INK_SOFT,\n        \"zerolinewidth\": 2,\n        \"linecolor\": INK_SOFT,\n    },\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    legend={\n        \"font\": {\"size\": 16, \"color\": INK_SOFT},\n        \"bgcolor\": ELEVATED_BG,\n        \"bordercolor\": INK_SOFT,\n        \"borderwidth\": 1,\n        \"orientation\": \"h\",\n        \"yanchor\": \"bottom\",\n        \"y\": 1.05,\n        \"xanchor\": \"center\",\n        \"x\": 0.5,\n    },\n    margin={\"l\": 100, \"r\": 40, \"t\": 140, \"b\": 100},\n    hovermode=\"closest\",\n)\n\n# Save as PNG and HTML\nfig.write_image(f\"plot-{THEME}.png\", width=1600, height=900, scale=3)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}