{"spec_id":"bar-heart-rate-zones","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nbar-heart-rate-zones: Time in Heart Rate Zones Bar Chart\nLibrary: letsplot 4.10.1 | Python 3.13.13\nQuality: 87/100 | Created: 2026-06-14\n\"\"\"\n\nimport os\n\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_bar,\n    geom_text,\n    ggplot,\n    ggsave,\n    ggsize,\n    labs,\n    layer_tooltips,\n    scale_fill_manual,\n    scale_x_discrete,\n    scale_y_continuous,\n    theme,\n    theme_classic,\n)\n\n\nLetsPlot.setup_html()\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\"\nRULE = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Zone colors — semantic exception: conventional fitness platform zone palette\n# mapped to nearest Imprint palette members (Z1→muted grey, Z2→blue, Z3→green, Z4→ochre, Z5→red)\nZONE_COLORS = [INK_MUTED, \"#4467A3\", \"#009E73\", \"#BD8233\", \"#AE3030\"]\n\n# Data — 60-minute tempo run session\nzones = [\"Z1\", \"Z2\", \"Z3\", \"Z4\", \"Z5\"]\nminutes = [8, 22, 15, 12, 3]\nzone_names = [\"Recovery\", \"Endurance\", \"Aerobic\", \"Threshold\", \"Maximum\"]\nhr_ranges = [\"< 114 bpm\", \"114–133 bpm\", \"134–151 bpm\", \"152–171 bpm\", \"> 171 bpm\"]\ntotal = sum(minutes)  # 60\n\ndf = pd.DataFrame(\n    {\n        \"zone\": zones,\n        \"minutes\": [float(m) for m in minutes],\n        \"zone_name\": zone_names,\n        \"hr_range\": hr_ranges,\n        \"duration_label\": [f\"{m} min\" for m in minutes],\n        \"pct_label\": [f\"{round(m / total * 100)}%\" for m in minutes],\n        \"bar_label\": [f\"{m} min  ·  {round(m / total * 100)}%\" for m in minutes],\n    }\n)\ndf[\"zone\"] = pd.Categorical(df[\"zone\"], categories=zones, ordered=True)\n\n# X-axis labels: zone code, zone name, and HR boundary\nzone_axis_labels = {\n    \"Z1\": \"Z1  Recovery\\n< 114 bpm\",\n    \"Z2\": \"Z2  Endurance\\n114–133 bpm\",\n    \"Z3\": \"Z3  Aerobic\\n134–151 bpm\",\n    \"Z4\": \"Z4  Threshold\\n152–171 bpm\",\n    \"Z5\": \"Z5  Maximum\\n> 171 bpm\",\n}\n\ntitle = \"bar-heart-rate-zones · python · letsplot · anyplot.ai\"\nn_chars = len(title)\ntitle_size = round(16 * 67 / n_chars) if n_chars > 67 else 16\n\n# lets-plot distinctive feature: formatted interactive tooltip specification\ntooltips = (\n    layer_tooltips()\n    .title(\"@zone_name\")\n    .line(\"Duration|@duration_label\")\n    .line(\"Share|@pct_label\")\n    .line(\"Heart rate|@hr_range\")\n)\n\nplot = (\n    ggplot(df, aes(x=\"zone\", y=\"minutes\", fill=\"zone\"))\n    + geom_bar(stat=\"identity\", width=0.6, color=INK_SOFT, size=0.3, tooltips=tooltips)\n    + geom_text(aes(label=\"bar_label\"), vjust=-0.5, size=4, color=INK)\n    + scale_fill_manual(values=ZONE_COLORS, name=\"\")\n    + scale_x_discrete(labels=zone_axis_labels)\n    + scale_y_continuous(limits=[0, 27])\n    + labs(x=\"\", y=\"Time (minutes)\", title=title, subtitle=\"60-minute tempo run  ·  training intensity distribution\")\n    + ggsize(800, 450)\n    + theme_classic()\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_y=element_line(color=RULE, size=0.5),\n        panel_grid_major_x=element_blank(),\n        panel_grid_minor=element_blank(),\n        axis_title_y=element_text(color=INK, size=12),\n        axis_title_x=element_blank(),\n        axis_text=element_text(color=INK_SOFT, size=10),\n        axis_line=element_line(color=INK_SOFT),\n        axis_ticks=element_line(color=INK_SOFT),\n        plot_title=element_text(color=INK, size=title_size),\n        plot_subtitle=element_text(color=INK_SOFT, size=10),\n        legend_position=\"none\",\n    )\n)\n\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=4)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}