{"spec_id":"frequency-polygon-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nfrequency-polygon-basic: Frequency Polygon for Distribution Comparison\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    element_line,\n    element_rect,\n    element_text,\n    geom_freqpoly,\n    ggplot,\n    ggsize,\n    labs,\n    scale_color_manual,\n    theme,\n    theme_minimal,\n)\nfrom lets_plot.export import ggsave\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\"\nRULE = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\n# Okabe-Ito palette (first series is brand green #009E73)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Data - Response times (ms) across three experimental conditions\nnp.random.seed(42)\n\n# Control group: normal distribution centered at 350ms\ncontrol = np.random.normal(loc=350, scale=60, size=200)\n\n# Treatment A: slightly faster responses, centered at 300ms\ntreatment_a = np.random.normal(loc=300, scale=50, size=200)\n\n# Treatment B: bimodal - mix of fast and slow responders\ntreatment_b = np.concatenate(\n    [np.random.normal(loc=280, scale=40, size=120), np.random.normal(loc=420, scale=45, size=80)]\n)\n\n# Combine into DataFrame\ndf = pd.DataFrame(\n    {\n        \"response_time\": np.concatenate([control, treatment_a, treatment_b]),\n        \"condition\": ([\"Control\"] * 200 + [\"Treatment A\"] * 200 + [\"Treatment B\"] * 200),\n    }\n)\n\n# Create frequency polygon with theme-adaptive styling\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=RULE, size=0.3),\n    panel_grid_minor=element_line(color=RULE, size=0.15),\n    axis_title=element_text(size=20, color=INK),\n    axis_text=element_text(size=16, color=INK_SOFT),\n    axis_line=element_line(color=INK_SOFT, size=0.5),\n    plot_title=element_text(size=24, color=INK),\n    legend_position=\"right\",\n    legend_text=element_text(size=16, color=INK_SOFT),\n    legend_title=element_text(size=16, color=INK),\n)\n\nplot = (\n    ggplot(df, aes(x=\"response_time\", color=\"condition\"))\n    + geom_freqpoly(bins=25, size=2.5, alpha=0.9)\n    + scale_color_manual(values=IMPRINT)\n    + labs(\n        x=\"Response Time (ms)\",\n        y=\"Frequency\",\n        title=\"frequency-polygon-basic · letsplot · anyplot.ai\",\n        color=\"Condition\",\n    )\n    + theme_minimal()\n    + anyplot_theme\n    + ggsize(1600, 900)\n)\n\n# Save PNG (scale 3x for 4800 × 2700 px)\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=3)\n\n# Save HTML for interactive version\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}