Lab 07: Polynomials#
Examples (by instructor)#
In this lab, you’ll learn to work with polynomials in NumPy: creating and evaluating polynomial objects, fitting data with least-squares polynomial regression, and assessing fit quality with residuals, MAE, MSE, and \(R^2\).
Example 1: Polynomial Basics — np.poly1d, np.polyval#
A degree-\(n\) polynomial is:
NumPy stores coefficients in descending order (highest power first).
Function |
Purpose |
|---|---|
|
Create a callable polynomial object |
|
Evaluate polynomial at \(x\) |
|
Least-squares fit — returns coefficients |
Define \(p(x) = 2x^3 - 3x^2 + x - 5\) and:
Print its
poly1drepresentationEvaluate it at \(x = 2\) using both
poly1dandpolyvalPrint its derivative and its roots
Example 2: Root Finding — Chemical Equilibrium#
In chemical engineering, we often need to find where a polynomial equals zero. A classic example is solving an equilibrium expression that reduces to a polynomial equation.
Problem: The equilibrium conversion \(x\) for the gas-phase reaction \(A \rightleftharpoons 2B\) at constant \(T\) and \(P\) satisfies:
Rearranging for \(K_p = 2.0\):
This is a degree-2 polynomial in \(x\). We will:
Express it as
np.poly1dand find its roots viap.rootsKeep only the physically meaningful root (\(0 < x < 1\))
Example 3: Fitting \(C_p(T)\) for CO\(_2\) with np.polyfit#
Below is NIST tabulated heat capacity data for CO\(_2\). Use np.polyfit to fit degree 2 polynomial, then:
Plot fit against the data
Compute and print MAE, MSE, and \(R^2\) for each degree
\(T\) (K) |
300 |
400 |
500 |
600 |
700 |
800 |
900 |
1000 |
1100 |
1200 |
|---|---|---|---|---|---|---|---|---|---|---|
\(C_p\) (J/mol/K) |
37.13 |
41.33 |
44.60 |
47.33 |
49.65 |
51.61 |
53.26 |
54.31 |
55.37 |
56.21 |
import numpy as np
import matplotlib.pyplot as plt
# NIST tabulated Cp data for CO2
T_data = np.array([300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200], dtype=float)
Cp_data = np.array([37.13, 41.33, 44.60, 47.33, 49.65,
51.61, 53.26, 54.31, 55.37, 56.21])
Warm-Up: Syntax Practice#
Short exercises to get comfortable with the key NumPy polynomial functions before the main problems.
Exercise 1 — Define a polynomial.
Define \(q(x) = x^2 - 5x + 6\) using np.poly1d and print it. Then confirm that the coefficients stored inside match what you passed in.
import numpy as np
# Define q(x) = x^2 - 5x + 6
q = np.poly1d([___, ___, ___]) # fill in the three coefficients
print(q)
print("Coefficients:", q.coeffs) # should print [1, -5, 6]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[2], line 6
3 # Define q(x) = x^2 - 5x + 6
4 q = np.poly1d([___, ___, ___]) # fill in the three coefficients
----> 6 print(q)
7 print("Coefficients:", q.coeffs) # should print [1, -5, 6]
File ~/miniconda3/envs/chemcomp/lib/python3.10/site-packages/numpy/lib/polynomial.py:1284, in poly1d.__str__(self)
1282 for k, coeff in enumerate(coeffs):
1283 if not iscomplex(coeff):
-> 1284 coefstr = fmt_float(real(coeff))
1285 elif real(coeff) == 0:
1286 coefstr = '%sj' % fmt_float(imag(coeff))
File ~/miniconda3/envs/chemcomp/lib/python3.10/site-packages/numpy/lib/polynomial.py:1277, in poly1d.__str__.<locals>.fmt_float(q)
1276 def fmt_float(q):
-> 1277 s = '%.4g' % q
1278 if s.endswith('.0000'):
1279 s = s[:-5]
TypeError: must be real number, not numpy.str_
Exercise 2 — Evaluate at a point.
Using the same \(q(x) = x^2 - 5x + 6\), evaluate \(q(2)\) and \(q(3)\) two ways: via the poly1d object and via np.polyval. Confirm both give the same answer, and check manually that the values make sense (hint: \(q(2) = 0\) and \(q(3) = 0\)).
import numpy as np
coeffs = [1, -5, 6]
q = np.poly1d(coeffs)
for x_val in [2, 3]:
via_poly1d = ___ # call q like a function
via_polyval = ___ # use np.polyval
print(f"q({x_val}): poly1d = {via_poly1d}, polyval = {via_polyval}")
Exercise 3 — Find roots.
Find the roots of \(q(x) = x^2 - 5x + 6\) using p.roots. You should get two real roots. Print them and verify they match what you’d get by factoring: \(q(x) = (x-2)(x-3)\).
import numpy as np
q = np.poly1d([1, -5, 6])
roots = ___ # get the roots of q
print("Roots:", roots)
# Verify by evaluating q at each root — should both be ~0
for r in roots:
print(f"q({r:.1f}) = {q(r):.6f}")
Exercise 4 — Fit a polynomial to data.
Five measurements of reaction rate \(r\) at different temperatures \(T\) are given below. Fit a degree-2 polynomial using np.polyfit, then evaluate it at \(T = 350\) K.
\(T\) (K) |
300 |
325 |
350 |
375 |
400 |
|---|---|---|---|---|---|
\(r\) (mol/L/s) |
1.2 |
2.3 |
3.8 |
5.7 |
8.1 |
import numpy as np
T_data = np.array([300, 325, 350, 375, 400], dtype=float)
r_data = np.array([1.2, 2.3, 3.8, 5.7, 8.1])
# Fit a degree-2 polynomial
coeffs = np.polyfit(___, ___, ___) # x, y, degree
p = np.poly1d(coeffs)
print("Fitted polynomial:")
print(p)
# Evaluate at T = 350 K
T_query = 350.0
print(f"\nr({T_query} K) = {p(___):.4f} mol/L/s") # call p at T_query
Practice Problems (by students)#
Problem 1: Polynomial Evaluation#
Consider the polynomial \(p(x) = 4x^3 - 2x^2 + 7x - 1\).
Create it as a
np.poly1dobject and print its representation.Evaluate \(p(3)\) using both
poly1dandnp.polyval. Verify manually.Find all roots of \(p(x)\). Which roots are real? Which are complex?
Plot \(p(x)\) over \(x \in [-2, 4]\) and mark the real root(s) with a red dot.
Hint: p.roots returns all roots (may be complex); filter with np.isreal() to find real ones.
Problem 2: Fitting Viscosity Data — Degrees of Freedom & Overfitting#
The dynamic viscosity of liquid water varies with temperature (11 data points):
\(T\) (°C) |
0 |
10 |
20 |
30 |
40 |
50 |
60 |
70 |
80 |
90 |
100 |
|---|---|---|---|---|---|---|---|---|---|---|---|
\(\mu\) (mPa·s) |
1.793 |
1.307 |
1.002 |
0.798 |
0.653 |
0.547 |
0.467 |
0.404 |
0.355 |
0.315 |
0.282 |
(a) Fill in the degrees of freedom table below. Recall: \(\text{DOF} = N - (n+1)\) where \(N\) = number of data points and \(n\) = polynomial degree.
Degree \(n\) |
Parameters \(n+1\) |
DOF = \(11 - (n+1)\) |
Overdetermined? |
|---|---|---|---|
1 |
|||
3 |
|||
5 |
|||
10 |
(b) Fit polynomials of degree 1, 3, 5, and 10, plot all fits over \(T \in [-10, 120]\) °C, and print MAE and \(R^2\) for each. Then answer: which degrees overfit, and how can you tell?