{"spec_id":"strip-basic","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nstrip-basic: Basic Strip Plot\nLibrary: pygal 3.1.3 | Python 3.13.14\nQuality: 90/100 | Updated: 2026-08-05\n\"\"\"\n\nimport os\n\nimport numpy as np\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\nIMPRINT = (\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\")\n\n# Data — employee satisfaction scores by department (1–10 scale)\nnp.random.seed(42)\ncategories = [\"Engineering\", \"Marketing\", \"Sales\", \"Support\"]\nn_per_category = 40\n\nscores = {\n    \"Engineering\": np.clip(np.random.normal(7.5, 1.2, n_per_category), 1, 10),\n    \"Marketing\": np.clip(np.random.normal(6.8, 1.5, n_per_category), 1, 10),\n    \"Sales\": np.clip(np.random.normal(7.2, 1.0, n_per_category), 1, 10),\n    \"Support\": np.clip(np.random.normal(6.5, 1.8, n_per_category), 1, 10),\n}\n\n# Style\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=66,\n    label_font_size=56,\n    major_label_font_size=44,\n    legend_font_size=44,\n    value_font_size=36,\n    opacity=0.60,\n    stroke_width=2.5,\n)\n\n# Chart\nchart = pygal.XY(\n    width=3200,\n    height=1800,\n    style=custom_style,\n    title=\"strip-basic · pygal · anyplot.ai\",\n    x_title=\"Department\",\n    y_title=\"Satisfaction Score (1–10)\",\n    show_legend=True,\n    legend_at_bottom=True,\n    legend_at_bottom_columns=5,\n    show_x_guides=False,\n    show_y_guides=True,\n    stroke=False,\n    dots_size=17,\n    x_label_rotation=0,\n)\n\n# Pygal-native tooltip formatting: value_formatter applies to all hover labels\nchart.value_formatter = lambda y: f\"{y:.1f}\"\n\n# X-axis labels aligned to integer positions\nchart.x_labels = [\"\", \"Engineering\", \"Marketing\", \"Sales\", \"Support\", \"\"]\nchart.xrange = (0, 5)\n\n# Add jittered points per category; per-point dicts enrich HTML tooltips\nfor i, cat in enumerate(categories, start=1):\n    jitter = np.random.uniform(-0.25, 0.25, n_per_category)\n    points = [\n        {\"value\": (float(i + j), float(v)), \"label\": f\"{cat}: {v:.1f}\"}\n        for j, v in zip(jitter, scores[cat], strict=True)\n    ]\n    chart.add(cat, points)\n\n# Mean reference markers — one dot per category at the mean position (5th Imprint\n# color: #AE3030). Positioned at exact integer x (no jitter) so they stand apart\n# from the scattered data cloud. A larger dot plus a dashed connecting line (both\n# per-serie overrides on top of the scatter-only global style) makes the reference\n# layer unambiguous at a glance, rather than reading as a fifth data category.\nmean_points = [\n    {\"value\": (float(i), float(np.mean(scores[cat]))), \"label\": f\"Mean {cat}: {np.mean(scores[cat]):.2f}\"}\n    for i, cat in enumerate(categories, start=1)\n]\nchart.add(\"─ Mean\", mean_points, dots_size=32, stroke=True, stroke_style={\"width\": 4, \"dasharray\": \"10,6\"})\n\n# Save\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}