{"spec_id":"violin-split","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nviolin-split: Split Violin Plot\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-05-08\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\nfrom lets_plot.export import ggsave\n\nLetsPlot.setup_html()\n\n# Theme tokens (see prompts/default-style-guide.md)\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\"\nGRID_LINE = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\n# Okabe-Ito palette (first series always #009E73)\nCOLOR_BEFORE = \"#009E73\"  # Okabe-Ito position 1\nCOLOR_AFTER = \"#C475FD\"  # Okabe-Ito position 2\n\n# Data - Employee satisfaction scores before and after office redesign across departments\nnp.random.seed(42)\n\ndepartments = [\"Engineering\", \"Marketing\", \"Sales\", \"Design\"]\ndata = []\n\n# Generate realistic distributions showing varied effects of office redesign\ndistributions = {\n    \"Engineering\": {\"Before\": {\"mean\": 65, \"std\": 12}, \"After\": {\"mean\": 78, \"std\": 10}},\n    \"Marketing\": {\"Before\": {\"mean\": 58, \"std\": 15}, \"After\": {\"mean\": 72, \"std\": 11}},\n    \"Sales\": {\"Before\": {\"mean\": 70, \"std\": 14}, \"After\": {\"mean\": 75, \"std\": 12}},\n    \"Design\": {\"Before\": {\"mean\": 55, \"std\": 18}, \"After\": {\"mean\": 82, \"std\": 8}},\n}\n\nfor dept in departments:\n    for period in [\"Before\", \"After\"]:\n        params = distributions[dept][period]\n        n_samples = np.random.randint(80, 150)\n        values = np.random.normal(params[\"mean\"], params[\"std\"], n_samples)\n        values = np.clip(values, 20, 100)\n        for v in values:\n            data.append({\"Department\": dept, \"Satisfaction\": v, \"Period\": period})\n\ndf = pd.DataFrame(data)\n\n# Prepare data for split violin\ndf_before = df[df[\"Period\"] == \"Before\"].copy()\ndf_after = df[df[\"Period\"] == \"After\"].copy()\n\n# Create split violin plot\nplot = (\n    ggplot()\n    # Left half: Before (show_half=-1)\n    + geom_violin(\n        aes(x=\"Department\", y=\"Satisfaction\", fill=\"Period\"),\n        data=df_before,\n        show_half=-1,\n        trim=False,\n        size=0.8,\n        alpha=0.75,\n    )\n    # Right half: After (show_half=1)\n    + geom_violin(\n        aes(x=\"Department\", y=\"Satisfaction\", fill=\"Period\"),\n        data=df_after,\n        show_half=1,\n        trim=False,\n        size=0.8,\n        alpha=0.75,\n    )\n    # Inner quartile lines for distribution visualization\n    + geom_boxplot(\n        aes(x=\"Department\", y=\"Satisfaction\", fill=\"Period\"),\n        data=df_before,\n        width=0.08,\n        outlier_shape=None,\n        position=position_nudge(x=-0.05),\n        alpha=0.9,\n        size=0.6,\n        show_legend=False,\n    )\n    + geom_boxplot(\n        aes(x=\"Department\", y=\"Satisfaction\", fill=\"Period\"),\n        data=df_after,\n        width=0.08,\n        outlier_shape=None,\n        position=position_nudge(x=0.05),\n        alpha=0.9,\n        size=0.6,\n        show_legend=False,\n    )\n    # Okabe-Ito colors (Before=#009E73, After=#C475FD)\n    + scale_fill_manual(values=[COLOR_AFTER, COLOR_BEFORE], name=\"Period\")\n    + scale_y_continuous(limits=[15, 105])\n    # Labels\n    + labs(x=\"Department\", y=\"Satisfaction Score (0-100)\", title=\"violin-split · letsplot · anyplot.ai\")\n    # Theme-adaptive styling\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_x=element_blank(),\n        panel_grid_minor=element_blank(),\n        panel_grid_major_y=element_line(color=GRID_LINE, size=0.4),\n        plot_title=element_text(size=24, face=\"bold\", color=INK),\n        axis_title_x=element_text(size=20, color=INK),\n        axis_title_y=element_text(size=20, color=INK),\n        axis_text_x=element_text(size=16, color=INK_SOFT),\n        axis_text_y=element_text(size=16, color=INK_SOFT),\n        axis_line=element_line(color=INK_SOFT, size=0.4),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_title=element_text(size=18, color=INK),\n        legend_text=element_text(size=16, color=INK_SOFT),\n        legend_position=\"right\",\n    )\n    + ggsize(1600, 900)\n)\n\n# Save outputs (scale 3x to get 4800 x 2700 px)\nggsave(plot, f\"plot-{THEME}.png\", scale=3)\nggsave(plot, f\"plot-{THEME}.html\")\n"}