{"spec_id":"violin-swarm","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nviolin-swarm: Violin Plot with Overlaid Swarm Points\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-18\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_jitter,\n    geom_violin,\n    ggplot,\n    ggsize,\n    labs,\n    scale_fill_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\"\n\n# Okabe-Ito palette\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\"]\n\n# Data: Reaction times (ms) across 4 experimental conditions\nnp.random.seed(42)\n\nconditions = [\"Control\", \"Low Dose\", \"Medium Dose\", \"High Dose\"]\nn_per_group = 50\n\ndata = []\nfor condition in conditions:\n    if condition == \"Control\":\n        # Normal distribution centered at 450ms\n        values = np.random.normal(450, 60, n_per_group)\n    elif condition == \"Low Dose\":\n        # Slightly faster, narrower distribution\n        values = np.random.normal(420, 50, n_per_group)\n    elif condition == \"Medium Dose\":\n        # Faster with some variability\n        values = np.random.normal(380, 70, n_per_group)\n    else:  # High Dose\n        # Fastest but bimodal (some responders, some non-responders)\n        responders = np.random.normal(320, 40, n_per_group // 2)\n        non_responders = np.random.normal(400, 35, n_per_group - n_per_group // 2)\n        values = np.concatenate([responders, non_responders])\n\n    for v in values:\n        data.append({\"Condition\": condition, \"Reaction Time\": v})\n\ndf = pd.DataFrame(data)\n\n# Ensure categorical order\ndf[\"Condition\"] = pd.Categorical(df[\"Condition\"], categories=conditions, ordered=True)\n\n# Create plot with violin and overlaid swarm points\nplot = (\n    ggplot(df, aes(x=\"Condition\", y=\"Reaction Time\"))\n    + geom_violin(aes(fill=\"Condition\"), alpha=0.4, size=1.2)\n    + geom_jitter(aes(color=\"Condition\"), width=0.12, height=0, size=3.5, alpha=0.85)\n    + scale_fill_manual(values=IMPRINT)\n    + theme_minimal()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        panel_grid_major=element_line(color=INK_SOFT, size=0.3),\n        panel_grid_minor=element_blank(),\n        plot_title=element_text(size=24, color=INK),\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),\n        legend_position=\"none\",\n    )\n    + labs(x=\"Experimental Condition\", y=\"Reaction Time (ms)\", title=\"violin-swarm · Python · letsplot · anyplot.ai\")\n    + ggsize(1600, 900)\n)\n\n# Save PNG (scale 3x for 4800 × 2700 px) and HTML\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=3)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}