{"spec_id":"histogram-overlapping","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nhistogram-overlapping: Overlapping Histograms\nLibrary: pygal 3.1.3 | Python 3.13.15\nQuality: 91/100 | Updated: 2026-08-18\n\"\"\"\n\nimport os\nimport sys\n\n\nsys.path = [p for p in sys.path if not p.endswith(\"python\")]\n\nimport numpy as np\nimport pygal\nfrom pygal.style import Style\n\n\n# Theme tokens (see prompts/default-style-guide.md \"Background\" + \"Theme-adaptive Chrome\")\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint categorical palette — first series is always brand green\nIMPRINT_PALETTE = (\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\n\n# Data - checkout completion time (seconds) for an A/B test on a streamlined\n# checkout flow. Control keeps the current multi-step form; Treatment trims it\n# to one page. Times are clipped at 5s — a real checkout can't complete faster.\nnp.random.seed(42)\ncontrol_times = np.clip(np.random.normal(52, 14, 200), 5, None)\ntreatment_times = np.clip(np.random.normal(41, 11, 200), 5, None)\n\n# Histogram parameters - shared bin edges for a fair overlap comparison\nbin_min = 0\nbin_max = 100\nn_bins = 20\nbin_edges = np.linspace(bin_min, bin_max, n_bins + 1)\n\n# Pygal Histogram expects tuples of (count, start, end)\nhist_control, _ = np.histogram(control_times, bins=bin_edges)\nhist_treatment, _ = np.histogram(treatment_times, bins=bin_edges)\n\ncontrol_data = [(int(count), float(bin_edges[i]), float(bin_edges[i + 1])) for i, count in enumerate(hist_control)]\ntreatment_data = [(int(count), float(bin_edges[i]), float(bin_edges[i + 1])) for i, count in enumerate(hist_treatment)]\n\n# Mean shift, worked into the legend labels below, tells the A/B story at a glance\ncontrol_mean = float(control_times.mean())\ntreatment_mean = float(treatment_times.mean())\n\n# Style — theme-adaptive chrome, Imprint palette, sizing tuned for 3200x1800\ncustom_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    # pygal's graph.css ships guide_stroke_color=\"black\" and layers it over\n    # style.css's foreground_subtle rule for the same selector, so gridlines\n    # render pure black regardless of theme unless overridden here — nearly\n    # invisible against the near-black dark-theme background.\n    guide_stroke_color=INK_MUTED,\n    major_guide_stroke_color=INK_MUTED,\n    opacity=0.55,\n    opacity_hover=0.75,\n    colors=IMPRINT_PALETTE,\n    title_font_size=66,\n    label_font_size=56,\n    major_label_font_size=44,\n    legend_font_size=44,\n    value_font_size=36,\n    stroke_width=2.5,\n)\n\n# Chart — semi-transparent overlapping bars on shared bins, y-axis grid only,\n# bottom legend to keep the plot area uncluttered.\nchart = pygal.Histogram(\n    width=3200,\n    height=1800,\n    style=custom_style,\n    title=\"histogram-overlapping · python · pygal · anyplot.ai\",\n    x_title=\"Checkout Time (seconds)\",\n    y_title=\"Frequency\",\n    show_legend=True,\n    legend_at_bottom=True,\n    legend_box_size=36,\n    show_y_guides=True,\n    show_x_guides=False,\n    x_label_rotation=0,\n    margin=60,\n    value_formatter=lambda x: f\"{x:.0f}\",\n    tooltip_border_radius=10,\n    tooltip_fancy_mode=True,\n    rounded_bars=4,\n    # Pygal-native interactivity: counts stay hidden in the static PNG and\n    # reveal per-bar on hover in the exported HTML, instead of a plain tooltip.\n    print_values=True,\n    dynamic_print_values=True,\n    print_values_position=\"top\",\n)\n\n# Add data series - mean shift folded into the legend labels tells the A/B\n# story directly (Treatment moves the mean ~21% faster than Control)\nchart.add(f\"Control (current flow, mean {control_mean:.0f}s)\", control_data)\nchart.add(f\"Treatment (streamlined flow, mean {treatment_mean:.0f}s)\", treatment_data)\n\n# Save outputs\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}