{"spec_id":"spectrum-nmr","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nspectrum-nmr: NMR Spectrum (Nuclear Magnetic Resonance)\nLibrary: letsplot 4.10.1 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-06-03\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_area,\n    geom_hline,\n    geom_line,\n    geom_text,\n    ggplot,\n    ggsize,\n    labs,\n    layer_tooltips,\n    scale_x_reverse,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\nfrom lets_plot.export import ggsave\n\n\nLetsPlot.setup_html()\n\n# Theme tokens (Imprint palette — see prompts/default-style-guide.md)\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nBRAND = \"#009E73\"  # Imprint palette position 1 — ALWAYS first series\n\n# Data - Synthetic 1H NMR spectrum of ethanol (CH3CH2OH)\nnp.random.seed(42)\nchemical_shift = np.linspace(5.0, -0.5, 5000)\nw = 0.008  # Lorentzian half-width for sharp peaks\n\n# Build spectrum by summing Lorentzian peaks: A * w^2 / ((x - c)^2 + w^2)\nintensity = np.zeros_like(chemical_shift)\n\n# TMS reference peak at 0 ppm\nintensity += 1.0 * w**2 / ((chemical_shift - 0.0) ** 2 + w**2)\n\n# CH3 triplet near 1.18 ppm (J-coupling = 0.06 ppm, intensity ratio 1:2:1)\nj = 0.06\nfor center, amp in [(1.18 - j, 0.75), (1.18, 1.5), (1.18 + j, 0.75)]:\n    intensity += amp * w**2 / ((chemical_shift - center) ** 2 + w**2)\n\n# CH2 quartet near 3.69 ppm (J-coupling = 0.06 ppm, intensity ratio 1:3:3:1)\nfor center, amp in [(3.69 - 1.5 * j, 0.4), (3.69 - 0.5 * j, 1.2), (3.69 + 0.5 * j, 1.2), (3.69 + 1.5 * j, 0.4)]:\n    intensity += amp * w**2 / ((chemical_shift - center) ** 2 + w**2)\n\n# OH singlet near 2.61 ppm (broader due to exchange)\nw_oh = 0.012\nintensity += 0.6 * w_oh**2 / ((chemical_shift - 2.61) ** 2 + w_oh**2)\n\n# Add subtle baseline noise\nintensity += np.random.normal(0, 0.003, len(chemical_shift))\nintensity = np.clip(intensity, 0, None)\n\ndf = pd.DataFrame({\"chemical_shift\": chemical_shift, \"intensity\": intensity})\n\n# Peak labels positioned above each peak group\npeak_labels = pd.DataFrame(\n    {\n        \"x\": [0.0, 1.18, 2.61, 3.69],\n        \"y\": [1.15, 1.58, 0.78, 1.42],\n        \"label\": [\"TMS\\n0.00 ppm\", \"CH₃ (triplet)\\n1.18 ppm\", \"OH (singlet)\\n2.61 ppm\", \"CH₂ (quartet)\\n3.69 ppm\"],\n    }\n)\n\n# Interactive tooltips — distinctly lets-plot (not available in plotnine)\nspectrum_tooltips = (\n    layer_tooltips()\n    .format(\"@{chemical_shift}\", \".3f\")\n    .line(\"δ: @{chemical_shift} ppm\")\n    .format(\"@{intensity}\", \".4f\")\n    .line(\"Intensity: @{intensity} a.u.\")\n)\n\n# Plot — geom_area fill gives distinctive spectrum appearance; tooltips add interactive HTML layer\nplot = (\n    ggplot(df, aes(x=\"chemical_shift\", y=\"intensity\"))\n    + geom_hline(yintercept=0, color=INK_SOFT, size=0.5)\n    + geom_area(fill=BRAND, alpha=0.15)\n    + geom_line(color=BRAND, size=1.2, tooltips=spectrum_tooltips)\n    + geom_text(data=peak_labels, mapping=aes(x=\"x\", y=\"y\", label=\"label\"), size=5.5, color=INK, fontface=\"bold\")\n    + labs(\n        x=\"δ Chemical Shift (ppm)\",\n        y=\"Intensity (a.u.)\",\n        title=\"Ethanol ¹H NMR · spectrum-nmr · python · letsplot · anyplot.ai\",\n    )\n    + scale_x_reverse(limits=[-0.5, 5.0])\n    + scale_y_continuous(expand=[0.02, 0, 0.20, 0])\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_major_y=element_line(color=INK_SOFT, size=0.2),\n        panel_grid_minor=element_blank(),\n        axis_title=element_text(size=12, color=INK),\n        axis_text=element_text(size=10, color=INK_SOFT),\n        plot_title=element_text(size=16, color=INK),\n        axis_line=element_line(color=INK_SOFT),\n        axis_ticks=element_blank(),\n        plot_margin=[30, 40, 20, 20],\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"}