Line Plots, Scatter, and Error Bars
The Workhorse Plots of Engineering
Line plots and scatter plots are the bread and butter of scientific
visualization. BER curves, convergence plots, measurement series,
and parameter sweeps all rely on line/scatter plots with error bars
and confidence intervals. Mastering ax.plot(), ax.scatter(),
ax.errorbar(), and ax.fill_between() covers 80% of your daily
plotting needs.
Definition: Line Plot (ax.plot)
Line Plot (ax.plot)
ax.plot(x, y, fmt, **kwargs) draws connected line segments between
pairs. The format string fmt combines color, marker,
and linestyle:
ax.plot(x, y, 'r--o') # red dashed line with circle markers
ax.plot(x, y, color='#2563EB', linewidth=2, marker='s',
markersize=6, markerfacecolor='white', label='BPSK')
Key keyword arguments: linewidth (lw), linestyle (ls),
marker, markersize (ms), markeredgecolor (mec),
markerfacecolor (mfc), alpha, label, zorder.
Multiple ax.plot() calls on the same Axes overlay the curves.
Use label and ax.legend() to distinguish them.
Definition: Scatter Plot (ax.scatter)
Scatter Plot (ax.scatter)
ax.scatter(x, y, s, c, **kwargs) places markers at
with per-point size s and color c:
ax.scatter(snr_db, throughput, s=errors*10,
c=ber, cmap='viridis', norm=LogNorm())
Unlike ax.plot(), scatter supports array-valued sizes and colors,
making it ideal for visualizing three or four dimensions simultaneously.
Use ax.plot() when all markers share the same size and color.
Use ax.scatter() when marker properties vary per point.
Theorem: Confidence Interval Width Scaling
For independent measurements of a quantity with standard deviation , the 95% confidence interval for the mean has half-width:
Halving the CI width requires the measurements. For BER estimation with error count in trials:
This scaling is why BER curves require millions of bits at high SNR. Always show error bars to communicate measurement uncertainty.
Example: BER Curve with Confidence Intervals
Plot a BER curve for BPSK with simulated error bars and a theoretical reference on a semilogy scale.
Simulation + Theoretical BER
import numpy as np
from scipy.special import erfc
import matplotlib.pyplot as plt
snr_db = np.arange(0, 13)
snr_lin = 10**(snr_db / 10)
ber_theory = 0.5 * erfc(np.sqrt(snr_lin))
rng = np.random.default_rng(42)
N_bits = 100_000
ber_sim = np.zeros_like(snr_db, dtype=float)
ci = np.zeros_like(snr_db, dtype=float)
for i, s in enumerate(snr_lin):
bits = rng.integers(0, 2, N_bits)
noise = rng.standard_normal(N_bits) / np.sqrt(2 * s)
rx = (2*bits - 1) + noise
errors = np.sum((rx > 0) != bits.astype(bool))
ber_sim[i] = errors / N_bits
ci[i] = 1.96 * np.sqrt(ber_sim[i] * (1 - ber_sim[i]) / N_bits)
Plotting
fig, ax = plt.subplots(figsize=(7, 5))
ax.semilogy(snr_db, ber_theory, 'k-', lw=2, label='Theory')
ax.errorbar(snr_db, ber_sim, yerr=ci, fmt='o',
color='#2563EB', capsize=4, label='Simulation')
ax.set(xlabel=r' (dB)', ylabel='BER',
title='BPSK BER Curve')
ax.set_ylim(1e-6, 1)
ax.grid(True, which='both', alpha=0.3)
ax.legend()
fig.tight_layout()
Example: Confidence Bands with fill_between
Show a shaded 95% confidence band around an estimated channel capacity curve.
Implementation
snr_db = np.linspace(0, 30, 50)
snr = 10**(snr_db / 10)
capacity = np.log2(1 + snr) # Shannon capacity
std = 0.15 * np.sqrt(snr_db + 1) # Simulated uncertainty
fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(snr_db, capacity, color='#2563EB', lw=2, label='Mean')
ax.fill_between(snr_db,
capacity - 1.96*std, capacity + 1.96*std,
color='#2563EB', alpha=0.15, label='95% CI')
ax.set(xlabel='SNR (dB)', ylabel='Capacity (bits/s/Hz)')
ax.legend()
fig.tight_layout()
Example: Overlaying Multiple Modulation BER Curves
Plot BER curves for BPSK, QPSK, and 16-QAM on the same axes with distinct markers and a legend.
Implementation
from scipy.special import erfc
snr_db = np.arange(0, 21)
snr = 10**(snr_db / 10)
modulations = {
'BPSK': (0.5 * erfc(np.sqrt(snr)), 'o-', '#2563EB'),
'QPSK': (0.5 * erfc(np.sqrt(snr)), 's--', '#DC2626'),
'16-QAM': (3/8 * erfc(np.sqrt(2*snr/5)), 'D:', '#059669'),
}
fig, ax = plt.subplots(figsize=(7, 5))
for name, (ber, fmt, color) in modulations.items():
ax.semilogy(snr_db, ber, fmt, color=color, lw=2,
ms=6, label=name)
ax.set(xlabel=r' (dB)', ylabel='BER')
ax.set_ylim(1e-6, 1)
ax.grid(True, which='both', alpha=0.3)
ax.legend(title='Modulation')
fig.tight_layout()
BER Curve Explorer
Explore BER curves for different modulation schemes with adjustable SNR range and optional error bars.
Parameters
Scatter Plot with Colormap
Visualize throughput vs. distance with color-coded BER and size-coded number of users.
Parameters
Common Mistake: Plotting Zero on a Log Scale
Mistake:
BER values of exactly 0 cause semilogy to silently drop those
points (log(0) is undefined).
Correction:
Replace zeros with a small value or mask them:
ber_plot = np.where(ber > 0, ber, np.nan)
ax.semilogy(snr_db, ber_plot, 'o-')
Key Takeaway
Always plot error bars or confidence bands on simulation results.
A BER or throughput curve without uncertainty information is
scientifically incomplete. Use ax.errorbar() for discrete points
and ax.fill_between() for continuous bands.
Quick Check
When should you use ax.scatter() instead of ax.plot()?
When you need a log scale
When each point needs a different size or color
When plotting more than 1000 points
When you want connected lines
scatter() accepts arrays for s (size) and c (color), allowing per-point variation.
Error Bar
A graphical indicator of uncertainty, typically showing the confidence interval or standard error around each data point.
Related: Confidence Interval (CI)
Confidence Interval (CI)
A range of values that contains the true parameter with a specified probability (e.g., 95%).