{"spec_id":"swarm-basic","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nswarm-basic: Basic Swarm Plot\nLibrary: seaborn 0.13.2 | Python 3.13.14\nQuality: 94/100 | Updated: 2026-07-26\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\n\n\n# Theme tokens (see prompts/default-style-guide.md \"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# Imprint categorical palette — first series always #009E73\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\"]\n\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.15,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Data - Reaction times in a psychology response-time experiment\nnp.random.seed(42)\n\nconditions = [\"Control\", \"Distraction\", \"Time Pressure\", \"Fatigue\"]\nn_per_condition = [42, 36, 50, 38]\n\ndata = []\nfor condition, n in zip(conditions, n_per_condition, strict=True):\n    if condition == \"Control\":\n        # Fast, tightly clustered baseline responses\n        times = np.random.normal(420, 35, n)\n    elif condition == \"Distraction\":\n        # Slower on average, wider spread from divided attention\n        times = np.random.normal(480, 70, n)\n    elif condition == \"Time Pressure\":\n        # Bimodal: rushed guesses vs. deliberate, careful responses\n        times = np.concatenate([np.random.normal(350, 25, n // 2), np.random.normal(520, 40, n - n // 2)])\n    else:  # Fatigue\n        # Generally slower, with a few severe attention lapses\n        times = np.concatenate(\n            [\n                np.random.normal(460, 55, n - 4),\n                np.array([650, 700, 300, 310]),  # Lapses and rare quick guesses\n            ]\n        )\n\n    for rt in times:\n        data.append({\"Condition\": condition, \"Reaction Time\": np.clip(rt, 250, 750)})\n\ndf = pd.DataFrame(data)\n\n# Plot — figsize=(8, 4.5) @ dpi=400 → 3200×1800 (see prompts/library/seaborn.md \"Canvas\")\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\n\n# Distribution silhouette — subtle violin outline behind each swarm for density\n# context beyond the raw points (drawn first so the swarm layers on top)\nsns.violinplot(\n    data=df,\n    x=\"Condition\",\n    y=\"Reaction Time\",\n    hue=\"Condition\",\n    palette=IMPRINT_PALETTE,\n    fill=False,\n    inner=None,\n    cut=0,\n    width=0.7,\n    linewidth=1.3,\n    alpha=0.35,\n    legend=False,\n    ax=ax,\n)\n\nsns.swarmplot(\n    data=df,\n    x=\"Condition\",\n    y=\"Reaction Time\",\n    hue=\"Condition\",\n    palette=IMPRINT_PALETTE,\n    size=4,\n    alpha=0.85,\n    linewidth=0.3,\n    edgecolor=PAGE_BG,\n    ax=ax,\n    legend=False,\n)\n\n# Median markers — hollow diamonds so the focal point reads clearly without\n# swallowing the underlying points in dense categories (Distraction, Fatigue)\nmedians = df.groupby(\"Condition\")[\"Reaction Time\"].median()\nfor i, condition in enumerate(conditions):\n    ax.scatter(i, medians[condition], marker=\"D\", s=70, facecolor=\"none\", edgecolor=INK, linewidth=1.8, zorder=10)\nax.scatter([], [], marker=\"D\", s=55, facecolor=\"none\", edgecolor=INK, linewidth=1.8, label=\"Median\")\n\n# Style\nax.set_xlabel(\"Experimental Condition\", fontsize=10)\nax.set_ylabel(\"Reaction Time (ms)\", fontsize=10)\nax.set_title(\"swarm-basic · python · seaborn · anyplot.ai\", fontsize=12, fontweight=\"medium\")\n# Sample size folded into each tick label — quick n context without crowding the plot area\nax.set_xticks(range(len(conditions)))\nax.set_xticklabels([f\"{c}\\n(n={n})\" for c, n in zip(conditions, n_per_condition, strict=True)])\nax.tick_params(axis=\"both\", labelsize=8)\nax.yaxis.grid(True, alpha=0.15, linewidth=0.8)\nax.set_ylim(230, 780)\nax.legend(fontsize=8, loc=\"upper right\")\nsns.despine(ax=ax)\nfig.tight_layout(pad=1.2)\n\n# Save\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}