{"spec_id":"frequency-polygon-basic","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nfrequency-polygon-basic: Frequency Polygon for Distribution Comparison\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\nimport sys\n\nimport numpy as np\n\n\n# Remove current directory from path to avoid shadowing pygal module\ncurrent_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p) != current_dir]\n\nimport pygal\nfrom pygal.style import Style\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_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Okabe-Ito palette (first series is always #009E73)\nIMPRINT = (\"#009E73\", \"#C475FD\", \"#4467A3\")\n\n# Data - Three groups of measurements (plant heights by soil type)\nnp.random.seed(42)\n\n# Generate three distributions with different characteristics\ngroup_a = np.random.normal(loc=45, scale=8, size=200)  # Sandy soil - lower mean\ngroup_b = np.random.normal(loc=55, scale=10, size=200)  # Loamy soil - medium mean, wider spread\ngroup_c = np.random.normal(loc=60, scale=6, size=200)  # Clay soil - higher mean, narrow spread\n\n# Define bins for frequency calculation\nbins = np.linspace(20, 85, 15)\nbin_midpoints = (bins[:-1] + bins[1:]) / 2\n\n# Calculate frequencies for each group\nfreq_a, _ = np.histogram(group_a, bins=bins)\nfreq_b, _ = np.histogram(group_b, bins=bins)\nfreq_c, _ = np.histogram(group_c, bins=bins)\n\n# Extend lines to zero at both ends to close the polygon shape\nmidpoints_extended = [float(bins[0])] + [float(m) for m in bin_midpoints] + [float(bins[-1])]\nfreq_a_extended = [0] + [int(f) for f in freq_a] + [0]\nfreq_b_extended = [0] + [int(f) for f in freq_b] + [0]\nfreq_c_extended = [0] + [int(f) for f in freq_c] + [0]\n\n# Custom style for large canvas (4800x2700) with theme-adaptive colors\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    colors=IMPRINT,\n    title_font_size=28,\n    label_font_size=22,\n    major_label_font_size=18,\n    legend_font_size=16,\n    value_font_size=14,\n    opacity=0.8,\n    opacity_hover=1.0,\n)\n\n# Create XY chart for frequency polygon with fill\nchart = pygal.XY(\n    width=4800,\n    height=2700,\n    style=custom_style,\n    title=\"frequency-polygon-basic · pygal · anyplot.ai\",\n    x_title=\"Plant Height (cm)\",\n    y_title=\"Frequency\",\n    show_x_guides=True,\n    show_y_guides=True,\n    dots_size=10,\n    stroke_style={\"width\": 4},\n    legend_at_bottom=True,\n    show_dots=True,\n    fill=True,\n    x_label_rotation=0,\n    range=(0, int(max(max(freq_a), max(freq_b), max(freq_c))) + 5),\n    xrange=(15, 90),\n)\n\n# Add data series - each point is (x, y) tuple\nchart.add(\"Sandy Soil\", list(zip(midpoints_extended, freq_a_extended, strict=True)))\nchart.add(\"Loamy Soil\", list(zip(midpoints_extended, freq_b_extended, strict=True)))\nchart.add(\"Clay Soil\", list(zip(midpoints_extended, freq_c_extended, strict=True)))\n\n# Save as PNG and HTML with theme-suffixed filenames\nchart.render_to_png(f\"plot-{THEME}.png\")\nchart.render_to_file(f\"plot-{THEME}.html\")\n"}