{"spec_id":"spectrum-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nspectrum-basic: Frequency Spectrum Plot\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 94/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\nimport shutil\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\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\"\nBRAND = \"#009E73\"  # Okabe-Ito position 1\n\n# Grid color (theme-adaptive)\nGRID_COLOR = \"rgba(26, 26, 23, 0.08)\" if THEME == \"light\" else \"rgba(240, 239, 232, 0.08)\"\n\n# Data: Generate a synthetic signal with multiple frequency components\nnp.random.seed(42)\n\n# Sampling parameters\nsample_rate = 1024  # Hz\nduration = 1.0  # seconds\nn_samples = int(sample_rate * duration)\nt = np.linspace(0, duration, n_samples, endpoint=False)\n\n# Create a signal with multiple frequency components\n# 50 Hz fundamental, 120 Hz harmonic, 200 Hz component, plus noise\nsignal = (\n    1.0 * np.sin(2 * np.pi * 50 * t)  # 50 Hz fundamental\n    + 0.5 * np.sin(2 * np.pi * 120 * t)  # 120 Hz harmonic\n    + 0.3 * np.sin(2 * np.pi * 200 * t)  # 200 Hz component\n    + 0.1 * np.random.randn(n_samples)  # Noise\n)\n\n# Compute FFT\nfft_result = np.fft.fft(signal)\nfrequencies = np.fft.fftfreq(n_samples, 1 / sample_rate)\n\n# Take only positive frequencies\npositive_mask = frequencies >= 0\nfrequencies = frequencies[positive_mask]\namplitude = np.abs(fft_result[positive_mask])\n\n# Convert to dB scale (power spectrum)\namplitude_db = 20 * np.log10(amplitude + 1e-10)\n\n# Create DataFrame for lets-plot\ndf = pd.DataFrame({\"frequency\": frequencies, \"amplitude\": amplitude_db})\n\n# Filter to show meaningful frequency range (0-300 Hz)\ndf = df[df[\"frequency\"] <= 300]\n\n# Create plot with theme-adaptive styling\nplot = (\n    ggplot(df, aes(x=\"frequency\", y=\"amplitude\"))\n    + geom_line(color=BRAND, size=1.2, alpha=0.9)\n    + geom_area(fill=BRAND, alpha=0.2)\n    + labs(x=\"Frequency (Hz)\", y=\"Amplitude (dB)\", title=\"spectrum-basic · letsplot · anyplot.ai\")\n    + theme_minimal()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_grid_major=element_line(color=GRID_COLOR, size=0.3),\n        panel_grid_minor=element_blank(),\n        axis_title=element_text(size=20, color=INK),\n        axis_text=element_text(size=16, color=INK_SOFT),\n        plot_title=element_text(size=24, color=INK, face=\"bold\"),\n        axis_line=element_line(color=INK_SOFT, size=0.3),\n    )\n    + ggsize(1600, 900)  # Will be scaled 3x to 4800x2700\n)\n\n# Save as PNG and HTML (ggsave saves to lets-plot-images/ subdirectory)\nggsave(plot, f\"plot-{THEME}.png\", scale=3)\nggsave(plot, f\"plot-{THEME}.html\")\n\n# Move files to current directory\nif os.path.exists(f\"lets-plot-images/plot-{THEME}.png\"):\n    shutil.move(f\"lets-plot-images/plot-{THEME}.png\", f\"plot-{THEME}.png\")\nif os.path.exists(f\"lets-plot-images/plot-{THEME}.html\"):\n    shutil.move(f\"lets-plot-images/plot-{THEME}.html\", f\"plot-{THEME}.html\")\n\n# Clean up empty subdirectory if all files are moved\ntry:\n    if not os.listdir(\"lets-plot-images\"):\n        os.rmdir(\"lets-plot-images\")\nexcept OSError:\n    pass\n"}