{"spec_id":"bar-stacked-labeled","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nbar-stacked-labeled: Stacked Bar Chart with Total Labels\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 85/100 | Updated: 2026-05-18\n\"\"\"\n\nimport os\nimport shutil\n\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    element_line,\n    element_rect,\n    element_text,\n    geom_bar,\n    geom_text,\n    ggplot,\n    ggsave,\n    ggsize,\n    labs,\n    scale_fill_manual,\n    theme,\n    theme_minimal,\n)\n\n\nLetsPlot.setup_html()\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\"]\n\n# Data - Quarterly revenue by product category (in millions)\ndata = {\n    \"Quarter\": [\"Q1\", \"Q1\", \"Q1\", \"Q2\", \"Q2\", \"Q2\", \"Q3\", \"Q3\", \"Q3\", \"Q4\", \"Q4\", \"Q4\"],\n    \"Product\": [\n        \"Electronics\",\n        \"Software\",\n        \"Services\",\n        \"Electronics\",\n        \"Software\",\n        \"Services\",\n        \"Electronics\",\n        \"Software\",\n        \"Services\",\n        \"Electronics\",\n        \"Software\",\n        \"Services\",\n    ],\n    \"Revenue\": [45, 32, 18, 52, 38, 22, 48, 41, 25, 58, 45, 28],\n}\ndf = pd.DataFrame(data)\n\n# Calculate totals for each quarter\ntotals = df.groupby(\"Quarter\", sort=False)[\"Revenue\"].sum().reset_index()\ntotals.columns = [\"Quarter\", \"Total\"]\ntotals[\"Label\"] = totals[\"Total\"].apply(lambda x: f\"${x}M\")\n\n# Create stacked bar chart with total labels\nplot = (\n    ggplot()\n    + geom_bar(\n        data=df, mapping=aes(x=\"Quarter\", y=\"Revenue\", fill=\"Product\"), stat=\"identity\", position=\"stack\", width=0.7\n    )\n    + geom_text(\n        data=totals,\n        mapping=aes(x=\"Quarter\", y=\"Total\", label=\"Label\"),\n        position=\"identity\",\n        vjust=-0.5,\n        size=18,\n        fontface=\"bold\",\n        color=INK,\n    )\n    + scale_fill_manual(values=IMPRINT)\n    + labs(\n        title=\"bar-stacked-labeled · Python · letsplot · anyplot.ai\",\n        x=\"Quarter\",\n        y=\"Revenue (Millions USD)\",\n        fill=\"Product\",\n    )\n    + theme_minimal()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        panel_grid_major=element_line(color=INK_SOFT, size=0.3),\n        axis_title=element_text(size=20, color=INK),\n        axis_text=element_text(size=16, color=INK_SOFT),\n        axis_line=element_line(color=INK_SOFT),\n        plot_title=element_text(size=24, face=\"bold\", color=INK),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_text=element_text(size=16, color=INK_SOFT),\n        legend_title=element_text(size=18, color=INK),\n    )\n    + ggsize(1600, 900)\n)\n\n# Save as PNG and HTML with theme suffix (scale 3x for 4800 × 2700 px)\nggsave(plot, f\"plot-{THEME}.png\", scale=3)\nggsave(plot, f\"plot-{THEME}.html\")\n\n# Move files from lets-plot-images subdirectory to current directory (lets-plot quirk)\nif os.path.exists(\"lets-plot-images\"):\n    for f in [f\"plot-{THEME}.png\", f\"plot-{THEME}.html\"]:\n        src = os.path.join(\"lets-plot-images\", f)\n        if os.path.exists(src):\n            shutil.move(src, f)\n    # Clean up subdirectory if empty\n    if not os.listdir(\"lets-plot-images\"):\n        shutil.rmtree(\"lets-plot-images\")\n"}