codexsampling-aliasing.py
sampling-aliasing.py
"""Plot distinguishable and aliased samples; write sampling-aliasing.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, 2001)
slow = np.cos(2 * np.pi * 2 * time)
fast = np.cos(2 * np.pi * 8 * time)
blue, orange = "#2266aa", "#ce6a16"
fig, axes = plt.subplots(2, 1, figsize=(9, 6.4), layout="constrained")
fig.suptitle("Sampling: different waves can leave the same numbers", fontsize=16)
for ax, rate in zip(axes, (20, 10)):
samples = np.arange(rate + 1) / rate
slow_samples = np.cos(2 * np.pi * 2 * samples)
fast_samples = np.cos(2 * np.pi * 8 * samples)
ax.plot(time, slow, color=blue, lw=2, label="2 cycles/s")
ax.plot(time, fast, color=orange, lw=1.7, alpha=0.8, label="8 cycles/s")
ax.scatter(samples, slow_samples, s=72, facecolors="white", edgecolors=blue,
linewidths=1.7, zorder=4, label="2 cycles/s samples")
ax.scatter(samples, fast_samples, s=38, marker="x", color=orange,
linewidths=1.6, zorder=5, label="8 cycles/s samples")
ax.set(xlim=(-0.025, 1.025), ylim=(-1.25, 1.25),
xlabel="Time (seconds)", ylabel="Value")
ax.grid(alpha=0.18)
if rate == 20:
ax.set_title("20 samples/s: the two sets of samples differ", loc="left", fontsize=12)
ax.legend(loc="upper center", bbox_to_anchor=(0.5, 1.30), ncol=4, fontsize=9)
else:
ax.set_title("10 samples/s: every orange cross lands on a blue circle", loc="left", fontsize=12)
fig.savefig(Path("sampling-aliasing.png"), dpi=120,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()