{"spec_id":"volcano-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nvolcano-basic: Volcano Plot for Statistical Significance\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 89/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\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# Okabe-Ito palette for significance categories\nCOLOR_DOWNREG = \"#4467A3\"  # Blue\nCOLOR_UPREG = \"#C475FD\"  # Vermillion\nCOLOR_NONSIG = \"#888888\"  # Neutral gray\n\n# Data - simulated differential expression results\nnp.random.seed(42)\nn_genes = 2000\n\n# Generate log2 fold changes (centered around 0)\nlog2_fc = np.random.normal(0, 1.5, n_genes)\n\n# Generate p-values (most non-significant, some significant)\nbase_pvalues = np.random.exponential(0.3, n_genes)\nbase_pvalues = np.clip(base_pvalues, 1e-50, 1.0)\n\n# Make genes with large fold changes more likely to be significant\nsignificance_boost = np.abs(log2_fc) / 3\npvalues = base_pvalues * np.exp(-significance_boost * 5)\npvalues = np.clip(pvalues, 1e-50, 1.0)\n\n# Convert to -log10(p-value)\nneg_log10_pval = -np.log10(pvalues)\n\n# Significance thresholds\npval_threshold = 1.3  # -log10(0.05)\nfc_threshold = 1.0  # log2(2) = 1\n\n# Classify points\nsig_up = (neg_log10_pval > pval_threshold) & (log2_fc > fc_threshold)\nsig_down = (neg_log10_pval > pval_threshold) & (log2_fc < -fc_threshold)\nnon_sig = ~sig_up & ~sig_down\n\n# Plot\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Plot non-significant points first (gray)\nax.scatter(\n    log2_fc[non_sig],\n    neg_log10_pval[non_sig],\n    c=COLOR_NONSIG,\n    s=120,\n    alpha=0.5,\n    label=\"Not significant\",\n    edgecolors=\"none\",\n)\n\n# Plot significant down-regulated (blue)\nax.scatter(\n    log2_fc[sig_down],\n    neg_log10_pval[sig_down],\n    c=COLOR_DOWNREG,\n    s=150,\n    alpha=0.8,\n    label=\"Down-regulated\",\n    edgecolors=\"none\",\n)\n\n# Plot significant up-regulated (vermillion)\nax.scatter(\n    log2_fc[sig_up], neg_log10_pval[sig_up], c=COLOR_UPREG, s=150, alpha=0.8, label=\"Up-regulated\", edgecolors=\"none\"\n)\n\n# Add threshold lines\nax.axhline(y=pval_threshold, color=INK_SOFT, linestyle=\"--\", linewidth=1.5, alpha=0.4, zorder=1)\nax.axvline(x=fc_threshold, color=INK_SOFT, linestyle=\"--\", linewidth=1.5, alpha=0.4, zorder=1)\nax.axvline(x=-fc_threshold, color=INK_SOFT, linestyle=\"--\", linewidth=1.5, alpha=0.4, zorder=1)\n\n# Label top significant genes\ntop_up_idx = np.where(sig_up)[0]\nif len(top_up_idx) > 0:\n    top_up_scores = neg_log10_pval[top_up_idx] + np.abs(log2_fc[top_up_idx])\n    top_up = top_up_idx[np.argsort(top_up_scores)[-5:]]\n    for idx in top_up:\n        ax.annotate(\n            f\"Gene_{idx}\",\n            (log2_fc[idx], neg_log10_pval[idx]),\n            fontsize=13,\n            ha=\"left\",\n            va=\"bottom\",\n            xytext=(5, 5),\n            textcoords=\"offset points\",\n            color=INK,\n        )\n\ntop_down_idx = np.where(sig_down)[0]\nif len(top_down_idx) > 0:\n    top_down_scores = neg_log10_pval[top_down_idx] + np.abs(log2_fc[top_down_idx])\n    top_down = top_down_idx[np.argsort(top_down_scores)[-5:]]\n    for idx in top_down:\n        ax.annotate(\n            f\"Gene_{idx}\",\n            (log2_fc[idx], neg_log10_pval[idx]),\n            fontsize=13,\n            ha=\"right\",\n            va=\"bottom\",\n            xytext=(-5, 5),\n            textcoords=\"offset points\",\n            color=INK,\n        )\n\n# Styling\nax.set_xlabel(\"Log₂ Fold Change\", fontsize=20, color=INK)\nax.set_ylabel(\"-Log₁₀ (p-value)\", fontsize=20, color=INK)\nax.set_title(\"volcano-basic · matplotlib · anyplot.ai\", fontsize=24, fontweight=\"medium\", color=INK)\nax.tick_params(axis=\"both\", labelsize=16, colors=INK_SOFT)\n\n# Spines and grid\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nfor s in (\"left\", \"bottom\"):\n    ax.spines[s].set_color(INK_SOFT)\n\nax.yaxis.grid(True, alpha=0.12, linewidth=0.8, color=INK)\n\n# Legend styling\nleg = ax.legend(fontsize=16, loc=\"upper left\", framealpha=0.95)\nif leg:\n    leg.get_frame().set_facecolor(ELEVATED_BG)\n    leg.get_frame().set_edgecolor(INK_SOFT)\n    leg.get_frame().set_linewidth(0.8)\n    for text in leg.get_texts():\n        text.set_color(INK_SOFT)\n\n# Set axis limits with padding\nx_max = max(abs(log2_fc.min()), abs(log2_fc.max())) * 1.1\nax.set_xlim(-x_max, x_max)\nax.set_ylim(0, neg_log10_pval.max() * 1.1)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}