{"spec_id":"raincloud-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nraincloud-basic: Basic Raincloud Plot\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 93/100 | Updated: 2026-05-26\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom scipy.stats import gaussian_kde\n\n\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 positions 1-3\nCOLORS = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Data: Reaction times (ms) for three experimental conditions\nnp.random.seed(42)\n\n# Control: roughly normal around 350 ms\ncontrol = np.random.normal(350, 50, 80)\n\n# Treatment A: clearly bimodal — two response strategies\ntreatment_a = np.concatenate([np.random.normal(250, 25, 45), np.random.normal(340, 25, 35)])\n\n# Treatment B: mostly central with a slow tail and a few outliers\ntreatment_b = np.concatenate([np.random.normal(300, 40, 60), np.random.normal(400, 25, 15), np.array([500, 520, 480])])\n\ncategories = [\"Control\", \"Treatment A\", \"Treatment B\"]\ndata = [control, treatment_a, treatment_b]\n\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Raincloud layout: cloud above baseline, boxplot on baseline, rain below\ncloud_offset = 0.05\ncloud_height = 0.32\nrain_offset = -0.22\nbox_width = 0.08\n\nfor i, (d, color) in enumerate(zip(data, COLORS, strict=True)):\n    pos = i + 1\n\n    # Cloud — half-violin via scipy KDE, Silverman's rule\n    kde = gaussian_kde(d, bw_method=\"silverman\")\n    x_min, x_max = d.min() - 30, d.max() + 30\n    x_vals = np.linspace(x_min, x_max, 256)\n    density = kde(x_vals)\n    density_scaled = density / density.max() * cloud_height\n\n    ax.fill_between(\n        x_vals,\n        pos + cloud_offset,\n        pos + cloud_offset + density_scaled,\n        color=color,\n        alpha=0.75,\n        edgecolor=color,\n        linewidth=0.8,\n        zorder=1,\n    )\n\n    # Boxplot — sits on the category baseline\n    bp = ax.boxplot([d], positions=[pos], widths=box_width, vert=False, patch_artist=True, showfliers=False, zorder=3)\n    bp[\"boxes\"][0].set_facecolor(ELEVATED_BG)\n    bp[\"boxes\"][0].set_edgecolor(INK_SOFT)\n    bp[\"boxes\"][0].set_linewidth(1.2)\n    bp[\"medians\"][0].set_color(INK)\n    bp[\"medians\"][0].set_linewidth(1.6)\n    for whisker in bp[\"whiskers\"]:\n        whisker.set_color(INK_SOFT)\n        whisker.set_linewidth(1.0)\n    for cap in bp[\"caps\"]:\n        cap.set_color(INK_SOFT)\n        cap.set_linewidth(1.0)\n\n    # Rain — jittered points below the baseline\n    jitter = np.random.uniform(-0.07, 0.07, len(d))\n    ax.scatter(\n        d,\n        np.full(len(d), pos + rain_offset) + jitter,\n        s=28,\n        alpha=0.6,\n        color=color,\n        edgecolor=PAGE_BG,\n        linewidth=0.4,\n        zorder=2,\n    )\n\n# Axes & ticks\nax.set_yticks([1, 2, 3])\nax.set_yticklabels(categories, fontsize=10, color=INK_SOFT)\nax.set_xlabel(\"Reaction Time (ms)\", fontsize=10, color=INK)\nax.set_ylabel(\"Experimental Condition\", fontsize=10, color=INK)\n\ntitle = \"raincloud-basic · python · matplotlib · anyplot.ai\"\nax.set_title(title, fontsize=12, fontweight=\"medium\", color=INK, pad=10)\n\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)\n\n# Range with breathing room\nall_data = np.concatenate(data)\nax.set_xlim(all_data.min() - 40, all_data.max() + 40)\nax.set_ylim(0.45, 3.55)\n\n# Subtle x-axis grid only\nax.xaxis.grid(True, alpha=0.15, color=INK, linewidth=0.8)\nax.set_axisbelow(True)\n\n# L-frame: keep left + bottom\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nax.spines[\"left\"].set_color(INK_SOFT)\nax.spines[\"bottom\"].set_color(INK_SOFT)\nax.spines[\"left\"].set_linewidth(0.8)\nax.spines[\"bottom\"].set_linewidth(0.8)\n\nfig.subplots_adjust(left=0.12, right=0.97, top=0.90, bottom=0.13)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}