{"spec_id":"bar-heart-rate-zones","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nbar-heart-rate-zones: Time in Heart Rate Zones Bar Chart\nLibrary: plotnine 0.15.7 | Python 3.13.13\nQuality: 88/100 | Created: 2026-06-14\n\"\"\"\n\nimport os\n\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_bar,\n    geom_text,\n    ggplot,\n    labs,\n    scale_alpha_manual,\n    scale_fill_manual,\n    scale_y_continuous,\n    theme,\n)\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nGRID = \"#1A1A171E\" if THEME == \"light\" else \"#F0EFE81E\"\n\n# Heart rate zone colors — semantic exception to Imprint canonical order:\n# Z1–Z5 carry strong fitness conventions (grey/blue/green/orange/red),\n# mapped to the nearest Imprint palette members.\n# Z1 uses fixed #6B6A63 (not theme-adaptive INK_MUTED) so data colors\n# stay identical across light and dark renders.\nZONE_COLORS = [\"#6B6A63\", \"#4467A3\", \"#009E73\", \"#BD8233\", \"#AE3030\"]\n\n# Data — 75-minute endurance run with threshold intervals\nzones = [\"Z1\\nRecovery\", \"Z2\\nEndurance\", \"Z3\\nAerobic\", \"Z4\\nThreshold\", \"Z5\\nMaximum\"]\nminutes = [12.0, 38.0, 14.0, 9.0, 2.0]\n\ndf = pd.DataFrame({\"zone\": zones, \"minutes\": minutes})\ndf[\"zone\"] = pd.Categorical(df[\"zone\"], categories=zones, ordered=True)\ndf[\"duration_label\"] = df[\"minutes\"].apply(lambda m: f\"{int(m)} min\")\n# Z2 (Endurance) dominates at 51% — map emphasis aesthetic to guide reader focus\ndf[\"emphasis\"] = [\"secondary\", \"primary\", \"secondary\", \"secondary\", \"secondary\"]\n\ntitle = \"bar-heart-rate-zones · python · plotnine · anyplot.ai\"\ncaption = \"HR bounds: Z1 < 120 | Z2: 120–139 | Z3: 140–154 | Z4: 155–169 | Z5: ≥ 170 bpm\"\n\nplot = (\n    ggplot(df, aes(x=\"zone\", y=\"minutes\"))\n    + geom_bar(aes(fill=\"zone\", alpha=\"emphasis\"), stat=\"identity\", width=0.65, show_legend=False)\n    + geom_text(aes(label=\"duration_label\"), va=\"bottom\", nudge_y=0.8, size=4.0, color=INK)\n    + scale_fill_manual(values=dict(zip(zones, ZONE_COLORS, strict=True)))\n    + scale_alpha_manual(values={\"primary\": 1.0, \"secondary\": 0.7})\n    + scale_y_continuous(limits=(0, 45), expand=(0, 0), breaks=[0, 10, 20, 30, 40])\n    + labs(title=title, caption=caption, x=None, y=\"Duration (minutes)\")\n    + theme(\n        figure_size=(8, 4.5),\n        text=element_text(size=7),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        panel_grid_major_y=element_line(color=GRID, size=0.3),\n        panel_grid_major_x=element_blank(),\n        panel_grid_minor=element_blank(),\n        panel_border=element_blank(),\n        axis_line_x=element_line(color=INK_SOFT, size=0.5),\n        axis_line_y=element_blank(),\n        axis_ticks=element_blank(),\n        axis_title_x=element_blank(),\n        axis_title_y=element_text(color=INK, size=10),\n        axis_text_x=element_text(color=INK_SOFT, size=8),\n        axis_text_y=element_text(color=INK_SOFT, size=8),\n        plot_title=element_text(color=INK, size=12, ha=\"center\"),\n        plot_caption=element_text(color=INK_MUTED, size=8, ha=\"right\"),\n        legend_position=\"none\",\n    )\n)\n\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=8, height=4.5, units=\"in\")\n"}