Polynomial Fitting: polyfit and poly1d¶
NumPy provides tools for fitting polynomials to data and working with polynomial objects.
import numpy as np
np.polyfit() — Fit Polynomial to Data¶
Fit a polynomial of specified degree to data points using least squares:
# Data points
x = np.array([0, 1, 2, 3, 4])
y = np.array([1, 3, 7, 13, 21])
# Fit quadratic (degree 2): y = ax² + bx + c
coefficients = np.polyfit(x, y, deg=2)
print(coefficients) # [1. 1. 1.] → y = x² + x + 1
Understanding the Output¶
# coefficients are in descending order of power
# [a, b, c] for ax² + bx + c
x = np.array([1, 2, 3, 4, 5])
y = np.array([2.1, 3.9, 6.2, 7.8, 10.1])
# Linear fit (degree 1)
m, b = np.polyfit(x, y, 1)
print(f"Slope: {m:.2f}, Intercept: {b:.2f}")
# Slope: 2.00, Intercept: 0.02
np.poly1d() — Polynomial Object¶
Create a polynomial object from coefficients for easy evaluation:
# Create polynomial: x² + 2x + 3
p = np.poly1d([1, 2, 3])
print(p)
# 2
# 1 x + 2 x + 3
# Evaluate at x = 2
print(p(2)) # 1*4 + 2*2 + 3 = 11
# Evaluate at multiple points
x_vals = np.array([0, 1, 2, 3])
print(p(x_vals)) # [ 3 6 11 18]
Combining polyfit and poly1d¶
x = np.array([0, 1, 2, 3, 4])
y = np.array([1, 2.1, 4.9, 9.2, 16.1])
# Fit and create polynomial object
coeffs = np.polyfit(x, y, 2)
p = np.poly1d(coeffs)
# Evaluate fitted polynomial
y_fitted = p(x)
print(y_fitted) # [1.02 2.04 4.98 9.84 16.62]
# Evaluate at new points
x_new = np.linspace(0, 5, 100)
y_new = p(x_new)
Polynomial Operations¶
Arithmetic¶
p1 = np.poly1d([1, 2]) # x + 2
p2 = np.poly1d([1, 0, 1]) # x² + 1
# Addition
print(p1 + p2) # x² + x + 3
# Multiplication
print(p1 * p2) # x³ + 2x² + x + 2
# Power
print(p1 ** 2) # x² + 4x + 4
Derivative and Integral¶
p = np.poly1d([1, -2, 1]) # x² - 2x + 1
print(p)
# 2
# 1 x - 2 x + 1
# Derivative
dp = p.deriv()
print(dp) # 2x - 2
# Second derivative
d2p = p.deriv(2)
print(d2p) # 2
# Integral (with constant of integration)
ip = p.integ()
print(ip) # 0.3333 x³ - x² + x
# Integral with specific constant
ip = p.integ(k=5) # constant = 5
Roots¶
p = np.poly1d([1, -5, 6]) # x² - 5x + 6
# Find roots
roots = p.r
print(roots) # [3. 2.]
# Verify: (x-2)(x-3) = x² - 5x + 6
Practical Examples¶
Linear Regression¶
# Simple linear regression
x = np.array([1, 2, 3, 4, 5])
y = np.array([2.2, 4.1, 5.9, 8.1, 9.8])
# Fit line
slope, intercept = np.polyfit(x, y, 1)
print(f"y = {slope:.2f}x + {intercept:.2f}")
# y = 1.94x + 0.28
# Create polynomial for predictions
line = np.poly1d([slope, intercept])
# Predict new values
x_new = np.array([6, 7, 8])
y_pred = line(x_new)
print(y_pred) # [11.92 13.86 15.80]
Quadratic Fit¶
# Projectile motion data
t = np.array([0, 0.5, 1.0, 1.5, 2.0]) # time
h = np.array([0, 11.4, 15.2, 11.5, 0.1]) # height
# Fit quadratic: h = at² + bt + c
a, b, c = np.polyfit(t, h, 2)
print(f"h = {a:.2f}t² + {b:.2f}t + {c:.2f}")
# h ≈ -9.8t² + 19.6t + 0.1
# Find maximum height
trajectory = np.poly1d([a, b, c])
t_max = -b / (2*a) # vertex of parabola
h_max = trajectory(t_max)
print(f"Max height: {h_max:.2f} at t={t_max:.2f}")
Polynomial Interpolation¶
# Given points
x = np.array([0, 1, 2, 3])
y = np.array([1, 2, 1, 2])
# Fit polynomial passing through all points
# Need degree = n_points - 1 for exact fit
coeffs = np.polyfit(x, y, 3)
p = np.poly1d(coeffs)
# Verify: polynomial passes through all points
print(p(x)) # [1. 2. 1. 2.]
Trend Removal¶
# Time series with trend
t = np.arange(100)
signal = 0.5 * t + 10 + np.random.randn(100) * 2
# Fit and remove trend
trend_coeffs = np.polyfit(t, signal, 1)
trend = np.poly1d(trend_coeffs)
detrended = signal - trend(t)
# detrended now has mean ≈ 0
Goodness of Fit¶
R-squared Calculation¶
def r_squared(y_true, y_pred):
"""Calculate R² (coefficient of determination)."""
ss_res = np.sum((y_true - y_pred) ** 2)
ss_tot = np.sum((y_true - y_true.mean()) ** 2)
return 1 - (ss_res / ss_tot)
x = np.array([1, 2, 3, 4, 5])
y = np.array([2.1, 4.0, 5.9, 8.1, 10.0])
# Fit and evaluate
p = np.poly1d(np.polyfit(x, y, 1))
y_pred = p(x)
r2 = r_squared(y, y_pred)
print(f"R² = {r2:.4f}") # R² ≈ 0.9988
Residual Covariance¶
# polyfit can return residuals and other info
coeffs, residuals, rank, sv, rcond = np.polyfit(
x, y, 1, full=True
)
print(f"Residual sum of squares: {residuals[0]:.4f}")
Choosing Polynomial Degree¶
x = np.linspace(0, 10, 50)
y = np.sin(x) + np.random.randn(50) * 0.1
# Compare different degrees
for deg in [1, 3, 5, 10]:
p = np.poly1d(np.polyfit(x, y, deg))
y_pred = p(x)
r2 = r_squared(y, y_pred)
print(f"Degree {deg:2d}: R² = {r2:.4f}")
# Higher degree → better fit, but risk overfitting
Modern Alternative: np.polynomial¶
NumPy's newer polynomial module offers improved numerical stability:
from numpy.polynomial import polynomial as P
x = np.array([0, 1, 2, 3, 4])
y = np.array([1, 3, 7, 13, 21])
# Fit polynomial (coefficients in ascending order!)
coeffs = P.polyfit(x, y, 2)
print(coeffs) # [1. 1. 1.] → 1 + x + x²
# Evaluate
y_fit = P.polyval(x, coeffs)
Key Differences¶
| Feature | np.polyfit |
np.polynomial.polynomial |
|---|---|---|
| Coefficient order | Descending (highest first) | Ascending (constant first) |
| Numerical stability | Can have issues | Better conditioning |
| Polynomial object | np.poly1d |
Polynomial class |
from numpy.polynomial import Polynomial
# Create polynomial: 1 + 2x + 3x²
p = Polynomial([1, 2, 3]) # ascending order!
# Fit from data
p_fit = Polynomial.fit(x, y, deg=2)
Summary¶
| Function | Purpose |
|---|---|
np.polyfit(x, y, deg) |
Fit polynomial of degree deg |
np.poly1d(coeffs) |
Create polynomial object |
p(x) |
Evaluate polynomial at x |
p.deriv() |
Derivative of polynomial |
p.integ() |
Integral of polynomial |
p.r |
Roots of polynomial |
Key Takeaways:
polyfitreturns coefficients in descending order of powerpoly1dcreates callable polynomial objects- Degree = n_points - 1 for exact interpolation
- Higher degree risks overfitting
- Check fit quality with R² or residuals
- For numerical stability, consider
np.polynomialmodule - Use
deriv()andinteg()for calculus operations