{"spec_id":"violin-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nviolin-basic: Basic Violin Plot\nLibrary: letsplot 4.10.1 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-29\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_boxplot,\n    geom_violin,\n    ggplot,\n    ggsave,\n    ggsize,\n    labs,\n    layer_tooltips,\n    scale_fill_manual,\n    scale_x_discrete,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\n\n\nLetsPlot.setup_html()\n\n# Theme tokens\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 palette — canonical positions 1-4\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\"]\n\n# Data\nnp.random.seed(42)\n\n# Ordered by median salary (high → low) for visual storytelling\ndept_order = [\"Engineering\", \"Design\", \"Marketing\", \"Sales\"]\n\ndata = []\n\n# Engineering: bimodal (junior ~$70k + senior ~$115k) — showcases violin strength\neng_junior = np.random.normal(70000, 8000, 80)\neng_senior = np.random.normal(115000, 12000, 120)\neng_values = np.clip(np.concatenate([eng_junior, eng_senior]), 30000, 200000)\nfor v in eng_values:\n    data.append({\"Department\": \"Engineering\", \"Salary\": v})\n\n# Design: moderate spread, roughly normal\ndesign_values = np.clip(np.random.normal(80000, 18000, 120), 30000, 200000)\nfor v in design_values:\n    data.append({\"Department\": \"Design\", \"Salary\": v})\n\n# Marketing: narrower with a small cluster of high earners\nmkt_base = np.random.normal(72000, 12000, 130)\nmkt_high = np.random.normal(105000, 8000, 20)\nmkt_values = np.clip(np.concatenate([mkt_base, mkt_high]), 30000, 200000)\nfor v in mkt_values:\n    data.append({\"Department\": \"Marketing\", \"Salary\": v})\n\n# Sales: right-skewed (many moderate earners, few top performers)\nsales_values = np.clip(np.random.exponential(20000, 180) + 45000, 30000, 200000)\nfor v in sales_values:\n    data.append({\"Department\": \"Sales\", \"Salary\": v})\n\ndf = pd.DataFrame(data)\n\ntitle = \"violin-basic · python · letsplot · anyplot.ai\"\n\n# Plot — violins colored by Imprint palette, thin boxplot overlay for clear quartile markers\nplot = (\n    ggplot(df, aes(x=\"Department\", y=\"Salary\", fill=\"Department\"))\n    + geom_violin(\n        alpha=0.82,\n        trim=True,\n        color=INK_SOFT,\n        size=0.8,\n        tooltips=layer_tooltips().format(\"@Salary\", \"${,.0f}\").line(\"^fill\").line(\"Salary|@Salary\"),\n    )\n    + geom_boxplot(\n        width=0.10,\n        fill=PAGE_BG,\n        color=INK,\n        size=1.2,\n        outlier_color=PAGE_BG,\n        outlier_fill=PAGE_BG,\n        tooltips=layer_tooltips()\n        .format(\"@{..middle..}\", \"${,.0f}\")\n        .format(\"@{..lower..}\", \"${,.0f}\")\n        .format(\"@{..upper..}\", \"${,.0f}\")\n        .line(\"^fill\")\n        .line(\"Median|@{..middle..}\")\n        .line(\"IQR|@{..lower..} – @{..upper..}\"),\n    )\n    + scale_x_discrete(limits=dept_order)\n    + scale_fill_manual(values=dict(zip(dept_order, IMPRINT_PALETTE, strict=True)))\n    + scale_y_continuous(format=\"${,.0f}\")\n    + labs(x=\"Department\", y=\"Salary\", title=title)\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_y=element_line(color=INK_SOFT, size=0.2),\n        panel_grid_major_x=element_blank(),\n        panel_grid_minor=element_blank(),\n        axis_title=element_text(color=INK, size=12),\n        axis_text=element_text(color=INK_SOFT, size=10),\n        plot_title=element_text(color=INK, size=16),\n        legend_position=\"none\",\n        axis_ticks=element_blank(),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_text=element_text(color=INK_SOFT),\n    )\n    + ggsize(800, 450)\n)\n\n# Save\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=4)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}