codexregression-residual-patterns.py
regression-residual-patterns.py
"""Show equal regression summaries with different residual shapes; save to cwd."""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
x = np.arange(-2, 3, dtype=float)
fitted = 3+x
patterns = [.2*np.array([2., -1., -2., -1., 2.]),
.2*np.sqrt(7/5)*np.array([1., -2., 0., 2., -1.])]
labels = ['Quadratic residual component', 'Alternating residual component']
colors = ['#b9652a', '#237c96']
design = np.column_stack([np.ones_like(x), x])
plt.rcParams.update({'font.size': 12})
fig, axes = plt.subplots(2, 2, figsize=(10.5, 7.3), layout='constrained', facecolor='white')
xx = np.linspace(-2.15, 2.15, 250)
for i, (residual, label, color) in enumerate(zip(patterns, labels, colors)):
y = fitted+residual
coefficients = np.linalg.lstsq(design, y, rcond=None)[0]
assert np.allclose(coefficients, [3, 1])
assert np.allclose(design.T@residual, [0, 0])
assert np.isclose(residual@residual, .56)
assert np.isclose(1-(residual@residual)/np.sum((y-y.mean())**2), 125/132)
axes[0, i].plot(xx, 3+xx, color='#667c90', lw=2, label='Identical fit: 3 + x')
axes[0, i].scatter(x, y, s=65, color=color, zorder=3, label='Constructed data')
axes[0, i].set(title=label, xlabel='Predictor x', ylabel='Response y', ylim=(.5, 5.8))
axes[0, i].legend(loc='upper left', fontsize=10)
axes[1, i].axhline(0, color='#87929c', lw=1)
axes[1, i].vlines(fitted, 0, residual, color=color, lw=1.5)
axes[1, i].scatter(fitted, residual, color=color, s=65, zorder=3)
if i == 0:
axes[1, i].plot(3+xx, .2*(xx*xx-2), color=color, lw=2, alpha=.8,
label=r'Exact pattern: $0.2(x^2-2)$')
axes[1, i].legend(loc='lower center', fontsize=10)
axes[1, i].set(xlabel='Fitted response', ylabel='Residual',
xlim=(.6, 5.4), ylim=(-.68, .65), title='Subtract the same fitted trend')
for ax in axes[:, i]:
ax.grid(alpha=.2)
fig.suptitle('Same slope, intercept, RSS = 0.56 and R² = 125/132 ≈ 0.947', fontsize=15)
fig.savefig(Path.cwd() / 'regression-residual-patterns.png', dpi=120, facecolor='white')
plt.close(fig)
if __name__ == '__main__':
main()