{"spec_id":"histogram-cumulative","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nhistogram-cumulative: Cumulative Histogram\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 93/100 | Updated: 2026-05-11\n\"\"\"\n\nimport sys\n\n\nsys.path = [p for p in sys.path if p and not p.endswith(\"/python\")]\n\nimport os\n\nimport numpy as np\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_vline,\n    ggplot,\n    ggsave,\n    labs,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\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\"\nBRAND = \"#009E73\"\n\n# Data - Product shelf life measurements (days until expiration)\nnp.random.seed(42)\nshelf_life = np.concatenate([np.random.normal(45, 8, 300), np.random.normal(65, 5, 150)])\n\n# Calculate cumulative histogram\nn_bins = 25\nhist, bin_edges = np.histogram(shelf_life, bins=n_bins)\ncumulative_counts = np.cumsum(hist)\nbin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2\nbin_width = bin_edges[1] - bin_edges[0]\n\n# Create DataFrame with cumulative counts\ndf_hist = pd.DataFrame({\"bin_center\": bin_centers, \"cumulative_count\": cumulative_counts})\n\ntotal = len(shelf_life)\n\n# Theme - L-shaped spine (remove top and right), improve visual hierarchy\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_grid_major=element_line(color=INK, size=0.25, alpha=0.08),\n    panel_grid_minor=element_blank(),\n    panel_border=element_blank(),\n    axis_title=element_text(color=INK, size=20, margin={\"b\": 10, \"l\": 10}),\n    axis_text=element_text(color=INK_SOFT, size=16),\n    axis_line=element_line(color=INK_SOFT, size=0.5),\n    axis_ticks=element_line(color=INK_SOFT, size=0.5),\n    plot_title=element_text(color=INK, size=24, margin={\"b\": 15}),\n    figure_size=(16, 9),\n)\n\n# Calculate quartile positions for visual reference\nq1_idx = np.searchsorted(cumulative_counts, total * 0.25)\nq2_idx = np.searchsorted(cumulative_counts, total * 0.50)\nq3_idx = np.searchsorted(cumulative_counts, total * 0.75)\n\nq1_x = bin_centers[q1_idx] if q1_idx < len(bin_centers) else bin_centers[-1]\nq2_x = bin_centers[q2_idx] if q2_idx < len(bin_centers) else bin_centers[-1]\nq3_x = bin_centers[q3_idx] if q3_idx < len(bin_centers) else bin_centers[-1]\n\n# Plot - with visual emphasis on quartile positions\nplot = (\n    ggplot(df_hist, aes(x=\"bin_center\", y=\"cumulative_count\"))\n    + geom_bar(stat=\"identity\", width=bin_width * 0.95, fill=BRAND, color=INK_SOFT, alpha=0.85, size=0.3)\n    + geom_vline(xintercept=q1_x, linetype=\"dashed\", color=INK_SOFT, size=0.4, alpha=0.4)\n    + geom_vline(xintercept=q2_x, linetype=\"dashed\", color=INK_SOFT, size=0.5, alpha=0.6)\n    + geom_vline(xintercept=q3_x, linetype=\"dashed\", color=INK_SOFT, size=0.4, alpha=0.4)\n    + labs(\n        x=\"Shelf Life (days)\",\n        y=\"Cumulative Count\",\n        title=\"histogram-cumulative · plotnine · anyplot.ai\",\n        subtitle=\"Quartile positions shown as reference lines\",\n    )\n    + scale_y_continuous(breaks=[0, 100, 200, 300, 400, total], limits=(0, total + 20))\n    + theme_minimal()\n    + anyplot_theme\n)\n\n# Save\nscript_dir = os.path.dirname(os.path.abspath(__file__))\noutput_file = os.path.join(script_dir, f\"plot-{THEME}.png\")\nggsave(plot, filename=output_file, dpi=300, width=16, height=9, verbose=False)\n"}