12.5: Curve Fitting
- Page ID
- 135908
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\dsum}{\displaystyle\sum\limits} \)
\( \newcommand{\dint}{\displaystyle\int\limits} \)
\( \newcommand{\dlim}{\displaystyle\lim\limits} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\(\newcommand{\longvect}{\overrightarrow}\)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)In the previous section, we discussed interpolation, where the goal is to estimate values between known data points and the curve usually passes through the given points. Curve fitting is a little different.
In curve fitting, we use data points to find a mathematical model that describes the overall trend of the data. The fitted curve does not necessarily pass through every data point. Instead, it tries to represent the general relationship between the independent variable and the dependent variable.
This is especially useful when working with experimental data. Real measurements often include noise, small errors, or natural variation. If we force a curve to pass through every data point, we may accidentally model the noise instead of the real trend. Curve fitting helps us find a smoother model that describes the behavior of the data.
For example, suppose we measure temperature during the day. The measured data may not follow a perfectly smooth curve because of sensor noise, small environmental changes, or measurement error. A fitted curve can help us describe the overall temperature trend without treating every small fluctuation as important.
|
Method |
Main Question |
Does the curve pass through every data point? |
|
Interpolation |
What value should we estimate between known points? |
Usually yes |
|
Curve fitting |
What overall relationship best describes the data? |
Not necessarily |
In experiments, measurements often contain noise or uncertainty. A best-fit curve helps us model the trend without being forced to follow every small measurement variation.
Least Squares Idea
Many curve fitting methods use least squares regression. The fitted curve is chosen to minimize the sum of the squared residual errors. A residual error is the difference between a measured value and the value predicted by the model.
Fitting a Polynomial with polyfit
MATLAB uses the polyfit function to find coefficients for a polynomial model that fits data. The general form is:
coefficients = polyfit(xData, yData, degree);
- xData: contains the known x values
- yData: contains the known y values
- degree: is the degree of the polynomial
- coefficents: is a vector containing the polynomial coefficients
The degree of the polynomial controls the shape of the fitted curve. For example:
| Polynomial degree | Type of fit |
|---|---|
| 1 | Linear fit |
| 2 | Quadratic fit |
| 3 | Cubic fit |
A first-degree polynomial creates a straight line. A second-degree polynomial creates a parabola. Higher-degree polynomials can create curves with more bends.
4.2 Evaluating a Polynomial with polyval
After we find the coefficients of the polynomial, we use polyval to evaluate the polynomial at selected x-values.
yFit = polyval(coefficients, xValues);
Example: Room Temperature Curve Fit
Suppose we recorded the temperature in a room every hour from 2 PM to 6 PM. We want to fit a second-order polynomial to the data.
Fitting a polynomial of a second order to a data set.
clc; clear; clf;
% x is the time and y is the temperature in F
x = 2:6;
y = [65 67 72 71 63];
% Fit a quadratic polynomial
coefs = polyfit(x, y, 2);
% Evaluate the polynomial at the original x values
curve = polyval(coefs, x);
% Plot both the data and fitted curve
plot(x, y, 'ro', x, curve)
xlabel('Time')
ylabel('Temperature (F)')
title('Temperature vs. Time')
axis([1 7 60 75])
grid on
Solution

The example 1 works, but the curve looks jagged because we only evaluate it at a few x-values. To draw a smoother curve, we can create many x-values using linspace.
Fitting a polynomial of a second order to a data set.
clc; clear; clf;
x = 2:6;
y = [65 67 72 71 63];
% Create more x-values for a smoother plot
moreX = linspace(min(x), max(x), 100);
% Fit a quadratic model
coefs = polyfit(x, y, 2);
% Evaluate the model at many x-values
curve = polyval(coefs, moreX);
plot(x, y, 'ro', moreX, curve)
xlabel('Time')
ylabel('Temperature (F)')
title('Temperature vs. Time')
axis([1 7 60 75])
grid on
Solution

polyfit finds the model. polyval evaluates the model. If you want a smooth-looking plotted curve, evaluate the model at many points, not just the original data points.
Choosing the Polynomial Degree
Choosing the degree of the polynomial is important. A polynomial degree that is too low may not capture the trend in the data. A polynomial degree that is too high may bend too much and follow the random noise in the data instead of the overall pattern.
This problem is called overfitting. Overfitting happens when a model matches the given data very closely but does not describe the real relationship well.
As a beginner rule:
- Use a first-degree polynomial if the trend looks approximately linear.
- Use a second-degree polynomial if the trend looks curved like a parabola.
- Be careful with high-degree polynomials unless you have a good reason to use them.
Comparing different polynomial degrees.
clc; clear; clf;
time = 2:6;
temp = [65 67 72 71 63];
moreX = linspace(min(time), max(time), 100);
for degree = 1:3
coefs = polyfit(time, temp, degree);
curve = polyval(coefs, moreX);
subplot(1, 3, degree)
plot(time, temp, 'ro', moreX, curve)
xlabel('Time')
ylabel('Temperature (F)')
title(sprintf('Degree %d', degree))
axis([1 7 60 75])
grid on
end
Solution

Common Mistake
Do not choose the highest degree just because it looks like it fits the points better. A useful model should make sense for the situation and should not behave wildly between data points.

