Chapter 16 — Regression Analysis
Regression predicts a continuous target from features. This chapter covers linear regression, evaluation metrics, polynomial features, and regularization (Ridge/Lasso) to combat overfitting.
Learning Objectives
- Fit and interpret a linear regression model.
- Evaluate with MSE, RMSE, MAE, and $R^2$.
- Use polynomial features to model nonlinearity.
- Apply Ridge and Lasso regularization.
- Compare models with cross-validation.
- Understand the bias-variance tradeoff.
Prerequisites / Imports
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.pipeline import Pipeline
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
1 The Diabetes Dataset
10 physiological features; target is a disease progression measure.
data = load_diabetes(as_frame=True)
X, y = data.data, data.target
print('shape:', X.shape)
X.head()
shape: (442, 10)
| age | sex | bmi | bp | s1 | s2 | s3 | s4 | s5 | s6 | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.038076 | 0.050680 | 0.061696 | 0.021872 | -0.044223 | -0.034821 | -0.043401 | -0.002592 | 0.019907 | -0.017646 |
| 1 | -0.001882 | -0.044642 | -0.051474 | -0.026328 | -0.008449 | -0.019163 | 0.074412 | -0.039493 | -0.068332 | -0.092204 |
| 2 | 0.085299 | 0.050680 | 0.044451 | -0.005670 | -0.045599 | -0.034194 | -0.032356 | -0.002592 | 0.002861 | -0.025930 |
| 3 | -0.089063 | -0.044642 | -0.011595 | -0.036656 | 0.012191 | 0.024991 | -0.036038 | 0.034309 | 0.022688 | -0.009362 |
| 4 | 0.005383 | -0.044642 | -0.036385 | 0.021872 | 0.003935 | 0.015596 | 0.008142 | -0.002592 | -0.031988 | -0.046641 |
2 Simple Linear Regression
Fit $y = w_0 + w_1 x_1 + \dots + w_p x_p$ by ordinary least squares.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
lr = LinearRegression()
lr.fit(X_train, y_train)
print('intercept:', round(lr.intercept_, 2))
print('coefficients:', np.round(lr.coef_, 2))
intercept: 151.35 coefficients: [ 37.9 -241.96 542.43 347.7 -931.49 518.06 163.42 275.32 736.2 48.67]
3 Regression Metrics
MSE, RMSE, MAE (in target units), and $R^2$ (fraction of variance explained).
y_pred = lr.predict(X_test)
print('MSE :', round(mean_squared_error(y_test, y_pred), 2))
print('RMSE:', np.sqrt(mean_squared_error(y_test, y_pred)).round(2))
print('MAE :', round(mean_absolute_error(y_test, y_pred), 2))
print('R2 :', round(r2_score(y_test, y_pred), 3))
MSE : 2900.19 RMSE: 53.85 MAE : 42.79 R2 : 0.453
4 Predicted vs Actual
A good regression has points clustered around the diagonal.
plt.figure(figsize=(6,6))
plt.scatter(y_test, y_pred, alpha=0.6)
lims = [y_test.min(), y_test.max()]
plt.plot(lims, lims, 'r--')
plt.xlabel('actual'); plt.ylabel('predicted'); plt.title('Predicted vs actual')
plt.show()
5 Polynomial Regression
Add polynomial features to capture curvature. Beware: higher degrees overfit.
rng = np.random.default_rng(0)
x = np.sort(rng.uniform(0, 6, 40))
y_true = np.sin(x)
y = y_true + rng.normal(0, 0.2, size=len(x))
X = x.reshape(-1, 1)
for deg in [1, 3, 9]:
pipe = Pipeline([('poly', PolynomialFeatures(degree=deg)), ('lr', LinearRegression())])
pipe.fit(X, y)
xs = np.linspace(0, 6, 100).reshape(-1, 1)
plt.plot(xs, pipe.predict(xs), label=f'degree {deg}')
plt.scatter(x, y, color='black', alpha=0.6, label='data')
plt.plot(x, y_true, 'g--', label='true')
plt.legend(); plt.title('Polynomial regression: under/overfitting'); plt.show()
6 Regularization: Ridge and Lasso
Ridge (L2) shrinks coefficients; Lasso (L1) can zero them out (feature selection). Scaling is essential before regularization.
X_train, X_test, y_train, y_test = train_test_split(data.data, data.target, test_size=0.2, random_state=42)
ridge = Pipeline([('scaler', StandardScaler()), ('model', Ridge(alpha=1.0))]).fit(X_train, y_train)
lasso = Pipeline([('scaler', StandardScaler()), ('model', Lasso(alpha=0.1))]).fit(X_train, y_train)
print('Ridge R2 :', round(ridge.score(X_test, y_test), 3))
print('Lasso R2 :', round(lasso.score(X_test, y_test), 3))
print('Lasso nonzero coefs:', int(np.sum(lasso.named_steps['model'].coef_ != 0)))
print('Lasso coefs:', np.round(lasso.named_steps['model'].coef_, 2))
Ridge R2 : 0.454 Lasso R2 : 0.456 Lasso nonzero coefs: 10 Lasso coefs: [ 1.73 -11.32 25.82 16.64 -29.36 13.28 0.55 10.24 29.63 2.39]
7 The Regularization Strength
Larger $\alpha$ shrinks coefficients more. Cross-validation picks a good value.
alphas = [0.001, 0.01, 0.1, 1, 10, 100]
ridge_scores = []
for a in alphas:
p = Pipeline([('scaler', StandardScaler()), ('model', Ridge(alpha=a))])
ridge_scores.append(cross_val_score(p, data.data, data.target, cv=5).mean())
plt.figure(figsize=(7,4))
plt.plot(alphas, ridge_scores, marker='o')
plt.xscale('log'); plt.title('Ridge CV R2 vs alpha'); plt.xlabel('alpha'); plt.ylabel('CV R2')
plt.show()
Case Study: Predicting Disease Progression
Compare linear, Ridge, and Lasso with 5-fold cross-validation and report the winner.
models = {
'Linear': Pipeline([('scaler', StandardScaler()), ('model', LinearRegression())]),
'Ridge': Pipeline([('scaler', StandardScaler()), ('model', Ridge(alpha=0.1))]),
'Lasso': Pipeline([('scaler', StandardScaler()), ('model', Lasso(alpha=0.1))]),
}
for name, m in models.items():
scores = cross_val_score(m, data.data, data.target, cv=5)
print(f'{name:7s} CV R2: {scores.mean():.3f} +/- {scores.std():.3f}')
Linear CV R2: 0.482 +/- 0.049 Ridge CV R2: 0.482 +/- 0.049 Lasso CV R2: 0.482 +/- 0.048
Exercises
- Fit a linear regression on the diabetes data and report $R^2$.
- Compute RMSE and MAE for the predictions.
- Make a predicted-vs-actual scatter with a diagonal reference line.
- Fit polynomial models of degree 1, 3, and 10 on synthetic data and observe overfitting.
- Explain why scaling matters before Ridge/Lasso.
- Compare Ridge and Lasso $R^2$ on the diabetes data.
- Use cross-validation to select the best Ridge
alpha. - Count how many coefficients Lasso sets to zero for a large
alpha. - Explain the bias-variance tradeoff in one paragraph.
- State when you would prefer Lasso over Ridge.
Python Data Science: From Foundations to Applications — Chapter 16