{"spec_id":"volcano-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nvolcano-basic: Volcano Plot for Statistical Significance\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 80/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\n\n\nLetsPlot.setup_html()\n\n# Theme-adaptive colors\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 (colorblind-safe)\nOKABE_ITO_DOWN = \"#4467A3\"  # imprint blue — down-regulated (cool)\nOKABE_ITO_UP = \"#AE3030\"  # imprint red — up-regulated (warm semantic)\nOKABE_ITO_NEUTRAL = \"#888888\"  # Gray for not significant\n\n# Data - Simulated differential expression results\nnp.random.seed(42)\nn_genes = 500\n\n# Generate log2 fold changes (mostly near zero with some extremes)\nlog2_fc = np.concatenate(\n    [\n        np.random.normal(0, 0.4, n_genes - 100),  # Unchanged genes\n        np.random.normal(2.2, 0.6, 50),  # Up-regulated\n        np.random.normal(-2.2, 0.6, 50),  # Down-regulated\n    ]\n)\n\n# Generate p-values (strongly correlated with fold change magnitude)\n# Higher fold change = lower p-value (more significant)\nneg_log10_pval = np.zeros(n_genes)\nfor i, fc in enumerate(log2_fc):\n    if abs(fc) > 1.5:  # Large fold changes get significant p-values\n        neg_log10_pval[i] = np.random.uniform(1.5, 3.5)\n    elif abs(fc) > 1.0:  # Moderate fold changes get borderline p-values\n        neg_log10_pval[i] = np.random.uniform(0.8, 2.0)\n    else:  # Small fold changes get non-significant p-values\n        neg_log10_pval[i] = np.random.uniform(0.1, 1.5)\n\n# Determine significance status\np_threshold = 1.3  # -log10(0.05)\nfc_threshold = 1.0  # log2(2) = 1\n\nsignificance = []\nfor fc, nlp in zip(log2_fc, neg_log10_pval, strict=False):\n    if nlp > p_threshold and fc > fc_threshold:\n        significance.append(\"Up-regulated\")\n    elif nlp > p_threshold and fc < -fc_threshold:\n        significance.append(\"Down-regulated\")\n    else:\n        significance.append(\"Not significant\")\n\n# Create DataFrame\ndf = pd.DataFrame({\"log2_fold_change\": log2_fc, \"neg_log10_pvalue\": neg_log10_pval, \"significance\": significance})\n\n# Create volcano plot\nplot = (\n    ggplot(df, aes(x=\"log2_fold_change\", y=\"neg_log10_pvalue\", color=\"significance\"))\n    + geom_point(aes(color=\"significance\"), size=4, alpha=0.7)\n    + geom_hline(yintercept=p_threshold, linetype=\"dashed\", color=INK_SOFT, size=1)\n    + geom_vline(xintercept=-fc_threshold, linetype=\"dashed\", color=INK_SOFT, size=1)\n    + geom_vline(xintercept=fc_threshold, linetype=\"dashed\", color=INK_SOFT, size=1)\n    + scale_color_manual(\n        values=[OKABE_ITO_DOWN, OKABE_ITO_NEUTRAL, OKABE_ITO_UP],\n        breaks=[\"Down-regulated\", \"Not significant\", \"Up-regulated\"],\n    )\n    + labs(x=\"Log2 Fold Change\", y=\"-Log10(p-value)\", title=\"volcano-basic · letsplot · anyplot.ai\", color=\"Status\")\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_line(color=INK_SOFT, size=0.2),\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        plot_title=element_text(size=24, face=\"bold\", color=INK),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_text=element_text(size=16, color=INK_SOFT),\n        legend_title=element_text(size=18, color=INK),\n        legend_position=\"right\",\n    )\n    + ggsize(1600, 900)\n)\n\n# Save as PNG (scale=3 gives 4800x2700)\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=3)\n\n# Save as HTML for interactivity\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}