{"spec_id":"frequency-polygon-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nfrequency-polygon-basic: Frequency Polygon for Distribution Comparison\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\nimport site\nimport sys\n\n\n# Workaround for script name shadowing the altair package\nsys.path = (\n    site.getsitepackages()\n    + [site.getusersitepackages()]\n    + [p for p in sys.path if p not in site.getsitepackages() + [site.getusersitepackages()]]\n)\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\n\n\n# Theme tokens (Okabe-Ito palette + theme-adaptive chrome)\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\"\n\n# Okabe-Ito palette (colorblind-safe)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Data: Response times (ms) by experimental condition\nnp.random.seed(42)\n\n# Three groups with different distributions\ncontrol = np.random.normal(loc=450, scale=80, size=200)\ntreatment_a = np.random.normal(loc=380, scale=60, size=200)\ntreatment_b = np.random.normal(loc=420, scale=100, size=200)\n\n# Compute histogram bins aligned across all groups\nall_data = np.concatenate([control, treatment_a, treatment_b])\nbin_edges = np.histogram_bin_edges(all_data, bins=20)\nbin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2\n\n# Compute frequencies for each group and build polygon data\ndata_rows = []\nfor data, group_name in [(control, \"Control\"), (treatment_a, \"Treatment A\"), (treatment_b, \"Treatment B\")]:\n    counts, _ = np.histogram(data, bins=bin_edges)\n    # Extend to zero at both ends to close the polygon shape\n    x = np.concatenate([[bin_edges[0]], bin_centers, [bin_edges[-1]]])\n    y = np.concatenate([[0], counts, [0]])\n    for xi, yi in zip(x, y, strict=True):\n        data_rows.append({\"Response Time (ms)\": xi, \"Frequency\": yi, \"Condition\": group_name})\n\ndf = pd.DataFrame(data_rows)\n\n# Create frequency polygon chart\nchart = (\n    alt.Chart(df)\n    .mark_line(strokeWidth=3)\n    .encode(\n        x=alt.X(\"Response Time (ms):Q\", title=\"Response Time (ms)\"),\n        y=alt.Y(\"Frequency:Q\", title=\"Frequency\"),\n        color=alt.Color(\n            \"Condition:N\",\n            scale=alt.Scale(domain=[\"Control\", \"Treatment A\", \"Treatment B\"], range=IMPRINT),\n            legend=alt.Legend(title=\"Condition\", titleFontSize=20, labelFontSize=18),\n        ),\n        strokeDash=alt.StrokeDash(\n            \"Condition:N\",\n            scale=alt.Scale(domain=[\"Control\", \"Treatment A\", \"Treatment B\"], range=[[1, 0], [8, 4], [4, 4]]),\n            legend=None,\n        ),\n    )\n    .properties(width=1600, height=900, background=PAGE_BG)\n    .configure_title(fontSize=28, anchor=\"middle\", color=INK)\n    .configure_axis(\n        labelFontSize=18,\n        titleFontSize=22,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        gridOpacity=0.10,\n        gridColor=INK,\n    )\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, titleColor=INK, labelColor=INK_SOFT)\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT)\n)\n\n# Add title with format: spec-id · library · anyplot.ai\nchart = chart.properties(title=\"frequency-polygon-basic · altair · anyplot.ai\")\n\n# Save as PNG (4800 x 2700 px) and HTML\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}