codexfourier-frequency-decomposition.py
fourier-frequency-decomposition.py
"""Plot three harmonics and their sum; write fourier-frequency-decomposition.png to cwd.
Uses the root pyproject.toml dependencies. Tested with Python 3.14.4,
NumPy 2.3.5 and Matplotlib 3.10.7.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False, "figure.facecolor": "white",
"axes.facecolor": "white"})
time = np.linspace(0, 1, 1201)
frequencies = np.array([1, 3, 5])
amplitudes = np.array([1, 0.5, 0.25])
components = amplitudes[:, None] * np.cos(2 * np.pi * frequencies[:, None] * time)
colors = ["#2266aa", "#ce6a16", "#26845b"]
fig, axes = plt.subplots(3, 1, figsize=(8.8, 8), layout="constrained",
gridspec_kw={"height_ratios": [1.1, 1.1, 1]})
fig.suptitle("Three harmonics, one waveform", fontsize=18)
for component, frequency, amplitude, color in zip(components, frequencies, amplitudes, colors):
axes[0].plot(time, component, color=color, lw=2,
label=f"{frequency} cycles/s; amplitude {amplitude:g}")
axes[0].set_title("The simple oscillations", loc="left")
axes[0].legend(loc="lower left", ncol=3, fontsize=9)
axes[0].set_ylim(-1.4, 1.25)
axes[1].plot(time, components.sum(axis=0), color="#343745", lw=2.5)
axes[1].set_title(r"Their sum: $f(t)=\cos(2\pi t)+\frac{1}{2}\cos(6\pi t)+\frac{1}{4}\cos(10\pi t)$",
loc="left", fontsize=11)
axes[1].set_ylim(-1.9, 1.9)
for ax in axes[:2]:
ax.set(xlim=(0, 1), xlabel="Time (seconds)", ylabel="Value")
ax.axhline(0, color="#aaaaaa", lw=0.8, zorder=0)
ax.grid(alpha=0.18)
axes[2].vlines(frequencies, 0, amplitudes, colors=colors, lw=3)
axes[2].scatter(frequencies, amplitudes, c=colors, s=50, zorder=3)
for frequency, amplitude in zip(frequencies, amplitudes):
axes[2].text(frequency + 0.15, amplitude + 0.03, f"{amplitude:g}")
axes[2].set(title="The same recipe in frequency coordinates (one-sided amplitudes)",
xlabel="Frequency (cycles per second)", ylabel="Cosine amplitude",
xlim=(0, 6), ylim=(0, 1.22), xticks=np.arange(7))
axes[2].grid(alpha=0.18)
fig.savefig(Path("fourier-frequency-decomposition.png"), dpi=120,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()